From 4ab7d22c816f089c3853e4ba44f917bafe9086a1 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lisandro=20Dami=C3=A1n=20Nicanor=20P=C3=A9rez=20Meyer?= Date: Fri, 7 Dec 2018 20:24:14 +0000 Subject: [PATCH 1/1] Import qtlocation-opensource-src_5.11.3+dfsg.orig.tar.xz [dgit import orig qtlocation-opensource-src_5.11.3+dfsg.orig.tar.xz] --- .QT-ENTERPRISE-LICENSE-AGREEMENT | 1089 ++++ ...-APPLICATION-DEVELOPMENT-LICENSE-AGREEMENT | 1089 ++++ .QT-FOR-AUTOMATION-LICENSE-AGREEMENT | 1089 ++++ .QT-FOR-AUTOMOTIVE-LICENSE-AGREEMENT | 894 +++ .QT-FOR-DEVICE-CREATION-LICENSE-AGREEMENT | 1089 ++++ .gitmodules | 4 + .qmake.conf | 4 + .tag | 1 + LICENSE.FDL | 450 ++ LICENSE.GPL2 | 339 ++ LICENSE.GPL3 | 674 +++ LICENSE.GPL3-EXCEPT | 704 +++ LICENSE.GPLv3 | 686 +++ LICENSE.LGPL3 | 165 + LICENSE.LGPLv3 | 175 + config.tests/gypsy/gypsy.pro | 2 + config.tests/gypsy/main.cpp | 44 + config.tests/winrt/main.cpp | 47 + config.tests/winrt/winrt.pro | 5 + configure.json | 6 + dist/changes-5.10.0 | 62 + dist/changes-5.10.1 | 26 + dist/changes-5.11.0 | 116 + dist/changes-5.11.1 | 40 + dist/changes-5.11.2 | 38 + dist/changes-5.11.3 | 32 + dist/changes-5.2.1 | 53 + dist/changes-5.3.0 | 74 + dist/changes-5.3.1 | 57 + dist/changes-5.3.2 | 50 + dist/changes-5.4.0 | 54 + dist/changes-5.4.1 | 29 + dist/changes-5.4.2 | 28 + dist/changes-5.5.0 | 40 + dist/changes-5.5.1 | 54 + dist/changes-5.6.0 | 69 + dist/changes-5.6.1 | 38 + dist/changes-5.6.2 | 55 + dist/changes-5.6.3 | 38 + dist/changes-5.7.0 | 53 + dist/changes-5.7.1 | 45 + dist/changes-5.8.0 | 70 + dist/changes-5.9.0 | 131 + dist/changes-5.9.1 | 43 + dist/changes-5.9.2 | 56 + dist/changes-5.9.3 | 41 + dist/changes-5.9.4 | 31 + examples/examples.pro | 7 + examples/location/location.pro | 10 + .../mapviewer/doc/images/mapviewer.png | Bin 0 -> 176944 bytes .../location/mapviewer/doc/src/mapviewer.qdoc | 171 + examples/location/mapviewer/forms/Geocode.qml | 91 + .../mapviewer/forms/GeocodeForm.ui.qml | 183 + examples/location/mapviewer/forms/Locale.qml | 92 + .../mapviewer/forms/LocaleForm.ui.qml | 163 + examples/location/mapviewer/forms/Message.qml | 68 + .../mapviewer/forms/MessageForm.ui.qml | 116 + .../mapviewer/forms/ReverseGeocode.qml | 85 + .../mapviewer/forms/ReverseGeocodeForm.ui.qml | 150 + .../location/mapviewer/forms/RouteAddress.qml | 152 + .../mapviewer/forms/RouteAddressForm.ui.qml | 207 + .../mapviewer/forms/RouteCoordinate.qml | 89 + .../forms/RouteCoordinateForm.ui.qml | 183 + .../location/mapviewer/forms/RouteList.qml | 97 + .../mapviewer/forms/RouteListDelegate.qml | 92 + .../mapviewer/forms/RouteListHeader.qml | 94 + examples/location/mapviewer/helper.js | 91 + examples/location/mapviewer/main.cpp | 145 + .../location/mapviewer/map/CircleItem.qml | 68 + examples/location/mapviewer/map/ImageItem.qml | 71 + .../location/mapviewer/map/MapComponent.qml | 641 +++ .../location/mapviewer/map/MapSliders.qml | 333 ++ examples/location/mapviewer/map/Marker.qml | 126 + examples/location/mapviewer/map/MiniMap.qml | 127 + .../location/mapviewer/map/PolygonItem.qml | 73 + .../location/mapviewer/map/PolylineItem.qml | 67 + .../location/mapviewer/map/RectangleItem.qml | 69 + examples/location/mapviewer/mapviewer.pro | 47 + examples/location/mapviewer/mapviewer.qml | 512 ++ examples/location/mapviewer/mapviewer.qrc | 38 + .../mapviewer/menus/ItemPopupMenu.qml | 63 + .../location/mapviewer/menus/MainMenu.qml | 142 + .../location/mapviewer/menus/MapPopupMenu.qml | 74 + .../mapviewer/menus/MarkerPopupMenu.qml | 86 + .../location/mapviewer/resources/icon.png | Bin 0 -> 1859 bytes .../location/mapviewer/resources/marker.png | Bin 0 -> 752 bytes .../location/mapviewer/resources/scale.png | Bin 0 -> 98 bytes .../mapviewer/resources/scale_end.png | Bin 0 -> 93 bytes .../minimal_map/doc/images/minimal_map.png | Bin 0 -> 229975 bytes .../minimal_map/doc/src/minimal_map.qdoc | 89 + examples/location/minimal_map/main.cpp | 63 + examples/location/minimal_map/main.qml | 77 + examples/location/minimal_map/minimal_map.pro | 10 + examples/location/minimal_map/qml.qrc | 6 + .../location/places/doc/images/places.png | Bin 0 -> 233430 bytes examples/location/places/doc/src/places.qdoc | 166 + examples/location/places/forms/Message.qml | 68 + .../location/places/forms/MessageForm.ui.qml | 116 + .../location/places/forms/PlaceDetails.qml | 137 + .../places/forms/PlaceDetailsForm.ui.qml | 295 + .../places/forms/SearchBoundingBox.qml | 81 + .../places/forms/SearchBoundingBoxForm.ui.qml | 173 + .../places/forms/SearchBoundingCircle.qml | 79 + .../forms/SearchBoundingCircleForm.ui.qml | 161 + .../location/places/forms/SearchCenter.qml | 80 + .../places/forms/SearchCenterForm.ui.qml | 150 + .../location/places/forms/SearchOptions.qml | 88 + .../places/forms/SearchOptionsForm.ui.qml | 169 + examples/location/places/helper.js | 63 + examples/location/places/items/MainMenu.qml | 101 + .../location/places/items/MapComponent.qml | 224 + examples/location/places/items/SearchBar.qml | 132 + examples/location/places/main.cpp | 136 + examples/location/places/places.pro | 44 + examples/location/places/places.qml | 507 ++ examples/location/places/places.qrc | 42 + .../location/places/resources/categories.png | Bin 0 -> 130 bytes examples/location/places/resources/left.png | Bin 0 -> 141 bytes examples/location/places/resources/marker.png | Bin 0 -> 752 bytes examples/location/places/resources/right.png | Bin 0 -> 147 bytes examples/location/places/resources/scale.png | Bin 0 -> 98 bytes .../location/places/resources/scale_end.png | Bin 0 -> 93 bytes examples/location/places/resources/search.png | Bin 0 -> 259 bytes examples/location/places/resources/star.png | Bin 0 -> 1403 bytes .../places/views/CategoryDelegate.qml | 114 + .../location/places/views/CategoryView.qml | 77 + .../places/views/EditorialDelegate.qml | 96 + .../location/places/views/EditorialPage.qml | 124 + .../location/places/views/EditorialView.qml | 67 + examples/location/places/views/ImageView.qml | 161 + examples/location/places/views/RatingView.qml | 65 + .../location/places/views/ReviewDelegate.qml | 108 + examples/location/places/views/ReviewPage.qml | 133 + examples/location/places/views/ReviewView.qml | 67 + .../places/views/SearchResultDelegate.qml | 206 + .../places/views/SearchResultView.qml | 101 + .../location/places/views/SuggestionView.qml | 77 + examples/location/places_list/Marker.qml | 69 + .../places_list/doc/images/places_list.png | Bin 0 -> 18195 bytes .../places_list/doc/src/places_list.qdoc | 76 + examples/location/places_list/main.cpp | 61 + examples/location/places_list/marker.png | Bin 0 -> 752 bytes examples/location/places_list/places_list.pro | 12 + examples/location/places_list/places_list.qml | 111 + examples/location/places_list/places_list.qrc | 7 + .../places_map/doc/images/places_map.png | Bin 0 -> 167995 bytes .../places_map/doc/src/places_map.qdoc | 83 + examples/location/places_map/main.cpp | 63 + examples/location/places_map/marker.png | Bin 0 -> 752 bytes examples/location/places_map/places_map.pro | 12 + examples/location/places_map/places_map.qml | 138 + examples/location/places_map/places_map.qrc | 6 + examples/location/planespotter/Plane.qml | 125 + examples/location/planespotter/airplane.png | Bin 0 -> 831 bytes .../planespotter/doc/images/planespotter.png | Bin 0 -> 135872 bytes .../planespotter/doc/src/planespotter.qdoc | 143 + examples/location/planespotter/main.cpp | 211 + .../location/planespotter/planespotter.pro | 10 + .../location/planespotter/planespotter.qml | 228 + examples/location/planespotter/qml.qrc | 7 + .../geoflickr/doc/images/qml-flickr-1.jpg | Bin 0 -> 66794 bytes .../geoflickr/doc/src/geoflickr.qdoc | 87 + examples/positioning/geoflickr/flickr-90.qml | 61 + examples/positioning/geoflickr/flickr.qml | 147 + examples/positioning/geoflickr/flickr.qrc | 30 + .../geoflickr/flickrcommon/Progress.qml | 82 + .../geoflickr/flickrcommon/RestModel.qml | 75 + .../geoflickr/flickrcommon/ScrollBar.qml | 90 + .../geoflickr/flickrcommon/Slider.qml | 86 + .../geoflickr/flickrmobile/Button.qml | 88 + .../geoflickr/flickrmobile/GeoTab.qml | 181 + .../geoflickr/flickrmobile/GridDelegate.qml | 122 + .../geoflickr/flickrmobile/ImageDetails.qml | 166 + .../geoflickr/flickrmobile/ListDelegate.qml | 76 + .../geoflickr/flickrmobile/TitleBar.qml | 87 + .../geoflickr/flickrmobile/ToolBar.qml | 74 + .../geoflickr/flickrmobile/images/gloss.png | Bin 0 -> 889 bytes .../flickrmobile/images/lineedit.png | Bin 0 -> 1307 bytes .../flickrmobile/images/lineedit.sci | 5 + .../geoflickr/flickrmobile/images/moon.png | Bin 0 -> 2366 bytes .../geoflickr/flickrmobile/images/quit.png | Bin 0 -> 1785 bytes .../geoflickr/flickrmobile/images/star.png | Bin 0 -> 259 bytes .../geoflickr/flickrmobile/images/stripes.png | Bin 0 -> 159 bytes .../geoflickr/flickrmobile/images/sun.png | Bin 0 -> 8110 bytes .../flickrmobile/images/titlebar.png | Bin 0 -> 1327 bytes .../flickrmobile/images/titlebar.sci | 5 + .../flickrmobile/images/toolbutton.png | Bin 0 -> 2550 bytes .../flickrmobile/images/toolbutton.sci | 5 + .../geoflickr/flickrmobile/nmealog.txt | 1403 +++++ examples/positioning/geoflickr/geoflickr.pro | 15 + .../geoflickr/geoflickr.qmlproject | 16 + .../geoflickr/qmllocationflickr.cpp | 68 + .../clientapplication.cpp | 72 + .../logfilepositionsource/clientapplication.h | 74 + .../doc/src/logfilepositionsource.qdoc | 87 + .../logfilepositionsource/logfile.qrc | 5 + .../logfilepositionsource.cpp | 132 + .../logfilepositionsource.h | 87 + .../logfilepositionsource.pro | 16 + .../logfilepositionsource/main.cpp | 62 + .../logfilepositionsource/simplelog.txt | 156 + examples/positioning/positioning.pro | 7 + .../doc/images/example-satelliteinfo.png | Bin 0 -> 27371 bytes .../satelliteinfo/doc/src/satelliteinfo.qdoc | 75 + examples/positioning/satelliteinfo/main.cpp | 72 + .../satelliteinfo/satelliteinfo.pro | 22 + .../satelliteinfo/satelliteinfo.qml | 314 ++ .../satelliteinfo/satelliteinfo.qrc | 5 + .../satelliteinfo/satellitemodel.cpp | 319 ++ .../satelliteinfo/satellitemodel.h | 134 + examples/positioning/weatherinfo/appmodel.cpp | 572 ++ examples/positioning/weatherinfo/appmodel.h | 182 + .../components/BigForecastIcon.qml | 93 + .../weatherinfo/components/ForecastIcon.qml | 99 + .../weatherinfo/components/WeatherIcon.qml | 112 + .../doc/images/example-weatherinfo.png | Bin 0 -> 82081 bytes .../weatherinfo/doc/src/weatherinfo.qdoc | 113 + .../positioning/weatherinfo/icons/README.txt | 5 + .../weatherinfo/icons/weather-few-clouds.png | Bin 0 -> 79488 bytes .../weatherinfo/icons/weather-fog.png | Bin 0 -> 43896 bytes .../weatherinfo/icons/weather-haze.png | Bin 0 -> 130030 bytes .../weatherinfo/icons/weather-icy.png | Bin 0 -> 49362 bytes .../weatherinfo/icons/weather-overcast.png | Bin 0 -> 60290 bytes .../weatherinfo/icons/weather-showers.png | Bin 0 -> 76340 bytes .../weatherinfo/icons/weather-sleet.png | Bin 0 -> 87168 bytes .../weatherinfo/icons/weather-snow.png | Bin 0 -> 70939 bytes .../weatherinfo/icons/weather-storm.png | Bin 0 -> 93207 bytes .../icons/weather-sunny-very-few-clouds.png | Bin 0 -> 65731 bytes .../weatherinfo/icons/weather-sunny.png | Bin 0 -> 59084 bytes .../icons/weather-thundershower.png | Bin 0 -> 105643 bytes examples/positioning/weatherinfo/main.cpp | 82 + .../positioning/weatherinfo/weatherinfo.pro | 22 + .../positioning/weatherinfo/weatherinfo.qml | 240 + .../positioning/weatherinfo/weatherinfo.qrc | 20 + .../QtLocation/private/error_messages_p.h | 1 + .../private/locationvaluetypehelper_p.h | 1 + .../private/mapitemviewdelegateincubator_p.h | 1 + .../private/qabstractgeotilecache_p.h | 1 + .../5.11.3/QtLocation/private/qcache3q_p.h | 1 + .../private/qdeclarativecategory_p.h | 1 + .../private/qdeclarativecirclemapitem_p.h | 1 + .../private/qdeclarativecontactdetail_p.h | 1 + .../private/qdeclarativegeocodemodel_p.h | 1 + .../private/qdeclarativegeomaneuver_p.h | 1 + .../QtLocation/private/qdeclarativegeomap_p.h | 1 + .../qdeclarativegeomapcopyrightsnotice_p.h | 1 + .../private/qdeclarativegeomapitembase_p.h | 1 + .../private/qdeclarativegeomapitemgroup_p.h | 1 + .../private/qdeclarativegeomapitemview_p.h | 1 + .../private/qdeclarativegeomapparameter_p.h | 1 + .../private/qdeclarativegeomapquickitem_p.h | 1 + .../private/qdeclarativegeomaptype_p.h | 1 + .../private/qdeclarativegeoroute_p.h | 1 + .../private/qdeclarativegeoroutemodel_p.h | 1 + .../private/qdeclarativegeoroutesegment_p.h | 1 + .../qdeclarativegeoserviceprovider_p.h | 1 + .../private/qdeclarativenavigator_p.h | 1 + .../private/qdeclarativenavigator_p_p.h | 1 + .../QtLocation/private/qdeclarativeperiod_p.h | 1 + .../QtLocation/private/qdeclarativeplace_p.h | 1 + .../private/qdeclarativeplaceattribute_p.h | 1 + .../private/qdeclarativeplacecontentmodel_p.h | 1 + .../qdeclarativeplaceeditorialmodel_p.h | 1 + .../private/qdeclarativeplaceicon_p.h | 1 + .../private/qdeclarativeplaceimagemodel_p.h | 1 + .../private/qdeclarativeplaceuser_p.h | 1 + .../private/qdeclarativepolygonmapitem_p.h | 1 + .../private/qdeclarativepolylinemapitem_p.h | 1 + .../private/qdeclarativeratings_p.h | 1 + .../private/qdeclarativerectanglemapitem_p.h | 1 + .../private/qdeclarativereviewmodel_p.h | 1 + .../private/qdeclarativeroutemapitem_p.h | 1 + .../private/qdeclarativesearchmodelbase_p.h | 1 + .../private/qdeclarativesearchresultmodel_p.h | 1 + .../qdeclarativesearchsuggestionmodel_p.h | 1 + .../private/qdeclarativesupplier_p.h | 1 + .../qdeclarativesupportedcategoriesmodel_p.h | 1 + .../private/qgeocameracapabilities_p.h | 1 + .../QtLocation/private/qgeocameradata_p.h | 1 + .../QtLocation/private/qgeocameratiles_p.h | 1 + .../QtLocation/private/qgeocodereply_p.h | 1 + .../QtLocation/private/qgeocodingmanager_p.h | 1 + .../private/qgeocodingmanagerengine_p.h | 1 + .../QtLocation/private/qgeofiletilecache_p.h | 1 + .../QtLocation/private/qgeomaneuver_p.h | 1 + .../5.11.3/QtLocation/private/qgeomap_p.h | 1 + .../5.11.3/QtLocation/private/qgeomap_p_p.h | 1 + .../private/qgeomapitemgeometry_p.h | 1 + .../QtLocation/private/qgeomapobject_p.h | 1 + .../QtLocation/private/qgeomapobject_p_p.h | 1 + .../private/qgeomapobjectqsgsupport_p.h | 1 + .../QtLocation/private/qgeomapparameter_p.h | 1 + .../QtLocation/private/qgeomappingmanager_p.h | 1 + .../private/qgeomappingmanager_p_p.h | 1 + .../private/qgeomappingmanagerengine_p.h | 1 + .../private/qgeomappingmanagerengine_p_p.h | 1 + .../5.11.3/QtLocation/private/qgeomaptype_p.h | 1 + .../QtLocation/private/qgeomaptype_p_p.h | 1 + .../QtLocation/private/qgeoprojection_p.h | 1 + .../5.11.3/QtLocation/private/qgeoroute_p.h | 1 + .../QtLocation/private/qgeorouteparser_p.h | 1 + .../QtLocation/private/qgeorouteparser_p_p.h | 1 + .../private/qgeorouteparserosrmv4_p.h | 1 + .../private/qgeorouteparserosrmv5_p.h | 1 + .../QtLocation/private/qgeoroutereply_p.h | 1 + .../QtLocation/private/qgeorouterequest_p.h | 1 + .../QtLocation/private/qgeoroutesegment_p.h | 1 + .../QtLocation/private/qgeoroutingmanager_p.h | 1 + .../private/qgeoroutingmanagerengine_p.h | 1 + .../private/qgeoserviceprovider_p.h | 1 + .../QtLocation/private/qgeotiledmap_p.h | 1 + .../QtLocation/private/qgeotiledmap_p_p.h | 1 + .../QtLocation/private/qgeotiledmaplabs_p.h | 1 + .../private/qgeotiledmappingmanagerengine_p.h | 1 + .../qgeotiledmappingmanagerengine_p_p.h | 1 + .../QtLocation/private/qgeotiledmapreply_p.h | 1 + .../private/qgeotiledmapreply_p_p.h | 1 + .../QtLocation/private/qgeotiledmapscene_p.h | 1 + .../QtLocation/private/qgeotilefetcher_p.h | 1 + .../QtLocation/private/qgeotilefetcher_p_p.h | 1 + .../private/qgeotilerequestmanager_p.h | 1 + .../QtLocation/private/qgeotilespec_p.h | 1 + .../QtLocation/private/qgeotilespec_p_p.h | 1 + .../QtLocation/private/qlocationglobal_p.h | 1 + .../QtLocation/private/qmapcircleobject_p.h | 1 + .../QtLocation/private/qmapcircleobject_p_p.h | 1 + .../private/qmapcircleobjectqsg_p_p.h | 1 + .../QtLocation/private/qmapiconobject_p.h | 1 + .../QtLocation/private/qmapiconobject_p_p.h | 1 + .../private/qmapiconobjectqsg_p_p.h | 1 + .../QtLocation/private/qmapobjectview_p.h | 1 + .../QtLocation/private/qmapobjectview_p_p.h | 1 + .../QtLocation/private/qmappolygonobject_p.h | 1 + .../private/qmappolygonobject_p_p.h | 1 + .../private/qmappolygonobjectqsg_p_p.h | 1 + .../QtLocation/private/qmappolylineobject_p.h | 1 + .../private/qmappolylineobject_p_p.h | 1 + .../private/qmappolylineobjectqsg_p_p.h | 1 + .../QtLocation/private/qmaprouteobject_p.h | 1 + .../QtLocation/private/qmaprouteobject_p_p.h | 1 + .../private/qmaprouteobjectqsg_p_p.h | 1 + .../QtLocation/private/qnavigationmanager_p.h | 1 + .../private/qnavigationmanagerengine_p.h | 1 + .../private/qparameterizableobject_p.h | 1 + .../5.11.3/QtLocation/private/qplace_p.h | 1 + .../QtLocation/private/qplaceattribute_p.h | 1 + .../QtLocation/private/qplacecategory_p.h | 1 + .../private/qplacecontactdetail_p.h | 1 + .../QtLocation/private/qplacecontent_p.h | 1 + .../private/qplacecontentrequest_p.h | 1 + .../QtLocation/private/qplaceeditorial_p.h | 1 + .../5.11.3/QtLocation/private/qplaceicon_p.h | 1 + .../5.11.3/QtLocation/private/qplaceimage_p.h | 1 + .../private/qplacemanagerengine_p.h | 1 + .../private/qplaceproposedsearchresult_p.h | 1 + .../QtLocation/private/qplaceratings_p.h | 1 + .../5.11.3/QtLocation/private/qplacereply_p.h | 1 + .../QtLocation/private/qplaceresult_p.h | 1 + .../QtLocation/private/qplacereview_p.h | 1 + .../QtLocation/private/qplacesearchresult_p.h | 1 + .../QtLocation/private/qplacesupplier_p.h | 1 + .../5.11.3/QtLocation/private/qplaceuser_p.h | 1 + .../QtLocation/private/qqsgmapobject_p.h | 1 + .../private/qquickgeomapgesturearea_p.h | 1 + .../QtLocation/private/unsupportedreplies_p.h | 1 + include/QtLocation/QGeoCodeReply | 1 + include/QtLocation/QGeoCodingManager | 1 + include/QtLocation/QGeoCodingManagerEngine | 1 + include/QtLocation/QGeoManeuver | 1 + include/QtLocation/QGeoRoute | 1 + include/QtLocation/QGeoRouteReply | 1 + include/QtLocation/QGeoRouteRequest | 1 + include/QtLocation/QGeoRouteSegment | 1 + include/QtLocation/QGeoRoutingManager | 1 + include/QtLocation/QGeoRoutingManagerEngine | 1 + include/QtLocation/QGeoServiceProvider | 1 + include/QtLocation/QGeoServiceProviderFactory | 1 + include/QtLocation/QLocation | 1 + include/QtLocation/QPlace | 1 + include/QtLocation/QPlaceAttribute | 1 + include/QtLocation/QPlaceCategory | 1 + include/QtLocation/QPlaceContactDetail | 1 + include/QtLocation/QPlaceContent | 1 + include/QtLocation/QPlaceContentReply | 1 + include/QtLocation/QPlaceContentRequest | 1 + include/QtLocation/QPlaceDetailsReply | 1 + include/QtLocation/QPlaceEditorial | 1 + include/QtLocation/QPlaceIcon | 1 + include/QtLocation/QPlaceIdReply | 1 + include/QtLocation/QPlaceImage | 1 + include/QtLocation/QPlaceManager | 1 + include/QtLocation/QPlaceManagerEngine | 1 + include/QtLocation/QPlaceMatchReply | 1 + include/QtLocation/QPlaceMatchRequest | 1 + include/QtLocation/QPlaceProposedSearchResult | 1 + include/QtLocation/QPlaceRatings | 1 + include/QtLocation/QPlaceReply | 1 + include/QtLocation/QPlaceResult | 1 + include/QtLocation/QPlaceReview | 1 + include/QtLocation/QPlaceSearchReply | 1 + include/QtLocation/QPlaceSearchRequest | 1 + include/QtLocation/QPlaceSearchResult | 1 + .../QtLocation/QPlaceSearchSuggestionReply | 1 + include/QtLocation/QPlaceSupplier | 1 + include/QtLocation/QPlaceUser | 1 + include/QtLocation/QtLocation | 47 + include/QtLocation/QtLocationVersion | 1 + include/QtLocation/headers.pri | 6 + include/QtLocation/placemacro.h | 1 + include/QtLocation/qgeocodereply.h | 1 + include/QtLocation/qgeocodingmanager.h | 1 + include/QtLocation/qgeocodingmanagerengine.h | 1 + include/QtLocation/qgeomaneuver.h | 1 + include/QtLocation/qgeoroute.h | 1 + include/QtLocation/qgeoroutereply.h | 1 + include/QtLocation/qgeorouterequest.h | 1 + include/QtLocation/qgeoroutesegment.h | 1 + include/QtLocation/qgeoroutingmanager.h | 1 + include/QtLocation/qgeoroutingmanagerengine.h | 1 + include/QtLocation/qgeoserviceprovider.h | 1 + .../QtLocation/qgeoserviceproviderfactory.h | 1 + include/QtLocation/qlocation.h | 1 + include/QtLocation/qlocationglobal.h | 1 + include/QtLocation/qplace.h | 1 + include/QtLocation/qplaceattribute.h | 1 + include/QtLocation/qplacecategory.h | 1 + include/QtLocation/qplacecontactdetail.h | 1 + include/QtLocation/qplacecontent.h | 1 + include/QtLocation/qplacecontentreply.h | 1 + include/QtLocation/qplacecontentrequest.h | 1 + include/QtLocation/qplacedetailsreply.h | 1 + include/QtLocation/qplaceeditorial.h | 1 + include/QtLocation/qplaceicon.h | 1 + include/QtLocation/qplaceidreply.h | 1 + include/QtLocation/qplaceimage.h | 1 + include/QtLocation/qplacemanager.h | 1 + include/QtLocation/qplacemanagerengine.h | 1 + include/QtLocation/qplacematchreply.h | 1 + include/QtLocation/qplacematchrequest.h | 1 + .../QtLocation/qplaceproposedsearchresult.h | 1 + include/QtLocation/qplaceratings.h | 1 + include/QtLocation/qplacereply.h | 1 + include/QtLocation/qplaceresult.h | 1 + include/QtLocation/qplacereview.h | 1 + include/QtLocation/qplacesearchreply.h | 1 + include/QtLocation/qplacesearchrequest.h | 1 + include/QtLocation/qplacesearchresult.h | 1 + .../QtLocation/qplacesearchsuggestionreply.h | 1 + include/QtLocation/qplacesupplier.h | 1 + include/QtLocation/qplaceuser.h | 1 + include/QtLocation/qtlocationversion.h | 9 + .../QtPositioning/private/qclipperutils_p.h | 1 + .../private/qdeclarativegeoaddress_p.h | 1 + .../private/qdeclarativegeolocation_p.h | 1 + .../private/qdoublematrix4x4_p.h | 1 + .../QtPositioning/private/qdoublevector2d_p.h | 1 + .../QtPositioning/private/qdoublevector3d_p.h | 1 + .../QtPositioning/private/qgeoaddress_p.h | 1 + .../QtPositioning/private/qgeocircle_p.h | 1 + .../QtPositioning/private/qgeocoordinate_p.h | 1 + .../private/qgeocoordinateobject_p.h | 1 + .../QtPositioning/private/qgeolocation_p.h | 1 + .../5.11.3/QtPositioning/private/qgeopath_p.h | 1 + .../private/qgeopositioninfo_p.h | 1 + .../private/qgeopositioninfosource_p.h | 1 + .../QtPositioning/private/qgeorectangle_p.h | 1 + .../QtPositioning/private/qgeoshape_p.h | 1 + .../private/qlocationdata_simulator_p.h | 1 + .../QtPositioning/private/qlocationutils_p.h | 1 + .../private/qnmeapositioninfosource_p.h | 1 + .../private/qpositioningglobal_p.h | 1 + .../QtPositioning/private/qwebmercator_p.h | 1 + include/QtPositioning/QGeoAddress | 1 + include/QtPositioning/QGeoAreaMonitorInfo | 1 + include/QtPositioning/QGeoAreaMonitorSource | 1 + include/QtPositioning/QGeoCircle | 1 + include/QtPositioning/QGeoCoordinate | 1 + include/QtPositioning/QGeoLocation | 1 + include/QtPositioning/QGeoPath | 1 + include/QtPositioning/QGeoPolygon | 1 + include/QtPositioning/QGeoPolygonPrivate | 1 + include/QtPositioning/QGeoPositionInfo | 1 + include/QtPositioning/QGeoPositionInfoSource | 1 + .../QGeoPositionInfoSourceFactory | 1 + include/QtPositioning/QGeoRectangle | 1 + include/QtPositioning/QGeoSatelliteInfo | 1 + include/QtPositioning/QGeoSatelliteInfoSource | 1 + include/QtPositioning/QGeoShape | 1 + include/QtPositioning/QNmeaPositionInfoSource | 1 + include/QtPositioning/QtPositioning | 22 + include/QtPositioning/QtPositioningVersion | 1 + include/QtPositioning/headers.pri | 6 + include/QtPositioning/qgeoaddress.h | 1 + include/QtPositioning/qgeoareamonitorinfo.h | 1 + include/QtPositioning/qgeoareamonitorsource.h | 1 + include/QtPositioning/qgeocircle.h | 1 + include/QtPositioning/qgeocoordinate.h | 1 + include/QtPositioning/qgeolocation.h | 1 + include/QtPositioning/qgeopath.h | 1 + include/QtPositioning/qgeopolygon.h | 1 + include/QtPositioning/qgeopositioninfo.h | 1 + .../QtPositioning/qgeopositioninfosource.h | 1 + .../qgeopositioninfosourcefactory.h | 1 + include/QtPositioning/qgeorectangle.h | 1 + include/QtPositioning/qgeosatelliteinfo.h | 1 + .../QtPositioning/qgeosatelliteinfosource.h | 1 + include/QtPositioning/qgeoshape.h | 1 + .../QtPositioning/qnmeapositioninfosource.h | 1 + include/QtPositioning/qpositioningglobal.h | 1 + include/QtPositioning/qtpositioningversion.h | 9 + .../private/qdeclarativeposition_p.h | 1 + .../private/qdeclarativepositionsource_p.h | 1 + .../private/qpositioningquickglobal_p.h | 1 + include/QtPositioningQuick/QtPositioningQuick | 6 + .../QtPositioningQuickVersion | 1 + include/QtPositioningQuick/headers.pri | 6 + .../qpositioningquickglobal.h | 1 + .../qtpositioningquickversion.h | 9 + qtlocation.pro | 7 + src/3rdparty/clip2tri/LICENSE | 21 + src/3rdparty/clip2tri/clip2tri.cpp | 406 ++ src/3rdparty/clip2tri/clip2tri.h | 102 + src/3rdparty/clip2tri/clip2tri.pro | 22 + src/3rdparty/clip2tri/qt_attribution.json | 13 + src/3rdparty/clipper/LICENSE | 21 + src/3rdparty/clipper/clipper.cpp | 4622 ++++++++++++++++ src/3rdparty/clipper/clipper.h | 404 ++ src/3rdparty/clipper/clipper.pro | 16 + src/3rdparty/clipper/qt_attribution.json | 13 + src/3rdparty/earcut/LICENSE | 15 + src/3rdparty/earcut/earcut.hpp | 779 +++ src/3rdparty/earcut/qt_attribution.json | 13 + src/3rdparty/poly2tri/AUTHORS | 8 + src/3rdparty/poly2tri/LICENSE | 27 + src/3rdparty/poly2tri/common/shapes.cpp | 363 ++ src/3rdparty/poly2tri/common/shapes.h | 325 ++ src/3rdparty/poly2tri/common/utils.h | 127 + src/3rdparty/poly2tri/poly2tri.h | 39 + src/3rdparty/poly2tri/poly2tri.pro | 27 + src/3rdparty/poly2tri/qt_attribution.json | 13 + .../poly2tri/sweep/advancing_front.cpp | 109 + src/3rdparty/poly2tri/sweep/advancing_front.h | 118 + src/3rdparty/poly2tri/sweep/cdt.cpp | 72 + src/3rdparty/poly2tri/sweep/cdt.h | 105 + src/3rdparty/poly2tri/sweep/sweep.cpp | 814 +++ src/3rdparty/poly2tri/sweep/sweep.h | 285 + src/3rdparty/poly2tri/sweep/sweep_context.cpp | 216 + src/3rdparty/poly2tri/sweep/sweep_context.h | 186 + src/3rdparty/zlib_dependency.pri | 6 + src/imports/imports.pro | 7 + src/imports/location/location.cpp | 216 + src/imports/location/location.pro | 10 + src/imports/location/plugin.json | 2 + src/imports/location/plugins.qmltypes | 1476 +++++ src/imports/location/qmldir | 4 + src/imports/locationlabs/locationlabs.cpp | 95 + src/imports/locationlabs/locationlabs.pro | 16 + src/imports/locationlabs/plugin.json | 2 + src/imports/locationlabs/plugins.qmltypes | 77 + src/imports/locationlabs/qmldir | 4 + src/imports/positioning/locationsingleton.cpp | 317 ++ src/imports/positioning/locationsingleton.h | 89 + src/imports/positioning/plugin.json | 2 + src/imports/positioning/plugins.qmltypes | 259 + src/imports/positioning/positioning.cpp | 633 +++ src/imports/positioning/positioning.pro | 12 + src/imports/positioning/qmldir | 4 + .../qquickgeocoordinateanimation.cpp | 297 + .../qquickgeocoordinateanimation_p.h | 99 + .../qquickgeocoordinateanimation_p_p.h | 69 + src/location/configure.json | 77 + .../declarativemaps/declarativemaps.pri | 63 + .../declarativemaps/error_messages.cpp | 52 + .../declarativemaps/error_messages_p.h | 67 + .../locationvaluetypehelper.cpp | 188 + .../locationvaluetypehelper_p.h | 64 + .../mapitemviewdelegateincubator_p.h | 77 + .../qdeclarativecirclemapitem.cpp | 723 +++ .../qdeclarativecirclemapitem_p.h | 141 + .../qdeclarativegeocodemodel.cpp | 732 +++ .../qdeclarativegeocodemodel_p.h | 207 + .../qdeclarativegeomaneuver.cpp | 229 + .../qdeclarativegeomaneuver_p.h | 118 + .../declarativemaps/qdeclarativegeomap.cpp | 2451 +++++++++ .../declarativemaps/qdeclarativegeomap_p.h | 323 ++ .../qdeclarativegeomapcopyrightsnotice.cpp | 374 ++ .../qdeclarativegeomapcopyrightsnotice_p.h | 123 + .../qdeclarativegeomapitembase.cpp | 374 ++ .../qdeclarativegeomapitembase_p.h | 169 + .../qdeclarativegeomapitemgroup.cpp | 158 + .../qdeclarativegeomapitemgroup_p.h | 74 + .../qdeclarativegeomapitemview.cpp | 386 ++ .../qdeclarativegeomapitemview_p.h | 146 + .../qdeclarativegeomapparameter.cpp | 133 + .../qdeclarativegeomapparameter_p.h | 90 + .../qdeclarativegeomapquickitem.cpp | 458 ++ .../qdeclarativegeomapquickitem_p.h | 137 + .../qdeclarativegeomaptype.cpp | 247 + .../qdeclarativegeomaptype_p.h | 135 + .../declarativemaps/qdeclarativegeoroute.cpp | 324 ++ .../declarativemaps/qdeclarativegeoroute_p.h | 114 + .../qdeclarativegeoroutemodel.cpp | 1913 +++++++ .../qdeclarativegeoroutemodel_p.h | 468 ++ .../qdeclarativegeoroutesegment.cpp | 162 + .../qdeclarativegeoroutesegment_p.h | 86 + .../qdeclarativegeoserviceprovider.cpp | 923 ++++ .../qdeclarativegeoserviceprovider_p.h | 303 ++ .../qdeclarativepolygonmapitem.cpp | 702 +++ .../qdeclarativepolygonmapitem_p.h | 160 + .../qdeclarativepolylinemapitem.cpp | 1107 ++++ .../qdeclarativepolylinemapitem_p.h | 224 + .../qdeclarativerectanglemapitem.cpp | 407 ++ .../qdeclarativerectanglemapitem_p.h | 126 + .../qdeclarativeroutemapitem.cpp | 148 + .../qdeclarativeroutemapitem_p.h | 92 + .../declarativemaps/qgeomapitemgeometry.cpp | 140 + .../declarativemaps/qgeomapitemgeometry_p.h | 157 + .../declarativemaps/qgeomapobject.cpp | 297 + .../declarativemaps/qgeomapobject_p.h | 133 + .../declarativemaps/qgeomapobject_p_p.h | 95 + .../qparameterizableobject.cpp | 88 + .../qparameterizableobject_p.h | 93 + .../qquickgeomapgesturearea.cpp | 1863 +++++++ .../qquickgeomapgesturearea_p.h | 399 ++ .../declarativeplaces/declarativeplaces.pri | 51 + .../qdeclarativecategory.cpp | 458 ++ .../qdeclarativecategory_p.h | 153 + .../qdeclarativecontactdetail.cpp | 223 + .../qdeclarativecontactdetail_p.h | 104 + .../declarativeplaces/qdeclarativeperiod_p.h | 98 + .../declarativeplaces/qdeclarativeplace.cpp | 1229 +++++ .../declarativeplaces/qdeclarativeplace_p.h | 262 + .../qdeclarativeplaceattribute.cpp | 221 + .../qdeclarativeplaceattribute_p.h | 95 + .../qdeclarativeplacecontentmodel.cpp | 397 ++ .../qdeclarativeplacecontentmodel_p.h | 137 + .../qdeclarativeplaceeditorialmodel.cpp | 169 + .../qdeclarativeplaceeditorialmodel_p.h | 76 + .../qdeclarativeplaceicon.cpp | 250 + .../qdeclarativeplaceicon_p.h | 104 + .../qdeclarativeplaceimagemodel.cpp | 170 + .../qdeclarativeplaceimagemodel_p.h | 78 + .../qdeclarativeplaceuser.cpp | 139 + .../qdeclarativeplaceuser_p.h | 92 + .../declarativeplaces/qdeclarativeratings.cpp | 153 + .../declarativeplaces/qdeclarativeratings_p.h | 98 + .../qdeclarativereviewmodel.cpp | 183 + .../qdeclarativereviewmodel_p.h | 78 + .../qdeclarativesearchmodelbase.cpp | 365 ++ .../qdeclarativesearchmodelbase_p.h | 159 + .../qdeclarativesearchresultmodel.cpp | 918 ++++ .../qdeclarativesearchresultmodel_p.h | 179 + .../qdeclarativesearchsuggestionmodel.cpp | 353 ++ .../qdeclarativesearchsuggestionmodel_p.h | 104 + .../qdeclarativesupplier.cpp | 221 + .../qdeclarativesupplier_p.h | 111 + .../qdeclarativesupportedcategoriesmodel.cpp | 689 +++ .../qdeclarativesupportedcategoriesmodel_p.h | 166 + src/location/doc/images/api-mapcircle.png | Bin 0 -> 11336 bytes src/location/doc/images/api-mapitemgroup.png | Bin 0 -> 26089 bytes src/location/doc/images/api-mappolygon.png | Bin 0 -> 42391 bytes src/location/doc/images/api-mappolyline.png | Bin 0 -> 101623 bytes .../doc/images/api-mapquickitem-anchor.png | Bin 0 -> 61695 bytes src/location/doc/images/api-mapquickitem.png | Bin 0 -> 23147 bytes src/location/doc/images/api-maprectangle.png | Bin 0 -> 38249 bytes src/location/doc/images/mapsdemo-finished.png | Bin 0 -> 65434 bytes src/location/doc/images/mapsdemo-routing.png | Bin 0 -> 55176 bytes .../doc/images/mapsdemo-searchgui.png | Bin 0 -> 80027 bytes .../doc/images/mapsdemo-verybasic.png | Bin 0 -> 63947 bytes src/location/doc/qtlocation.qdocconf | 56 + src/location/doc/snippets/cpp/cpp.pro | 8 + src/location/doc/snippets/cpp/cppqml.cpp | 128 + src/location/doc/snippets/cpp/main.cpp | 55 + .../doc/snippets/declarative/content/Cell.qml | 78 + .../declarative/declarative-location.qml | 95 + .../doc/snippets/declarative/declarative.pro | 18 + .../doc/snippets/declarative/maps.qml | 110 + .../doc/snippets/declarative/marker.png | Bin 0 -> 1855 bytes .../doc/snippets/declarative/nmealog.txt | 1403 +++++ .../doc/snippets/declarative/places.qml | 463 ++ .../snippets/declarative/places_loader.qml | 118 + .../doc/snippets/declarative/plugin.qml | 68 + .../doc/snippets/declarative/routing.qml | 121 + src/location/doc/snippets/places/main.cpp | 57 + src/location/doc/snippets/places/places.pro | 5 + .../doc/snippets/places/requesthandler.h | 590 ++ src/location/doc/snippets/snippets.pro | 2 + src/location/doc/src/cpp-qml.qdoc | 115 + .../doc/src/example-parameters.qdocinc | 12 + src/location/doc/src/maps.qdoc | 250 + src/location/doc/src/place-caveats.qdocinc | 21 + src/location/doc/src/place-crossref.qdocinc | 7 + src/location/doc/src/place-definition.qdocinc | 27 + src/location/doc/src/places.qdoc | 433 ++ src/location/doc/src/plugins/esri.qdoc | 234 + .../doc/src/plugins/itemsoverlay.qdoc | 109 + src/location/doc/src/plugins/mapbox.qdoc | 171 + src/location/doc/src/plugins/mapboxgl.qdoc | 268 + src/location/doc/src/plugins/nokia.qdoc | 329 ++ src/location/doc/src/plugins/osm.qdoc | 227 + .../doc/src/plugins/places-backend.qdoc | 151 + src/location/doc/src/qml-maps.qdoc | 229 + src/location/doc/src/qtlocation-changes.qdoc | 77 + src/location/doc/src/qtlocation-cpp.qdoc | 89 + src/location/doc/src/qtlocation-examples.qdoc | 45 + .../doc/src/qtlocation-geoservices.qdoc | 92 + src/location/doc/src/qtlocation-qml.qdoc | 108 + src/location/doc/src/qtlocation.qdoc | 199 + src/location/doc/src/src.pro | 9 + src/location/labs/labs.pri | 5 + src/location/labs/qdeclarativenavigator.cpp | 448 ++ src/location/labs/qdeclarativenavigator_p.h | 153 + src/location/labs/qdeclarativenavigator_p_p.h | 92 + src/location/labs/qgeotiledmaplabs.cpp | 171 + src/location/labs/qgeotiledmaplabs_p.h | 90 + src/location/labs/qmapcircleobject.cpp | 288 + src/location/labs/qmapcircleobject_p.h | 93 + src/location/labs/qmapcircleobject_p_p.h | 117 + src/location/labs/qmapiconobject.cpp | 246 + src/location/labs/qmapiconobject_p.h | 88 + src/location/labs/qmapiconobject_p_p.h | 105 + src/location/labs/qmapobjectview.cpp | 387 ++ src/location/labs/qmapobjectview_p.h | 116 + src/location/labs/qmapobjectview_p_p.h | 86 + src/location/labs/qmappolygonobject.cpp | 256 + src/location/labs/qmappolygonobject_p.h | 90 + src/location/labs/qmappolygonobject_p_p.h | 112 + src/location/labs/qmappolylineobject.cpp | 219 + src/location/labs/qmappolylineobject_p.h | 85 + src/location/labs/qmappolylineobject_p_p.h | 107 + src/location/labs/qmaprouteobject.cpp | 180 + src/location/labs/qmaprouteobject_p.h | 90 + src/location/labs/qmaprouteobject_p_p.h | 79 + .../labs/qsg/qgeomapobjectqsgsupport.cpp | 224 + .../labs/qsg/qgeomapobjectqsgsupport_p.h | 92 + src/location/labs/qsg/qmapcircleobjectqsg.cpp | 224 + .../labs/qsg/qmapcircleobjectqsg_p_p.h | 101 + src/location/labs/qsg/qmapiconobjectqsg.cpp | 225 + src/location/labs/qsg/qmapiconobjectqsg_p_p.h | 97 + .../labs/qsg/qmappolygonobjectqsg.cpp | 220 + .../labs/qsg/qmappolygonobjectqsg_p_p.h | 103 + .../labs/qsg/qmappolylineobjectqsg.cpp | 168 + .../labs/qsg/qmappolylineobjectqsg_p_p.h | 99 + src/location/labs/qsg/qmaprouteobjectqsg.cpp | 103 + .../labs/qsg/qmaprouteobjectqsg_p_p.h | 90 + src/location/labs/qsg/qqsgmapobject.cpp | 68 + src/location/labs/qsg/qqsgmapobject_p.h | 74 + src/location/location.pro | 48 + src/location/maps/maps.pri | 102 + src/location/maps/qabstractgeotilecache.cpp | 153 + src/location/maps/qabstractgeotilecache_p.h | 148 + src/location/maps/qcache3q_p.h | 477 ++ src/location/maps/qgeocameracapabilities.cpp | 468 ++ src/location/maps/qgeocameracapabilities_p.h | 114 + src/location/maps/qgeocameradata.cpp | 230 + src/location/maps/qgeocameradata_p.h | 101 + src/location/maps/qgeocameratiles.cpp | 902 ++++ src/location/maps/qgeocameratiles_p.h | 85 + src/location/maps/qgeocodereply.cpp | 338 ++ src/location/maps/qgeocodereply.h | 106 + src/location/maps/qgeocodereply_p.h | 83 + src/location/maps/qgeocodingmanager.cpp | 325 ++ src/location/maps/qgeocodingmanager.h | 92 + src/location/maps/qgeocodingmanager_p.h | 76 + src/location/maps/qgeocodingmanagerengine.cpp | 334 ++ src/location/maps/qgeocodingmanagerengine.h | 89 + src/location/maps/qgeocodingmanagerengine_p.h | 75 + src/location/maps/qgeofiletilecache.cpp | 627 +++ src/location/maps/qgeofiletilecache_p.h | 178 + src/location/maps/qgeomaneuver.cpp | 560 ++ src/location/maps/qgeomaneuver.h | 111 + src/location/maps/qgeomaneuver_p.h | 149 + src/location/maps/qgeomap.cpp | 432 ++ src/location/maps/qgeomap_p.h | 181 + src/location/maps/qgeomap_p_p.h | 122 + src/location/maps/qgeomapparameter.cpp | 102 + src/location/maps/qgeomapparameter_p.h | 90 + src/location/maps/qgeomappingmanager.cpp | 189 + src/location/maps/qgeomappingmanager_p.h | 105 + src/location/maps/qgeomappingmanager_p_p.h | 73 + .../maps/qgeomappingmanagerengine.cpp | 213 + .../maps/qgeomappingmanagerengine_p.h | 125 + .../maps/qgeomappingmanagerengine_p_p.h | 87 + src/location/maps/qgeomaptype.cpp | 157 + src/location/maps/qgeomaptype_p.h | 108 + src/location/maps/qgeomaptype_p_p.h | 91 + src/location/maps/qgeoprojection.cpp | 776 +++ src/location/maps/qgeoprojection_p.h | 243 + src/location/maps/qgeoroute.cpp | 568 ++ src/location/maps/qgeoroute.h | 102 + src/location/maps/qgeoroute_p.h | 163 + src/location/maps/qgeorouteparser.cpp | 90 + src/location/maps/qgeorouteparser_p.h | 79 + src/location/maps/qgeorouteparser_p_p.h | 71 + src/location/maps/qgeorouteparserosrmv4.cpp | 404 ++ src/location/maps/qgeorouteparserosrmv4_p.h | 72 + src/location/maps/qgeorouteparserosrmv5.cpp | 1042 ++++ src/location/maps/qgeorouteparserosrmv5_p.h | 90 + src/location/maps/qgeoroutereply.cpp | 277 + src/location/maps/qgeoroutereply.h | 96 + src/location/maps/qgeoroutereply_p.h | 80 + src/location/maps/qgeorouterequest.cpp | 532 ++ src/location/maps/qgeorouterequest.h | 168 + src/location/maps/qgeorouterequest_p.h | 85 + src/location/maps/qgeoroutesegment.cpp | 429 ++ src/location/maps/qgeoroutesegment.h | 90 + src/location/maps/qgeoroutesegment_p.h | 135 + src/location/maps/qgeoroutingmanager.cpp | 411 ++ src/location/maps/qgeoroutingmanager.h | 91 + src/location/maps/qgeoroutingmanager_p.h | 69 + .../maps/qgeoroutingmanagerengine.cpp | 423 ++ src/location/maps/qgeoroutingmanagerengine.h | 100 + .../maps/qgeoroutingmanagerengine_p.h | 83 + src/location/maps/qgeoserviceprovider.cpp | 780 +++ src/location/maps/qgeoserviceprovider.h | 178 + src/location/maps/qgeoserviceprovider_p.h | 126 + .../maps/qgeoserviceproviderfactory.cpp | 210 + .../maps/qgeoserviceproviderfactory.h | 87 + src/location/maps/qgeotiledmap.cpp | 427 ++ src/location/maps/qgeotiledmap_p.h | 112 + src/location/maps/qgeotiledmap_p_p.h | 108 + .../maps/qgeotiledmappingmanagerengine.cpp | 329 ++ .../maps/qgeotiledmappingmanagerengine_p.h | 120 + .../maps/qgeotiledmappingmanagerengine_p_p.h | 84 + src/location/maps/qgeotiledmapreply.cpp | 317 ++ src/location/maps/qgeotiledmapreply_p.h | 110 + src/location/maps/qgeotiledmapreply_p_p.h | 75 + src/location/maps/qgeotiledmapscene.cpp | 727 +++ src/location/maps/qgeotiledmapscene_p.h | 98 + src/location/maps/qgeotilefetcher.cpp | 224 + src/location/maps/qgeotilefetcher_p.h | 105 + src/location/maps/qgeotilefetcher_p_p.h | 90 + src/location/maps/qgeotilerequestmanager.cpp | 245 + src/location/maps/qgeotilerequestmanager_p.h | 81 + src/location/maps/qgeotilespec.cpp | 241 + src/location/maps/qgeotilespec_p.h | 103 + src/location/maps/qgeotilespec_p_p.h | 78 + src/location/maps/qnavigationmanager.cpp | 157 + src/location/maps/qnavigationmanager_p.h | 116 + .../maps/qnavigationmanagerengine.cpp | 126 + .../maps/qnavigationmanagerengine_p.h | 104 + src/location/places/placemacro.h | 71 + src/location/places/places.pri | 93 + src/location/places/qplace.cpp | 814 +++ src/location/places/qplace.h | 140 + src/location/places/qplace_p.h | 170 + src/location/places/qplaceattribute.cpp | 231 + src/location/places/qplaceattribute.h | 81 + src/location/places/qplaceattribute_p.h | 79 + src/location/places/qplacecategory.cpp | 222 + src/location/places/qplacecategory.h | 91 + src/location/places/qplacecategory_p.h | 78 + src/location/places/qplacecontactdetail.cpp | 211 + src/location/places/qplacecontactdetail.h | 83 + src/location/places/qplacecontactdetail_p.h | 72 + src/location/places/qplacecontent.cpp | 267 + src/location/places/qplacecontent.h | 108 + src/location/places/qplacecontent_p.h | 103 + src/location/places/qplacecontentreply.cpp | 191 + src/location/places/qplacecontentreply.h | 80 + src/location/places/qplacecontentrequest.cpp | 256 + src/location/places/qplacecontentrequest.h | 82 + src/location/places/qplacecontentrequest_p.h | 77 + src/location/places/qplacedetailsreply.cpp | 106 + src/location/places/qplacedetailsreply.h | 67 + src/location/places/qplaceeditorial.cpp | 164 + src/location/places/qplaceeditorial.h | 71 + src/location/places/qplaceeditorial_p.h | 77 + src/location/places/qplaceicon.cpp | 233 + src/location/places/qplaceicon.h | 87 + src/location/places/qplaceicon_p.h | 74 + src/location/places/qplaceidreply.cpp | 132 + src/location/places/qplaceidreply.h | 75 + src/location/places/qplaceimage.cpp | 159 + src/location/places/qplaceimage.h | 77 + src/location/places/qplaceimage_p.h | 78 + src/location/places/qplacemanager.cpp | 489 ++ src/location/places/qplacemanager.h | 127 + src/location/places/qplacemanagerengine.cpp | 460 ++ src/location/places/qplacemanagerengine.h | 121 + src/location/places/qplacemanagerengine_p.h | 72 + src/location/places/qplacematchreply.cpp | 135 + src/location/places/qplacematchreply.h | 68 + src/location/places/qplacematchrequest.cpp | 259 + src/location/places/qplacematchrequest.h | 81 + .../places/qplaceproposedsearchresult.cpp | 116 + .../places/qplaceproposedsearchresult.h | 70 + .../places/qplaceproposedsearchresult_p.h | 72 + src/location/places/qplaceratings.cpp | 189 + src/location/places/qplaceratings.h | 82 + src/location/places/qplaceratings_p.h | 74 + src/location/places/qplacereply.cpp | 245 + src/location/places/qplacereply.h | 107 + src/location/places/qplacereply_p.h | 70 + src/location/places/qplaceresult.cpp | 174 + src/location/places/qplaceresult.h | 77 + src/location/places/qplaceresult_p.h | 74 + src/location/places/qplacereview.cpp | 229 + src/location/places/qplacereview.h | 78 + src/location/places/qplacereview_p.h | 80 + src/location/places/qplacesearchreply.cpp | 173 + src/location/places/qplacesearchreply.h | 76 + src/location/places/qplacesearchrequest.cpp | 440 ++ src/location/places/qplacesearchrequest.h | 107 + src/location/places/qplacesearchresult.cpp | 214 + src/location/places/qplacesearchresult.h | 106 + src/location/places/qplacesearchresult_p.h | 107 + .../places/qplacesearchsuggestionreply.cpp | 109 + .../places/qplacesearchsuggestionreply.h | 68 + src/location/places/qplacesupplier.cpp | 223 + src/location/places/qplacesupplier.h | 85 + src/location/places/qplacesupplier_p.h | 79 + src/location/places/qplaceuser.cpp | 153 + src/location/places/qplaceuser.h | 76 + src/location/places/qplaceuser_p.h | 72 + src/location/places/unsupportedreplies_p.h | 228 + src/location/qlocation.cpp | 78 + src/location/qlocation.h | 65 + src/location/qlocationglobal.h | 60 + src/location/qlocationglobal_p.h | 63 + src/locationlabs/qlocationlabsglobal_p.h | 69 + src/plugins/geoservices/esri/esri.pro | 42 + src/plugins/geoservices/esri/esri.qrc | 5 + src/plugins/geoservices/esri/esri_plugin.json | 13 + .../geoservices/esri/geocodereply_esri.cpp | 206 + .../geoservices/esri/geocodereply_esri.h | 83 + .../esri/geocodingmanagerengine_esri.cpp | 186 + .../esri/geocodingmanagerengine_esri.h | 77 + src/plugins/geoservices/esri/geomapsource.cpp | 107 + src/plugins/geoservices/esri/geomapsource.h | 78 + .../esri/georoutejsonparser_esri.cpp | 246 + .../esri/georoutejsonparser_esri.h | 72 + .../geoservices/esri/georoutereply_esri.cpp | 96 + .../geoservices/esri/georoutereply_esri.h | 63 + .../esri/georoutingmanagerengine_esri.cpp | 190 + .../esri/georoutingmanagerengine_esri.h | 76 + .../esri/geoserviceproviderfactory_esri.cpp | 86 + .../esri/geoserviceproviderfactory_esri.h | 72 + .../geoservices/esri/geotiledmap_esri.cpp | 73 + .../geoservices/esri/geotiledmap_esri.h | 80 + .../geotiledmappingmanagerengine_esri.cpp | 304 ++ .../esri/geotiledmappingmanagerengine_esri.h | 78 + .../esri/geotiledmapreply_esri.cpp | 105 + .../geoservices/esri/geotiledmapreply_esri.h | 64 + .../geoservices/esri/geotilefetcher_esri.cpp | 77 + .../geoservices/esri/geotilefetcher_esri.h | 93 + src/plugins/geoservices/esri/maps.json | 123 + src/plugins/geoservices/geoservices.pro | 18 + .../geoservices/itemsoverlay/itemsoverlay.pro | 24 + .../itemsoverlay/itemsoverlay_plugin.json | 9 + .../itemsoverlay/qgeomapitemsoverlay.cpp | 192 + .../itemsoverlay/qgeomapitemsoverlay.h | 69 + .../qgeomappingmanagerengineitemsoverlay.cpp | 80 + .../qgeomappingmanagerengineitemsoverlay.h | 59 + .../qgeoserviceproviderpluginitemsoverlay.cpp | 78 + .../qgeoserviceproviderpluginitemsoverlay.h | 69 + .../geoservices/mapbox/maki-4.0.0/LICENSE.txt | 116 + .../mapbox/maki-4.0.0/aerialway.svg | 10 + .../mapbox/maki-4.0.0/airfield.svg | 11 + .../geoservices/mapbox/maki-4.0.0/airport.svg | 11 + .../mapbox/maki-4.0.0/alcohol-shop.svg | 12 + .../mapbox/maki-4.0.0/america-football.svg | 10 + .../mapbox/maki-4.0.0/amusement-park.svg | 20 + .../mapbox/maki-4.0.0/aquarium.svg | 12 + .../mapbox/maki-4.0.0/art-gallery.svg | 10 + .../mapbox/maki-4.0.0/attraction.svg | 13 + .../geoservices/mapbox/maki-4.0.0/bakery.svg | 15 + .../geoservices/mapbox/maki-4.0.0/bank.svg | 19 + .../geoservices/mapbox/maki-4.0.0/bar.svg | 11 + .../geoservices/mapbox/maki-4.0.0/barrier.svg | 4 + .../mapbox/maki-4.0.0/baseball.svg | 12 + .../mapbox/maki-4.0.0/basketball.svg | 13 + .../geoservices/mapbox/maki-4.0.0/bbq.svg | 17 + .../geoservices/mapbox/maki-4.0.0/beer.svg | 11 + .../mapbox/maki-4.0.0/bicycle-share.svg | 20 + .../geoservices/mapbox/maki-4.0.0/bicycle.svg | 21 + .../mapbox/maki-4.0.0/blood-bank.svg | 8 + .../mapbox/maki-4.0.0/buddhism.svg | 24 + .../mapbox/maki-4.0.0/building-alt1.svg | 7 + .../mapbox/maki-4.0.0/building.svg | 7 + .../geoservices/mapbox/maki-4.0.0/bus.svg | 13 + .../geoservices/mapbox/maki-4.0.0/cafe.svg | 9 + .../mapbox/maki-4.0.0/campsite.svg | 11 + .../geoservices/mapbox/maki-4.0.0/car.svg | 10 + .../geoservices/mapbox/maki-4.0.0/castle.svg | 11 + .../mapbox/maki-4.0.0/cemetery.svg | 9 + .../geoservices/mapbox/maki-4.0.0/cinema.svg | 11 + .../mapbox/maki-4.0.0/circle-stroked.svg | 11 + .../geoservices/mapbox/maki-4.0.0/circle.svg | 7 + .../geoservices/mapbox/maki-4.0.0/city.svg | 14 + .../mapbox/maki-4.0.0/clothing-store.svg | 10 + .../geoservices/mapbox/maki-4.0.0/college.svg | 5 + .../mapbox/maki-4.0.0/commercial.svg | 7 + .../geoservices/mapbox/maki-4.0.0/cricket.svg | 14 + .../geoservices/mapbox/maki-4.0.0/cross.svg | 11 + .../geoservices/mapbox/maki-4.0.0/dam.svg | 20 + .../geoservices/mapbox/maki-4.0.0/danger.svg | 18 + .../mapbox/maki-4.0.0/defibrillator.svg | 4 + .../geoservices/mapbox/maki-4.0.0/dentist.svg | 9 + .../geoservices/mapbox/maki-4.0.0/doctor.svg | 13 + .../mapbox/maki-4.0.0/dog-park.svg | 49 + .../mapbox/maki-4.0.0/drinking-water.svg | 5 + .../geoservices/mapbox/maki-4.0.0/embassy.svg | 12 + .../mapbox/maki-4.0.0/emergency-phone.svg | 4 + .../mapbox/maki-4.0.0/entrance-alt1.svg | 4 + .../mapbox/maki-4.0.0/entrance.svg | 11 + .../geoservices/mapbox/maki-4.0.0/farm.svg | 4 + .../mapbox/maki-4.0.0/fast-food.svg | 12 + .../geoservices/mapbox/maki-4.0.0/fence.svg | 4 + .../geoservices/mapbox/maki-4.0.0/ferry.svg | 19 + .../mapbox/maki-4.0.0/fire-station.svg | 12 + .../geoservices/mapbox/maki-4.0.0/florist.svg | 4 + .../geoservices/mapbox/maki-4.0.0/fuel.svg | 12 + .../geoservices/mapbox/maki-4.0.0/gaming.svg | 11 + .../mapbox/maki-4.0.0/garden-center.svg | 5 + .../geoservices/mapbox/maki-4.0.0/garden.svg | 10 + .../geoservices/mapbox/maki-4.0.0/gift.svg | 13 + .../geoservices/mapbox/maki-4.0.0/golf.svg | 13 + .../geoservices/mapbox/maki-4.0.0/grocery.svg | 48 + .../mapbox/maki-4.0.0/hairdresser.svg | 13 + .../geoservices/mapbox/maki-4.0.0/harbor.svg | 14 + .../geoservices/mapbox/maki-4.0.0/heart.svg | 8 + .../mapbox/maki-4.0.0/heliport.svg | 13 + .../geoservices/mapbox/maki-4.0.0/home.svg | 8 + .../mapbox/maki-4.0.0/horse-riding.svg | 4 + .../mapbox/maki-4.0.0/hospital.svg | 10 + .../mapbox/maki-4.0.0/ice-cream.svg | 11 + .../mapbox/maki-4.0.0/industry.svg | 9 + .../mapbox/maki-4.0.0/information.svg | 11 + .../geoservices/mapbox/maki-4.0.0/karaoke.svg | 7 + .../mapbox/maki-4.0.0/landmark.svg | 9 + .../geoservices/mapbox/maki-4.0.0/landuse.svg | 4 + .../geoservices/mapbox/maki-4.0.0/laundry.svg | 11 + .../geoservices/mapbox/maki-4.0.0/library.svg | 15 + .../mapbox/maki-4.0.0/lighthouse.svg | 9 + .../geoservices/mapbox/maki-4.0.0/lodging.svg | 12 + .../geoservices/mapbox/maki-4.0.0/logging.svg | 4 + .../mapbox/maki-4.0.0/marker-stroked.svg | 4 + .../geoservices/mapbox/maki-4.0.0/marker.svg | 10 + .../mapbox/maki-4.0.0/mobile-phone.svg | 4 + .../mapbox/maki-4.0.0/monument.svg | 10 + .../mapbox/maki-4.0.0/mountain.svg | 11 + .../geoservices/mapbox/maki-4.0.0/museum.svg | 14 + .../geoservices/mapbox/maki-4.0.0/music.svg | 9 + .../geoservices/mapbox/maki-4.0.0/natural.svg | 4 + .../mapbox/maki-4.0.0/park-alt1.svg | 4 + .../geoservices/mapbox/maki-4.0.0/park.svg | 15 + .../mapbox/maki-4.0.0/parking-garage.svg | 12 + .../geoservices/mapbox/maki-4.0.0/parking.svg | 10 + .../mapbox/maki-4.0.0/pharmacy.svg | 9 + .../mapbox/maki-4.0.0/picnic-site.svg | 13 + .../geoservices/mapbox/maki-4.0.0/pitch.svg | 9 + .../mapbox/maki-4.0.0/place-of-worship.svg | 11 + .../mapbox/maki-4.0.0/playground.svg | 16 + .../geoservices/mapbox/maki-4.0.0/police.svg | 12 + .../geoservices/mapbox/maki-4.0.0/post.svg | 10 + .../geoservices/mapbox/maki-4.0.0/prison.svg | 11 + .../mapbox/maki-4.0.0/rail-light.svg | 12 + .../mapbox/maki-4.0.0/rail-metro.svg | 13 + .../geoservices/mapbox/maki-4.0.0/rail.svg | 49 + .../mapbox/maki-4.0.0/ranger-station.svg | 10 + .../mapbox/maki-4.0.0/recycling.svg | 10 + .../mapbox/maki-4.0.0/religious-christian.svg | 10 + .../mapbox/maki-4.0.0/religious-jewish.svg | 7 + .../mapbox/maki-4.0.0/religious-muslim.svg | 12 + .../maki-4.0.0/residential-community.svg | 8 + .../mapbox/maki-4.0.0/restaurant.svg | 12 + .../mapbox/maki-4.0.0/roadblock.svg | 7 + .../geoservices/mapbox/maki-4.0.0/rocket.svg | 13 + .../geoservices/mapbox/maki-4.0.0/school.svg | 7 + .../geoservices/mapbox/maki-4.0.0/scooter.svg | 4 + .../geoservices/mapbox/maki-4.0.0/shelter.svg | 7 + .../geoservices/mapbox/maki-4.0.0/shop.svg | 12 + .../geoservices/mapbox/maki-4.0.0/skiing.svg | 11 + .../mapbox/maki-4.0.0/slaughterhouse.svg | 4 + .../mapbox/maki-4.0.0/snowmobile.svg | 4 + .../geoservices/mapbox/maki-4.0.0/soccer.svg | 12 + .../mapbox/maki-4.0.0/square-stroked.svg | 8 + .../geoservices/mapbox/maki-4.0.0/square.svg | 7 + .../geoservices/mapbox/maki-4.0.0/stadium.svg | 13 + .../mapbox/maki-4.0.0/star-stroked.svg | 9 + .../geoservices/mapbox/maki-4.0.0/star.svg | 10 + .../mapbox/maki-4.0.0/suitcase.svg | 10 + .../geoservices/mapbox/maki-4.0.0/sushi.svg | 19 + .../mapbox/maki-4.0.0/swimming.svg | 13 + .../mapbox/maki-4.0.0/teahouse.svg | 8 + .../mapbox/maki-4.0.0/telephone.svg | 4 + .../geoservices/mapbox/maki-4.0.0/tennis.svg | 16 + .../geoservices/mapbox/maki-4.0.0/theatre.svg | 13 + .../geoservices/mapbox/maki-4.0.0/toilet.svg | 16 + .../mapbox/maki-4.0.0/town-hall.svg | 10 + .../geoservices/mapbox/maki-4.0.0/town.svg | 4 + .../mapbox/maki-4.0.0/triangle-stroked.svg | 12 + .../mapbox/maki-4.0.0/triangle.svg | 11 + .../mapbox/maki-4.0.0/veterinary.svg | 18 + .../geoservices/mapbox/maki-4.0.0/village.svg | 4 + .../geoservices/mapbox/maki-4.0.0/volcano.svg | 16 + .../mapbox/maki-4.0.0/warehouse.svg | 9 + .../mapbox/maki-4.0.0/waste-basket.svg | 10 + .../geoservices/mapbox/maki-4.0.0/water.svg | 7 + .../geoservices/mapbox/maki-4.0.0/wetland.svg | 21 + .../mapbox/maki-4.0.0/wheelchair.svg | 17 + .../geoservices/mapbox/maki-4.0.0/zoo.svg | 15 + src/plugins/geoservices/mapbox/mapbox.pro | 47 + src/plugins/geoservices/mapbox/mapbox.qrc | 150 + .../geoservices/mapbox/mapbox_plugin.json | 17 + .../mapbox/qgeocodereplymapbox.cpp | 106 + .../geoservices/mapbox/qgeocodereplymapbox.h | 63 + .../mapbox/qgeocodingmanagerenginemapbox.cpp | 205 + .../mapbox/qgeocodingmanagerenginemapbox.h | 83 + .../mapbox/qgeofiletilecachemapbox.cpp | 131 + .../mapbox/qgeofiletilecachemapbox.h | 63 + .../geoservices/mapbox/qgeomapreplymapbox.cpp | 80 + .../geoservices/mapbox/qgeomapreplymapbox.h | 64 + .../mapbox/qgeoroutereplymapbox.cpp | 157 + .../geoservices/mapbox/qgeoroutereplymapbox.h | 66 + .../mapbox/qgeoroutingmanagerenginemapbox.cpp | 309 ++ .../mapbox/qgeoroutingmanagerenginemapbox.h | 80 + .../qgeoserviceproviderpluginmapbox.cpp | 109 + .../mapbox/qgeoserviceproviderpluginmapbox.h | 69 + .../qgeotiledmappingmanagerenginemapbox.cpp | 260 + .../qgeotiledmappingmanagerenginemapbox.h | 63 + .../mapbox/qgeotilefetchermapbox.cpp | 104 + .../mapbox/qgeotilefetchermapbox.h | 74 + .../geoservices/mapbox/qmapboxcommon.cpp | 140 + .../geoservices/mapbox/qmapboxcommon.h | 71 + .../mapbox/qplacecategoriesreplymapbox.cpp | 66 + .../mapbox/qplacecategoriesreplymapbox.h | 62 + .../mapbox/qplacemanagerenginemapbox.cpp | 318 ++ .../mapbox/qplacemanagerenginemapbox.h | 102 + .../mapbox/qplacesearchreplymapbox.cpp | 231 + .../mapbox/qplacesearchreplymapbox.h | 71 + .../qplacesearchsuggestionreplymapbox.cpp | 120 + .../qplacesearchsuggestionreplymapbox.h | 70 + src/plugins/geoservices/mapboxgl/logo.png | Bin 0 -> 2749 bytes src/plugins/geoservices/mapboxgl/mapboxgl.pro | 48 + src/plugins/geoservices/mapboxgl/mapboxgl.qrc | 5 + .../geoservices/mapboxgl/mapboxgl_plugin.json | 11 + .../geoservices/mapboxgl/qgeomapmapboxgl.cpp | 469 ++ .../geoservices/mapboxgl/qgeomapmapboxgl.h | 84 + .../geoservices/mapboxgl/qgeomapmapboxgl_p.h | 108 + .../qgeomappingmanagerenginemapboxgl.cpp | 166 + .../qgeomappingmanagerenginemapboxgl.h | 67 + .../qgeoserviceproviderpluginmapboxgl.cpp | 91 + .../qgeoserviceproviderpluginmapboxgl.h | 72 + .../mapboxgl/qmapboxglstylechange.cpp | 678 +++ .../mapboxgl/qmapboxglstylechange_p.h | 206 + .../geoservices/mapboxgl/qsgmapboxglnode.cpp | 160 + .../geoservices/mapboxgl/qsgmapboxglnode.h | 81 + src/plugins/geoservices/nokia/logo.png | Bin 0 -> 1217 bytes .../geoservices/nokia/marclanguagecodes.h | 312 ++ src/plugins/geoservices/nokia/nokia.pro | 60 + src/plugins/geoservices/nokia/nokia.qrc | 5 + .../geoservices/nokia/nokia_plugin.json | 19 + .../nokia/placesv2/jsonparserhelpers.cpp | 243 + .../nokia/placesv2/jsonparserhelpers.h | 78 + .../geoservices/nokia/placesv2/placesv2.pri | 21 + .../placesv2/qplacecategoriesreplyhere.cpp | 62 + .../placesv2/qplacecategoriesreplyhere.h | 60 + .../nokia/placesv2/qplacecontentreplyimpl.cpp | 122 + .../nokia/placesv2/qplacecontentreplyimpl.h | 68 + .../nokia/placesv2/qplacedetailsreplyimpl.cpp | 345 ++ .../nokia/placesv2/qplacedetailsreplyimpl.h | 70 + .../nokia/placesv2/qplaceidreplyimpl.cpp | 66 + .../nokia/placesv2/qplaceidreplyimpl.h | 60 + .../nokia/placesv2/qplacesearchreplyhere.cpp | 228 + .../nokia/placesv2/qplacesearchreplyhere.h | 73 + .../qplacesearchsuggestionreplyimpl.cpp | 117 + .../qplacesearchsuggestionreplyimpl.h | 61 + .../geoservices/nokia/qgeocodejsonparser.cpp | 413 ++ .../geoservices/nokia/qgeocodejsonparser.h | 75 + .../geoservices/nokia/qgeocodereply_nokia.cpp | 127 + .../geoservices/nokia/qgeocodereply_nokia.h | 65 + .../nokia/qgeocodingmanagerengine_nokia.cpp | 363 ++ .../nokia/qgeocodingmanagerengine_nokia.h | 91 + .../geoservices/nokia/qgeoerror_messages.cpp | 52 + .../geoservices/nokia/qgeoerror_messages.h | 58 + .../nokia/qgeofiletilecachenokia.cpp | 124 + .../nokia/qgeofiletilecachenokia.h | 60 + .../qgeointrinsicnetworkaccessmanager.cpp | 100 + .../nokia/qgeointrinsicnetworkaccessmanager.h | 66 + .../geoservices/nokia/qgeomapreply_nokia.cpp | 91 + .../geoservices/nokia/qgeomapreply_nokia.h | 62 + .../geoservices/nokia/qgeomapversion.cpp | 79 + .../geoservices/nokia/qgeomapversion.h | 63 + .../nokia/qgeonetworkaccessmanager.h | 62 + .../nokia/qgeoroutereply_nokia.cpp | 128 + .../geoservices/nokia/qgeoroutereply_nokia.h | 66 + .../geoservices/nokia/qgeoroutexmlparser.cpp | 605 +++ .../geoservices/nokia/qgeoroutexmlparser.h | 126 + .../nokia/qgeoroutingmanagerengine_nokia.cpp | 503 ++ .../nokia/qgeoroutingmanagerengine_nokia.h | 86 + .../nokia/qgeoserviceproviderplugin_nokia.cpp | 155 + .../nokia/qgeoserviceproviderplugin_nokia.h | 73 + .../geoservices/nokia/qgeotiledmap_nokia.cpp | 112 + .../geoservices/nokia/qgeotiledmap_nokia.h | 75 + .../qgeotiledmappingmanagerengine_nokia.cpp | 454 ++ .../qgeotiledmappingmanagerengine_nokia.h | 110 + .../nokia/qgeotilefetcher_nokia.cpp | 336 ++ .../geoservices/nokia/qgeotilefetcher_nokia.h | 96 + .../geoservices/nokia/qgeouriprovider.cpp | 98 + .../geoservices/nokia/qgeouriprovider.h | 76 + .../nokia/qplacemanagerengine_nokiav2.cpp | 864 +++ .../nokia/qplacemanagerengine_nokiav2.h | 134 + .../geoservices/nokia/uri_constants.cpp | 47 + src/plugins/geoservices/nokia/uri_constants.h | 53 + src/plugins/geoservices/osm/osm.pro | 46 + src/plugins/geoservices/osm/osm_plugin.json | 13 + .../geoservices/osm/providers/5.8/cycle | 8 + .../geoservices/osm/providers/5.8/hiking | 9 + .../osm/providers/5.8/night-transit | 9 + .../geoservices/osm/providers/5.8/satellite | 10 + .../geoservices/osm/providers/5.8/street | 10 + .../osm/providers/5.8/street-hires | 9 + .../geoservices/osm/providers/5.8/terrain | 9 + .../geoservices/osm/providers/5.8/transit | 9 + .../geoservices/osm/qgeocodereplyosm.cpp | 174 + .../geoservices/osm/qgeocodereplyosm.h | 63 + .../osm/qgeocodingmanagerengineosm.cpp | 182 + .../osm/qgeocodingmanagerengineosm.h | 78 + .../geoservices/osm/qgeofiletilecacheosm.cpp | 318 ++ .../geoservices/osm/qgeofiletilecacheosm.h | 87 + .../geoservices/osm/qgeomapreplyosm.cpp | 89 + src/plugins/geoservices/osm/qgeomapreplyosm.h | 63 + .../geoservices/osm/qgeoroutereplyosm.cpp | 96 + .../geoservices/osm/qgeoroutereplyosm.h | 65 + .../osm/qgeoroutingmanagerengineosm.cpp | 115 + .../osm/qgeoroutingmanagerengineosm.h | 78 + .../osm/qgeoserviceproviderpluginosm.cpp | 72 + .../osm/qgeoserviceproviderpluginosm.h | 72 + .../geoservices/osm/qgeotiledmaposm.cpp | 118 + src/plugins/geoservices/osm/qgeotiledmaposm.h | 77 + .../osm/qgeotiledmappingmanagerengineosm.cpp | 383 ++ .../osm/qgeotiledmappingmanagerengineosm.h | 82 + .../geoservices/osm/qgeotilefetcherosm.cpp | 171 + .../geoservices/osm/qgeotilefetcherosm.h | 90 + .../geoservices/osm/qgeotileproviderosm.cpp | 644 +++ .../geoservices/osm/qgeotileproviderosm.h | 193 + .../osm/qplacecategoriesreplyosm.cpp | 65 + .../osm/qplacecategoriesreplyosm.h | 61 + .../osm/qplacemanagerengineosm.cpp | 359 ++ .../geoservices/osm/qplacemanagerengineosm.h | 99 + .../geoservices/osm/qplacesearchreplyosm.cpp | 227 + .../geoservices/osm/qplacesearchreplyosm.h | 74 + src/plugins/plugins.pro | 3 + src/plugins/position/android/android.pro | 2 + .../position/android/jar/AndroidManifest.xml | 6 + src/plugins/position/android/jar/jar.pro | 16 + .../android/positioning/QtPositioning.java | 589 ++ .../position/android/src/jnipositioning.cpp | 606 +++ .../position/android/src/jnipositioning.h | 63 + src/plugins/position/android/src/plugin.json | 9 + .../android/src/positionfactory_android.cpp | 60 + .../android/src/positionfactory_android.h | 58 + .../src/qgeopositioninfosource_android.cpp | 262 + .../src/qgeopositioninfosource_android_p.h | 97 + .../src/qgeosatelliteinfosource_android.cpp | 216 + .../src/qgeosatelliteinfosource_android_p.h | 97 + src/plugins/position/android/src/src.pro | 21 + .../position/corelocation/corelocation.pro | 24 + src/plugins/position/corelocation/plugin.json | 9 + .../corelocation/qgeopositioninfosource_cl.mm | 287 + .../qgeopositioninfosource_cl_p.h | 108 + .../qgeopositioninfosourcefactory_cl.h | 58 + .../qgeopositioninfosourcefactory_cl.mm | 58 + src/plugins/position/geoclue/geoclue.pro | 38 + src/plugins/position/geoclue/geocluetypes.cpp | 105 + src/plugins/position/geoclue/geocluetypes.h | 93 + .../org.freedesktop.Geoclue.Master.xml | 10 + .../org.freedesktop.Geoclue.MasterClient.xml | 41 + .../org.freedesktop.Geoclue.Position.xml | 25 + .../org.freedesktop.Geoclue.Satellite.xml | 33 + .../org.freedesktop.Geoclue.Velocity.xml | 20 + .../geoclue/org.freedesktop.Geoclue.xml | 24 + src/plugins/position/geoclue/plugin.json | 9 + .../position/geoclue/qgeocluemaster.cpp | 137 + src/plugins/position/geoclue/qgeocluemaster.h | 90 + .../qgeopositioninfosource_geocluemaster.cpp | 495 ++ .../qgeopositioninfosource_geocluemaster.h | 143 + .../qgeopositioninfosourcefactory_geoclue.cpp | 68 + .../qgeopositioninfosourcefactory_geoclue.h | 70 + .../qgeosatelliteinfosource_geocluemaster.cpp | 310 ++ .../qgeosatelliteinfosource_geocluemaster.h | 105 + src/plugins/position/gypsy/gypsy.pro | 20 + src/plugins/position/gypsy/plugin.json | 9 + .../qgeopositioninfosourcefactory_gypsy.cpp | 63 + .../qgeopositioninfosourcefactory_gypsy.h | 59 + .../gypsy/qgeosatelliteinfosource_gypsy.cpp | 373 ++ .../gypsy/qgeosatelliteinfosource_gypsy_p.h | 141 + src/plugins/position/position.pro | 14 + src/plugins/position/positionpoll/plugin.json | 9 + .../position/positionpoll/positionpoll.pro | 18 + .../positionpoll/positionpollfactory.cpp | 62 + .../positionpoll/positionpollfactory.h | 58 + .../positionpoll/qgeoareamonitor_polling.cpp | 497 ++ .../positionpoll/qgeoareamonitor_polling.h | 94 + src/plugins/position/serialnmea/plugin.json | 9 + ...eopositioninfosourcefactory_serialnmea.cpp | 127 + ...qgeopositioninfosourcefactory_serialnmea.h | 59 + .../position/serialnmea/serialnmea.pro | 16 + src/plugins/position/simulator/plugin.json | 9 + .../qgeopositioninfosource_simulator.cpp | 170 + .../qgeopositioninfosource_simulator_p.h | 95 + ...geopositioninfosourcefactory_simulator.cpp | 61 + .../qgeopositioninfosourcefactory_simulator.h | 61 + .../qgeosatelliteinfosource_simulator.cpp | 132 + .../qgeosatelliteinfosource_simulator_p.h | 94 + .../qlocationconnection_simulator.cpp | 126 + .../qlocationconnection_simulator_p.h | 97 + src/plugins/position/simulator/simulator.pro | 22 + src/plugins/position/winrt/plugin.json | 9 + .../winrt/qgeopositioninfosource_winrt.cpp | 567 ++ .../winrt/qgeopositioninfosource_winrt_p.h | 125 + .../qgeopositioninfosourcefactory_winrt.cpp | 60 + .../qgeopositioninfosourcefactory_winrt.h | 59 + src/plugins/position/winrt/winrt.pro | 16 + src/positioning/configure.json | 48 + src/positioning/doc/qtpositioning.qdocconf | 57 + src/positioning/doc/snippets/cpp/cpp.pro | 8 + src/positioning/doc/snippets/cpp/cppqml.cpp | 118 + src/positioning/doc/snippets/cpp/main.cpp | 55 + .../doc/snippets/doc_src_qtpositioning.qml | 57 + src/positioning/doc/snippets/snippets.pro | 2 + src/positioning/doc/src/cpp-position.qdoc | 191 + .../doc/src/cpp-qml-positioning.qdoc | 96 + src/positioning/doc/src/qml-position.qdoc | 119 + .../doc/src/qtpositioning-examples.qdoc | 37 + .../doc/src/qtpositioning-plugins.qdoc | 82 + .../doc/src/qtpositioning-qml.qdoc | 65 + src/positioning/doc/src/qtpositioning.qdoc | 136 + src/positioning/positioning.pro | 98 + src/positioning/qclipperutils.cpp | 100 + src/positioning/qclipperutils_p.h | 86 + src/positioning/qdeclarativegeoaddress.cpp | 353 ++ src/positioning/qdeclarativegeoaddress_p.h | 120 + src/positioning/qdeclarativegeolocation.cpp | 190 + src/positioning/qdeclarativegeolocation_p.h | 99 + src/positioning/qdoublematrix4x4.cpp | 1112 ++++ src/positioning/qdoublematrix4x4_p.h | 946 ++++ src/positioning/qdoublevector2d.cpp | 121 + src/positioning/qdoublevector2d_p.h | 262 + src/positioning/qdoublevector3d.cpp | 144 + src/positioning/qdoublevector3d_p.h | 302 ++ src/positioning/qgeoaddress.cpp | 635 +++ src/positioning/qgeoaddress.h | 106 + src/positioning/qgeoaddress_p.h | 80 + src/positioning/qgeoareamonitorinfo.cpp | 379 ++ src/positioning/qgeoareamonitorinfo.h | 103 + src/positioning/qgeoareamonitorsource.cpp | 389 ++ src/positioning/qgeoareamonitorsource.h | 108 + src/positioning/qgeocircle.cpp | 492 ++ src/positioning/qgeocircle.h | 96 + src/positioning/qgeocircle_p.h | 94 + src/positioning/qgeocoordinate.cpp | 767 +++ src/positioning/qgeocoordinate.h | 136 + src/positioning/qgeocoordinate_p.h | 92 + src/positioning/qgeocoordinateobject.cpp | 92 + src/positioning/qgeocoordinateobject_p.h | 94 + src/positioning/qgeolocation.cpp | 201 + src/positioning/qgeolocation.h | 88 + src/positioning/qgeolocation_p.h | 80 + src/positioning/qgeopath.cpp | 788 +++ src/positioning/qgeopath.h | 106 + src/positioning/qgeopath_p.h | 117 + src/positioning/qgeopolygon.cpp | 327 ++ src/positioning/qgeopolygon.h | 99 + src/positioning/qgeopositioninfo.cpp | 378 ++ src/positioning/qgeopositioninfo.h | 117 + src/positioning/qgeopositioninfo_p.h | 78 + src/positioning/qgeopositioninfosource.cpp | 479 ++ src/positioning/qgeopositioninfosource.h | 114 + src/positioning/qgeopositioninfosource_p.h | 82 + .../qgeopositioninfosourcefactory.cpp | 87 + .../qgeopositioninfosourcefactory.h | 66 + src/positioning/qgeorectangle.cpp | 1040 ++++ src/positioning/qgeorectangle.h | 129 + src/positioning/qgeorectangle_p.h | 87 + src/positioning/qgeosatelliteinfo.cpp | 314 ++ src/positioning/qgeosatelliteinfo.h | 112 + src/positioning/qgeosatelliteinfosource.cpp | 348 ++ src/positioning/qgeosatelliteinfosource.h | 98 + src/positioning/qgeoshape.cpp | 454 ++ src/positioning/qgeoshape.h | 114 + src/positioning/qgeoshape_p.h | 93 + src/positioning/qlocationdata_simulator.cpp | 119 + src/positioning/qlocationdata_simulator_p.h | 134 + src/positioning/qlocationutils.cpp | 416 ++ src/positioning/qlocationutils_p.h | 302 ++ src/positioning/qnmeapositioninfosource.cpp | 961 ++++ src/positioning/qnmeapositioninfosource.h | 97 + src/positioning/qnmeapositioninfosource_p.h | 188 + src/positioning/qpositioningglobal.h | 60 + src/positioning/qpositioningglobal_p.h | 62 + src/positioning/qwebmercator.cpp | 136 + src/positioning/qwebmercator_p.h | 75 + src/positioningquick/positioningquick.pro | 10 + src/positioningquick/qdeclarativeposition.cpp | 474 ++ src/positioningquick/qdeclarativeposition_p.h | 149 + .../qdeclarativepositionsource.cpp | 768 +++ .../qdeclarativepositionsource_p.h | 174 + .../qpositioningquickglobal.h | 69 + .../qpositioningquickglobal_p.h | 61 + src/src.pro | 44 + sync.profile | 7 + .../applications/positioning_backend/main.cpp | 48 + .../positioning_backend.pro | 14 + .../positioning_backend/widget.cpp | 165 + .../applications/positioning_backend/widget.h | 66 + .../positioning_backend/widget.ui | 323 ++ tests/auto/auto.pro | 85 + .../QtPositioning.5.10.0.linux-gcc-amd64.txt | 4773 +++++++++++++++++ .../QtPositioning.5.11.0.linux-gcc-amd64.txt | 4773 +++++++++++++++++ .../QtPositioning.5.3.0.linux-gcc-amd64.txt | 3822 +++++++++++++ .../QtPositioning.5.4.0.linux-gcc-amd64.txt | 3854 +++++++++++++ .../QtPositioning.5.6.0.linux-gcc-amd64.txt | 4118 ++++++++++++++ .../QtPositioning.5.7.0.linux-gcc-amd64.txt | 4400 +++++++++++++++ .../QtPositioning.5.8.0.linux-gcc-amd64.txt | 4425 +++++++++++++++ .../QtPositioning.5.9.0.linux-gcc-amd64.txt | 4446 +++++++++++++++ tests/auto/cmake/CMakeLists.txt | 19 + tests/auto/cmake/cmake.pro | 7 + .../declarative_core/declarative_core.pro | 14 + tests/auto/declarative_core/main.cpp | 48 + tests/auto/declarative_core/tst_address.qml | 88 + tests/auto/declarative_core/tst_category.qml | 236 + .../declarative_core/tst_categorymodel.qml | 237 + .../declarative_core/tst_contactdetail.qml | 71 + .../auto/declarative_core/tst_coordinate.qml | 359 ++ .../declarative_core/tst_editorialmodel.qml | 186 + tests/auto/declarative_core/tst_geocoding.qml | 641 +++ .../auto/declarative_core/tst_imagemodel.qml | 186 + tests/auto/declarative_core/tst_place.qml | 627 +++ .../declarative_core/tst_placeattribute.qml | 53 + tests/auto/declarative_core/tst_placeicon.qml | 106 + .../declarative_core/tst_placesearchmodel.qml | 311 ++ .../tst_placesearchsuggestionmodel.qml | 153 + tests/auto/declarative_core/tst_plugin.qml | 148 + .../declarative_core/tst_plugin_error.qml | 61 + tests/auto/declarative_core/tst_position.qml | 99 + .../declarative_core/tst_positionsource.qml | 164 + tests/auto/declarative_core/tst_ratings.qml | 75 + .../auto/declarative_core/tst_reviewmodel.qml | 201 + tests/auto/declarative_core/tst_routing.qml | 950 ++++ tests/auto/declarative_core/tst_supplier.qml | 100 + tests/auto/declarative_core/tst_user.qml | 71 + tests/auto/declarative_core/utils.js | 182 + .../declarative_geoshape.pro | 11 + tests/auto/declarative_geoshape/main.cpp | 30 + .../tst_locationsingleton.qml | 317 ++ tests/auto/declarative_ui/BLACKLIST | 20 + tests/auto/declarative_ui/ItemGroup.qml | 62 + tests/auto/declarative_ui/declarative_ui.pro | 21 + tests/auto/declarative_ui/main.cpp | 48 + tests/auto/declarative_ui/tst_map.qml | 688 +++ .../tst_map_coordinateanimation.qml | 137 + tests/auto/declarative_ui/tst_map_error.qml | 215 + tests/auto/declarative_ui/tst_map_flick.qml | 364 ++ tests/auto/declarative_ui/tst_map_item.qml | 623 +++ .../declarative_ui/tst_map_item_details.qml | 651 +++ .../tst_map_item_fit_viewport.qml | 690 +++ .../auto/declarative_ui/tst_map_itemview.qml | 576 ++ .../auto/declarative_ui/tst_map_keepgrab.qml | 156 + tests/auto/declarative_ui/tst_map_maptype.qml | 222 + tests/auto/declarative_ui/tst_map_mouse.qml | 731 +++ .../tst_map_pinch.qml.QTBUG-47970 | 576 ++ tests/auto/doublevectors/doublevectors.pro | 7 + .../auto/doublevectors/tst_doublevectors.cpp | 293 + tests/auto/geotestplugin/geotestplugin.json | 19 + tests/auto/geotestplugin/geotestplugin.pro | 23 + tests/auto/geotestplugin/place_data.json | 146 + .../qgeocodingmanagerengine_test.h | 269 + .../qgeomappingmanagerengine_test.h | 175 + .../qgeoroutingmanagerengine_test.h | 225 + .../qgeoserviceproviderplugin_test.cpp | 93 + .../qgeoserviceproviderplugin_test.h | 64 + .../auto/geotestplugin/qgeotiledmap_test.cpp | 88 + tests/auto/geotestplugin/qgeotiledmap_test.h | 57 + .../qgeotiledmappingmanagerengine_test.h | 115 + .../auto/geotestplugin/qgeotilefetcher_test.h | 179 + .../geotestplugin/qplacemanagerengine_test.h | 732 +++ tests/auto/geotestplugin/testdata.qrc | 5 + tests/auto/maptype/maptype.pro | 7 + tests/auto/maptype/tst_maptype.cpp | 190 + tests/auto/nokia_services/nokia_services.pro | 2 + .../places_semiauto/places_semiauto.pro | 10 + .../places_semiauto/tst_places.cpp | 747 +++ .../nokia_services/routing/error-no-route.xml | 1 + .../invalid-response-half-way-through.xml | 150 + ...invalid-response-no-calculateroute-tag.xml | 1 + .../routing/invalid-response-no-route-tag.xml | 18 + .../routing/invalid-response-trash.xml | Bin 0 -> 785 bytes .../routing/littered-with-new-tags.xml | 648 +++ .../routing/multiple-routes-in-response.xml | 1 + .../nokia_services/routing/optim-fastest.xml | 628 +++ .../nokia_services/routing/optim-shortest.xml | 1 + tests/auto/nokia_services/routing/routing.pro | 13 + .../nokia_services/routing/travelmode-car.xml | 628 +++ .../routing/travelmode-pedestrian.xml | 798 +++ .../routing/travelmode-public-transport.xml | 343 ++ .../nokia_services/routing/tst_routing.cpp | 518 ++ .../placemanager_utils/placemanager_utils.cpp | 376 ++ .../placemanager_utils/placemanager_utils.h | 230 + .../placesplugin.json | 8 + .../placesplugin_unsupported.pro | 14 + .../qgeoserviceproviderplugin_test.cpp | 50 + .../qgeoserviceproviderplugin_test.h | 54 + tests/auto/positionplugin/plugin.cpp | 213 + tests/auto/positionplugin/plugin.json | 9 + tests/auto/positionplugin/positionplugin.pro | 12 + .../positionplugintest/positionplugintest.pro | 9 + .../positionplugintest/tst_positionplugin.cpp | 110 + tests/auto/qgeoaddress/qgeoaddress.pro | 7 + tests/auto/qgeoaddress/tst_qgeoaddress.cpp | 559 ++ .../qgeoareamonitor/logfilepositionsource.cpp | 112 + .../qgeoareamonitor/logfilepositionsource.h | 66 + .../auto/qgeoareamonitor/qgeoareamonitor.pro | 14 + tests/auto/qgeoareamonitor/simplelog.txt | 87 + .../qgeoareamonitor/tst_qgeoareamonitor.cpp | 760 +++ .../qgeocameracapabilities.pro | 9 + .../tst_qgeocameracapabilities.cpp | 359 ++ tests/auto/qgeocameradata/qgeocameradata.pro | 9 + .../qgeocameradata/tst_qgeocameradata.cpp | 221 + .../auto/qgeocameratiles/qgeocameratiles.pro | 8 + .../qgeocameratiles/tst_qgeocameratiles.cpp | 1830 +++++++ tests/auto/qgeocircle/qgeocircle.pro | 8 + tests/auto/qgeocircle/tst_qgeocircle.cpp | 453 ++ tests/auto/qgeocodereply/qgeocodereply.pro | 10 + .../auto/qgeocodereply/tst_qgeocodereply.cpp | 275 + tests/auto/qgeocodereply/tst_qgeocodereply.h | 105 + .../qgeocodingmanager/qgeocodingmanager.pro | 12 + .../tst_qgeocodingmanager.cpp | 183 + .../qgeocodingmanager/tst_qgeocodingmanager.h | 76 + .../geocoding_plugin.json | 10 + .../qgeocodingmanagerengine_test.h | 107 + .../qgeocodingmanagerplugins.pro | 15 + .../qgeoserviceproviderplugin_test.cpp | 47 + .../qgeoserviceproviderplugin_test.h | 55 + tests/auto/qgeocoordinate/qgeocoordinate.pro | 8 + .../qgeocoordinate/tst_qgeocoordinate.cpp | 952 ++++ tests/auto/qgeolocation/qgeolocation.pro | 9 + tests/auto/qgeolocation/tst_qgeolocation.cpp | 249 + tests/auto/qgeolocation/tst_qgeolocation.h | 84 + tests/auto/qgeomaneuver/qgeomaneuver.pro | 11 + tests/auto/qgeomaneuver/tst_qgeomaneuver.cpp | 301 ++ tests/auto/qgeomaneuver/tst_qgeomaneuver.h | 85 + tests/auto/qgeopath/qgeopath.pro | 8 + tests/auto/qgeopath/tst_qgeopath.cpp | 413 ++ tests/auto/qgeopolygon/qgeopolygon.pro | 8 + tests/auto/qgeopolygon/tst_qgeopolygon.cpp | 402 ++ .../qgeopositioninfo/qgeopositioninfo.pro | 7 + .../qgeopositioninfo/tst_qgeopositioninfo.cpp | 385 ++ .../qgeopositioninfosource.pro | 15 + .../testqgeopositioninfosource.cpp | 778 +++ .../testqgeopositioninfosource_p.h | 127 + .../tst_qgeopositioninfosource.cpp | 38 + tests/auto/qgeorectangle/qgeorectangle.pro | 8 + .../auto/qgeorectangle/tst_qgeorectangle.cpp | 2376 ++++++++ tests/auto/qgeoroute/qgeoroute.pro | 9 + tests/auto/qgeoroute/tst_qgeoroute.cpp | 306 ++ tests/auto/qgeoroute/tst_qgeoroute.h | 90 + tests/auto/qgeoroutereply/qgeoroutereply.pro | 9 + .../qgeoroutereply/tst_qgeoroutereply.cpp | 242 + .../auto/qgeoroutereply/tst_qgeoroutereply.h | 96 + .../qgeorouterequest/qgeorouterequest.pro | 9 + .../qgeorouterequest/tst_qgeorouterequest.cpp | 351 ++ .../qgeorouterequest/tst_qgeorouterequest.h | 95 + .../qgeoroutesegment/qgeoroutesegment.pro | 9 + .../qgeoroutesegment/tst_qgeoroutesegment.cpp | 302 ++ .../qgeoroutesegment/tst_qgeoroutesegment.h | 82 + tests/auto/qgeoroutexmlparser/fixtures.qrc | 6 + .../qgeoroutexmlparser/qgeoroutexmlparser.pro | 13 + tests/auto/qgeoroutexmlparser/route1.xml | 1 + tests/auto/qgeoroutexmlparser/route2.xml | 1 + .../tst_qgeoroutexmlparser.cpp | 172 + .../qgeoroutingmanager/qgeoroutingmanager.pro | 11 + .../tst_qgeoroutingmanager.cpp | 165 + .../tst_qgeoroutingmanager.h | 74 + .../qgeoroutingmanagerengine_test.h | 76 + .../qgeoroutingmanagerplugins.pro | 15 + .../qgeoserviceproviderplugin_test.cpp | 47 + .../qgeoserviceproviderplugin_test.h | 55 + .../routing_plugin.json | 12 + .../qgeosatelliteinfo/qgeosatelliteinfo.pro | 7 + .../tst_qgeosatelliteinfo.cpp | 405 ++ .../qgeosatelliteinfosource.pro | 12 + .../testqgeosatelliteinfosource.cpp | 767 +++ .../testqgeosatelliteinfosource_p.h | 114 + .../tst_qgeosatelliteinfosource.cpp | 37 + .../qgeoserviceprovider.pro | 9 + .../tst_qgeoserviceprovider.cpp | 232 + tests/auto/qgeoshape/qgeoshape.pro | 5 + tests/auto/qgeoshape/tst_qgeoshape.cpp | 130 + tests/auto/qgeotiledmap/BLACKLIST | 2 + tests/auto/qgeotiledmap/qgeotiledmap.pro | 9 + tests/auto/qgeotiledmap/tst_qgeotiledmap.cpp | 212 + .../qgeotiledmapscene/qgeotiledmapscene.pro | 8 + .../tst_qgeotiledmapscene.cpp | 400 ++ tests/auto/qgeotilespec/qgeotilespec.pro | 9 + tests/auto/qgeotilespec/tst_qgeotilespec.cpp | 319 ++ tests/auto/qmlinterface/data/TestAddress.qml | 38 + tests/auto/qmlinterface/data/TestCategory.qml | 34 + .../qmlinterface/data/TestContactDetail.qml | 34 + tests/auto/qmlinterface/data/TestIcon.qml | 36 + tests/auto/qmlinterface/data/TestLocation.qml | 49 + tests/auto/qmlinterface/data/TestPlace.qml | 50 + .../qmlinterface/data/TestPlaceAttribute.qml | 34 + tests/auto/qmlinterface/data/TestRatings.qml | 35 + tests/auto/qmlinterface/data/TestSupplier.qml | 36 + tests/auto/qmlinterface/data/TestUser.qml | 34 + tests/auto/qmlinterface/qmlinterface.pro | 26 + tests/auto/qmlinterface/tst_qmlinterface.cpp | 353 ++ .../dummynmeapositioninfosource.pro | 23 + .../tst_dummynmeapositioninfosource.cpp | 148 + .../qnmeapositioninfosource.pro | 8 + .../qnmeapositioninfosource_realtime.pro | 25 + .../tst_qnmeapositioninfosource_realtime.cpp | 44 + ...meapositioninfosource_realtime_generic.pro | 28 + ...meapositioninfosource_realtime_generic.cpp | 76 + .../qnmeapositioninfosource_simulation.pro | 25 + ...tst_qnmeapositioninfosource_simulation.cpp | 43 + ...apositioninfosource_simulation_generic.pro | 28 + ...apositioninfosource_simulation_generic.cpp | 64 + .../qnmeapositioninfosourceproxyfactory.cpp | 95 + .../qnmeapositioninfosourceproxyfactory.h | 75 + .../tst_qnmeapositioninfosource.cpp | 580 ++ .../tst_qnmeapositioninfosource.h | 182 + tests/auto/qplace/qplace.pro | 7 + tests/auto/qplace/tst_qplace.cpp | 681 +++ .../auto/qplaceattribute/qplaceattribute.pro | 7 + .../qplaceattribute/tst_qplaceattribute.cpp | 95 + tests/auto/qplacecategory/qplacecategory.pro | 7 + .../qplacecategory/tst_qplacecategory.cpp | 142 + .../qplacecontactdetail.pro | 7 + .../tst_qplacecontactdetail.cpp | 146 + .../qplacecontentrequest.pro | 6 + .../tst_qplacecontentrequest.cpp | 90 + .../qplacedetailsreply/qplacedetailsreply.pro | 7 + .../tst_qplacedetailsreply.cpp | 89 + .../auto/qplaceeditorial/qplaceeditorial.pro | 7 + .../qplaceeditorial/tst_qplaceeditorial.cpp | 166 + tests/auto/qplaceimage/qplaceimage.pro | 7 + tests/auto/qplaceimage/tst_qplaceimage.cpp | 168 + tests/auto/qplacemanager/qplacemanager.pro | 9 + .../auto/qplacemanager/tst_qplacemanager.cpp | 236 + .../qplacemanager_nokia.pro | 7 + .../tst_qplacemanager_nokia.cpp | 200 + .../qplacemanager_unsupported.pro | 9 + .../tst_qplacemanager_unsupported.cpp | 279 + .../qplacematchreply/qplacematchreply.pro | 7 + .../qplacematchreply/tst_qplacematchreply.cpp | 111 + .../qplacematchrequest/qplacematchrequest.pro | 6 + .../tst_qplacematchrequest.cpp | 166 + tests/auto/qplaceperiod/qplaceperiod.pro | 8 + tests/auto/qplaceperiod/tst_qplaceperiod.cpp | 116 + tests/auto/qplaceratings/qplaceratings.pro | 7 + .../auto/qplaceratings/tst_qplaceratings.cpp | 119 + tests/auto/qplacereply/qplacereply.pro | 7 + tests/auto/qplacereply/tst_qplacereply.cpp | 109 + tests/auto/qplaceresult/qplaceresult.pro | 7 + tests/auto/qplaceresult/tst_qplaceresult.cpp | 270 + tests/auto/qplacereview/qplacereview.pro | 7 + tests/auto/qplacereview/tst_qplacereview.cpp | 220 + .../qplacesearchreply/qplacesearchreply.pro | 7 + .../tst_qplacesearchreply.cpp | 135 + .../qplacesearchrequest.pro | 6 + .../tst_qplacesearchrequest.cpp | 268 + .../qplacesearchresult/qplacesearchresult.pro | 7 + .../tst_qplacesearchresult.cpp | 112 + .../qplacesearchsuggestionreply.pro | 7 + .../tst_qplacesearchsuggestionreply.cpp | 95 + tests/auto/qplacesupplier/qplacesupplier.pro | 7 + .../qplacesupplier/tst_qplacesupplier.cpp | 167 + tests/auto/qplaceuser/qplaceuser.pro | 7 + tests/auto/qplaceuser/tst_qplaceuser.cpp | 139 + .../qproposedsearchresult.pro | 7 + .../tst_qproposedsearchresult.cpp | 221 + tests/auto/utils/qlocationtestutils.cpp | 87 + tests/auto/utils/qlocationtestutils_p.h | 170 + tests/global/global.cfg | 5 + .../declarativetestplugin.pro | 27 + .../declarativetestplugin/locationtest.cpp | 68 + .../qdeclarativelocationtestmodel.cpp | 246 + .../qdeclarativelocationtestmodel_p.h | 130 + .../qdeclarativepinchgenerator.cpp | 383 ++ .../qdeclarativepinchgenerator_p.h | 139 + tests/plugins/declarativetestplugin/qmldir | 3 + .../declarativetestplugin/testhelper.h | 62 + tests/plugins/imports.pri | 5 + tests/tests.pro | 3 + 1688 files changed, 241541 insertions(+) create mode 100644 .QT-ENTERPRISE-LICENSE-AGREEMENT create mode 100644 .QT-FOR-APPLICATION-DEVELOPMENT-LICENSE-AGREEMENT create mode 100644 .QT-FOR-AUTOMATION-LICENSE-AGREEMENT create mode 100644 .QT-FOR-AUTOMOTIVE-LICENSE-AGREEMENT create mode 100644 .QT-FOR-DEVICE-CREATION-LICENSE-AGREEMENT create mode 100644 .gitmodules create mode 100644 .qmake.conf create mode 100644 .tag create mode 100644 LICENSE.FDL create mode 100644 LICENSE.GPL2 create mode 100644 LICENSE.GPL3 create mode 100644 LICENSE.GPL3-EXCEPT create mode 100644 LICENSE.GPLv3 create mode 100644 LICENSE.LGPL3 create mode 100644 LICENSE.LGPLv3 create mode 100644 config.tests/gypsy/gypsy.pro create mode 100644 config.tests/gypsy/main.cpp create mode 100644 config.tests/winrt/main.cpp create mode 100644 config.tests/winrt/winrt.pro create mode 100644 configure.json create mode 100644 dist/changes-5.10.0 create mode 100644 dist/changes-5.10.1 create mode 100644 dist/changes-5.11.0 create mode 100644 dist/changes-5.11.1 create mode 100644 dist/changes-5.11.2 create mode 100644 dist/changes-5.11.3 create mode 100644 dist/changes-5.2.1 create mode 100644 dist/changes-5.3.0 create mode 100644 dist/changes-5.3.1 create mode 100644 dist/changes-5.3.2 create mode 100644 dist/changes-5.4.0 create mode 100644 dist/changes-5.4.1 create mode 100644 dist/changes-5.4.2 create mode 100644 dist/changes-5.5.0 create mode 100644 dist/changes-5.5.1 create mode 100644 dist/changes-5.6.0 create mode 100644 dist/changes-5.6.1 create mode 100644 dist/changes-5.6.2 create mode 100644 dist/changes-5.6.3 create mode 100644 dist/changes-5.7.0 create mode 100644 dist/changes-5.7.1 create mode 100644 dist/changes-5.8.0 create mode 100644 dist/changes-5.9.0 create mode 100644 dist/changes-5.9.1 create mode 100644 dist/changes-5.9.2 create mode 100644 dist/changes-5.9.3 create mode 100644 dist/changes-5.9.4 create mode 100644 examples/examples.pro create mode 100644 examples/location/location.pro create mode 100644 examples/location/mapviewer/doc/images/mapviewer.png create mode 100644 examples/location/mapviewer/doc/src/mapviewer.qdoc create mode 100644 examples/location/mapviewer/forms/Geocode.qml create mode 100644 examples/location/mapviewer/forms/GeocodeForm.ui.qml create mode 100644 examples/location/mapviewer/forms/Locale.qml create mode 100644 examples/location/mapviewer/forms/LocaleForm.ui.qml create mode 100644 examples/location/mapviewer/forms/Message.qml create mode 100644 examples/location/mapviewer/forms/MessageForm.ui.qml create mode 100644 examples/location/mapviewer/forms/ReverseGeocode.qml create mode 100644 examples/location/mapviewer/forms/ReverseGeocodeForm.ui.qml create mode 100644 examples/location/mapviewer/forms/RouteAddress.qml create mode 100644 examples/location/mapviewer/forms/RouteAddressForm.ui.qml create mode 100644 examples/location/mapviewer/forms/RouteCoordinate.qml create mode 100644 examples/location/mapviewer/forms/RouteCoordinateForm.ui.qml create mode 100644 examples/location/mapviewer/forms/RouteList.qml create mode 100644 examples/location/mapviewer/forms/RouteListDelegate.qml create mode 100644 examples/location/mapviewer/forms/RouteListHeader.qml create mode 100644 examples/location/mapviewer/helper.js create mode 100644 examples/location/mapviewer/main.cpp create mode 100644 examples/location/mapviewer/map/CircleItem.qml create mode 100644 examples/location/mapviewer/map/ImageItem.qml create mode 100644 examples/location/mapviewer/map/MapComponent.qml create mode 100644 examples/location/mapviewer/map/MapSliders.qml create mode 100644 examples/location/mapviewer/map/Marker.qml create mode 100644 examples/location/mapviewer/map/MiniMap.qml create mode 100644 examples/location/mapviewer/map/PolygonItem.qml create mode 100644 examples/location/mapviewer/map/PolylineItem.qml create mode 100644 examples/location/mapviewer/map/RectangleItem.qml create mode 100644 examples/location/mapviewer/mapviewer.pro create mode 100644 examples/location/mapviewer/mapviewer.qml create mode 100644 examples/location/mapviewer/mapviewer.qrc create mode 100644 examples/location/mapviewer/menus/ItemPopupMenu.qml create mode 100644 examples/location/mapviewer/menus/MainMenu.qml create mode 100644 examples/location/mapviewer/menus/MapPopupMenu.qml create mode 100644 examples/location/mapviewer/menus/MarkerPopupMenu.qml create mode 100644 examples/location/mapviewer/resources/icon.png create mode 100644 examples/location/mapviewer/resources/marker.png create mode 100644 examples/location/mapviewer/resources/scale.png create mode 100644 examples/location/mapviewer/resources/scale_end.png create mode 100644 examples/location/minimal_map/doc/images/minimal_map.png create mode 100644 examples/location/minimal_map/doc/src/minimal_map.qdoc create mode 100644 examples/location/minimal_map/main.cpp create mode 100644 examples/location/minimal_map/main.qml create mode 100644 examples/location/minimal_map/minimal_map.pro create mode 100644 examples/location/minimal_map/qml.qrc create mode 100644 examples/location/places/doc/images/places.png create mode 100644 examples/location/places/doc/src/places.qdoc create mode 100644 examples/location/places/forms/Message.qml create mode 100644 examples/location/places/forms/MessageForm.ui.qml create mode 100644 examples/location/places/forms/PlaceDetails.qml create mode 100644 examples/location/places/forms/PlaceDetailsForm.ui.qml create mode 100644 examples/location/places/forms/SearchBoundingBox.qml create mode 100644 examples/location/places/forms/SearchBoundingBoxForm.ui.qml create mode 100644 examples/location/places/forms/SearchBoundingCircle.qml create mode 100644 examples/location/places/forms/SearchBoundingCircleForm.ui.qml create mode 100644 examples/location/places/forms/SearchCenter.qml create mode 100644 examples/location/places/forms/SearchCenterForm.ui.qml create mode 100644 examples/location/places/forms/SearchOptions.qml create mode 100644 examples/location/places/forms/SearchOptionsForm.ui.qml create mode 100644 examples/location/places/helper.js create mode 100644 examples/location/places/items/MainMenu.qml create mode 100644 examples/location/places/items/MapComponent.qml create mode 100644 examples/location/places/items/SearchBar.qml create mode 100644 examples/location/places/main.cpp create mode 100644 examples/location/places/places.pro create mode 100644 examples/location/places/places.qml create mode 100644 examples/location/places/places.qrc create mode 100644 examples/location/places/resources/categories.png create mode 100644 examples/location/places/resources/left.png create mode 100644 examples/location/places/resources/marker.png create mode 100644 examples/location/places/resources/right.png create mode 100644 examples/location/places/resources/scale.png create mode 100644 examples/location/places/resources/scale_end.png create mode 100644 examples/location/places/resources/search.png create mode 100644 examples/location/places/resources/star.png create mode 100644 examples/location/places/views/CategoryDelegate.qml create mode 100644 examples/location/places/views/CategoryView.qml create mode 100644 examples/location/places/views/EditorialDelegate.qml create mode 100644 examples/location/places/views/EditorialPage.qml create mode 100644 examples/location/places/views/EditorialView.qml create mode 100644 examples/location/places/views/ImageView.qml create mode 100644 examples/location/places/views/RatingView.qml create mode 100644 examples/location/places/views/ReviewDelegate.qml create mode 100644 examples/location/places/views/ReviewPage.qml create mode 100644 examples/location/places/views/ReviewView.qml create mode 100644 examples/location/places/views/SearchResultDelegate.qml create mode 100644 examples/location/places/views/SearchResultView.qml create mode 100644 examples/location/places/views/SuggestionView.qml create mode 100644 examples/location/places_list/Marker.qml create mode 100644 examples/location/places_list/doc/images/places_list.png create mode 100644 examples/location/places_list/doc/src/places_list.qdoc create mode 100644 examples/location/places_list/main.cpp create mode 100644 examples/location/places_list/marker.png create mode 100644 examples/location/places_list/places_list.pro create mode 100644 examples/location/places_list/places_list.qml create mode 100644 examples/location/places_list/places_list.qrc create mode 100644 examples/location/places_map/doc/images/places_map.png create mode 100644 examples/location/places_map/doc/src/places_map.qdoc create mode 100644 examples/location/places_map/main.cpp create mode 100644 examples/location/places_map/marker.png create mode 100644 examples/location/places_map/places_map.pro create mode 100644 examples/location/places_map/places_map.qml create mode 100644 examples/location/places_map/places_map.qrc create mode 100644 examples/location/planespotter/Plane.qml create mode 100644 examples/location/planespotter/airplane.png create mode 100644 examples/location/planespotter/doc/images/planespotter.png create mode 100644 examples/location/planespotter/doc/src/planespotter.qdoc create mode 100644 examples/location/planespotter/main.cpp create mode 100644 examples/location/planespotter/planespotter.pro create mode 100644 examples/location/planespotter/planespotter.qml create mode 100644 examples/location/planespotter/qml.qrc create mode 100644 examples/positioning/geoflickr/doc/images/qml-flickr-1.jpg create mode 100644 examples/positioning/geoflickr/doc/src/geoflickr.qdoc create mode 100644 examples/positioning/geoflickr/flickr-90.qml create mode 100644 examples/positioning/geoflickr/flickr.qml create mode 100644 examples/positioning/geoflickr/flickr.qrc create mode 100644 examples/positioning/geoflickr/flickrcommon/Progress.qml create mode 100644 examples/positioning/geoflickr/flickrcommon/RestModel.qml create mode 100644 examples/positioning/geoflickr/flickrcommon/ScrollBar.qml create mode 100644 examples/positioning/geoflickr/flickrcommon/Slider.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/Button.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/GeoTab.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/GridDelegate.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/ImageDetails.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/ListDelegate.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/TitleBar.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/ToolBar.qml create mode 100644 examples/positioning/geoflickr/flickrmobile/images/gloss.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/lineedit.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/lineedit.sci create mode 100644 examples/positioning/geoflickr/flickrmobile/images/moon.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/quit.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/star.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/stripes.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/sun.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/titlebar.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/titlebar.sci create mode 100644 examples/positioning/geoflickr/flickrmobile/images/toolbutton.png create mode 100644 examples/positioning/geoflickr/flickrmobile/images/toolbutton.sci create mode 100644 examples/positioning/geoflickr/flickrmobile/nmealog.txt create mode 100644 examples/positioning/geoflickr/geoflickr.pro create mode 100644 examples/positioning/geoflickr/geoflickr.qmlproject create mode 100644 examples/positioning/geoflickr/qmllocationflickr.cpp create mode 100644 examples/positioning/logfilepositionsource/clientapplication.cpp create mode 100644 examples/positioning/logfilepositionsource/clientapplication.h create mode 100644 examples/positioning/logfilepositionsource/doc/src/logfilepositionsource.qdoc create mode 100644 examples/positioning/logfilepositionsource/logfile.qrc create mode 100644 examples/positioning/logfilepositionsource/logfilepositionsource.cpp create mode 100644 examples/positioning/logfilepositionsource/logfilepositionsource.h create mode 100644 examples/positioning/logfilepositionsource/logfilepositionsource.pro create mode 100644 examples/positioning/logfilepositionsource/main.cpp create mode 100644 examples/positioning/logfilepositionsource/simplelog.txt create mode 100644 examples/positioning/positioning.pro create mode 100644 examples/positioning/satelliteinfo/doc/images/example-satelliteinfo.png create mode 100644 examples/positioning/satelliteinfo/doc/src/satelliteinfo.qdoc create mode 100644 examples/positioning/satelliteinfo/main.cpp create mode 100644 examples/positioning/satelliteinfo/satelliteinfo.pro create mode 100644 examples/positioning/satelliteinfo/satelliteinfo.qml create mode 100644 examples/positioning/satelliteinfo/satelliteinfo.qrc create mode 100644 examples/positioning/satelliteinfo/satellitemodel.cpp create mode 100644 examples/positioning/satelliteinfo/satellitemodel.h create mode 100644 examples/positioning/weatherinfo/appmodel.cpp create mode 100644 examples/positioning/weatherinfo/appmodel.h create mode 100644 examples/positioning/weatherinfo/components/BigForecastIcon.qml create mode 100644 examples/positioning/weatherinfo/components/ForecastIcon.qml create mode 100644 examples/positioning/weatherinfo/components/WeatherIcon.qml create mode 100644 examples/positioning/weatherinfo/doc/images/example-weatherinfo.png create mode 100644 examples/positioning/weatherinfo/doc/src/weatherinfo.qdoc create mode 100644 examples/positioning/weatherinfo/icons/README.txt create mode 100644 examples/positioning/weatherinfo/icons/weather-few-clouds.png create mode 100644 examples/positioning/weatherinfo/icons/weather-fog.png create mode 100644 examples/positioning/weatherinfo/icons/weather-haze.png create mode 100644 examples/positioning/weatherinfo/icons/weather-icy.png create mode 100644 examples/positioning/weatherinfo/icons/weather-overcast.png create mode 100644 examples/positioning/weatherinfo/icons/weather-showers.png create mode 100644 examples/positioning/weatherinfo/icons/weather-sleet.png create mode 100644 examples/positioning/weatherinfo/icons/weather-snow.png create mode 100644 examples/positioning/weatherinfo/icons/weather-storm.png create mode 100644 examples/positioning/weatherinfo/icons/weather-sunny-very-few-clouds.png create mode 100644 examples/positioning/weatherinfo/icons/weather-sunny.png create mode 100644 examples/positioning/weatherinfo/icons/weather-thundershower.png create mode 100644 examples/positioning/weatherinfo/main.cpp create mode 100644 examples/positioning/weatherinfo/weatherinfo.pro create mode 100644 examples/positioning/weatherinfo/weatherinfo.qml create mode 100644 examples/positioning/weatherinfo/weatherinfo.qrc create mode 100644 include/QtLocation/5.11.3/QtLocation/private/error_messages_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/locationvaluetypehelper_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/mapitemviewdelegateincubator_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qabstractgeotilecache_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qcache3q_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativecategory_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativecirclemapitem_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativecontactdetail_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeocodemodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaneuver_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomap_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapcopyrightsnotice_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitembase_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemgroup_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemview_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapparameter_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapquickitem_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaptype_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroute_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutemodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutesegment_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoserviceprovider_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeperiod_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplace_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceattribute_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplacecontentmodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceeditorialmodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceicon_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceimagemodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceuser_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolygonmapitem_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolylinemapitem_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeratings_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativerectanglemapitem_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativereviewmodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativeroutemapitem_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchmodelbase_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchresultmodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchsuggestionmodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupplier_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupportedcategoriesmodel_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeocameracapabilities_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeocameradata_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeocameratiles_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeocodereply_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanager_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanagerengine_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeofiletilecache_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomaneuver_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomap_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomap_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomapitemgeometry_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomapobjectqsgsupport_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomapparameter_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeoprojection_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeoroute_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv4_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv5_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeoroutereply_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeorouterequest_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeoroutesegment_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanager_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanagerengine_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeoserviceprovider_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmaplabs_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapscene_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotilerequestmanager_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qlocationglobal_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapcircleobjectqsg_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapiconobjectqsg_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmappolygonobjectqsg_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmappolylineobjectqsg_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qmaprouteobjectqsg_p_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qnavigationmanager_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qnavigationmanagerengine_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qparameterizableobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplace_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceattribute_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacecategory_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacecontactdetail_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacecontent_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacecontentrequest_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceeditorial_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceicon_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceimage_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacemanagerengine_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceproposedsearchresult_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceratings_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacereply_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceresult_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacereview_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacesearchresult_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplacesupplier_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qplaceuser_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qqsgmapobject_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/qquickgeomapgesturearea_p.h create mode 100644 include/QtLocation/5.11.3/QtLocation/private/unsupportedreplies_p.h create mode 100644 include/QtLocation/QGeoCodeReply create mode 100644 include/QtLocation/QGeoCodingManager create mode 100644 include/QtLocation/QGeoCodingManagerEngine create mode 100644 include/QtLocation/QGeoManeuver create mode 100644 include/QtLocation/QGeoRoute create mode 100644 include/QtLocation/QGeoRouteReply create mode 100644 include/QtLocation/QGeoRouteRequest create mode 100644 include/QtLocation/QGeoRouteSegment create mode 100644 include/QtLocation/QGeoRoutingManager create mode 100644 include/QtLocation/QGeoRoutingManagerEngine create mode 100644 include/QtLocation/QGeoServiceProvider create mode 100644 include/QtLocation/QGeoServiceProviderFactory create mode 100644 include/QtLocation/QLocation create mode 100644 include/QtLocation/QPlace create mode 100644 include/QtLocation/QPlaceAttribute create mode 100644 include/QtLocation/QPlaceCategory create mode 100644 include/QtLocation/QPlaceContactDetail create mode 100644 include/QtLocation/QPlaceContent create mode 100644 include/QtLocation/QPlaceContentReply create mode 100644 include/QtLocation/QPlaceContentRequest create mode 100644 include/QtLocation/QPlaceDetailsReply create mode 100644 include/QtLocation/QPlaceEditorial create mode 100644 include/QtLocation/QPlaceIcon create mode 100644 include/QtLocation/QPlaceIdReply create mode 100644 include/QtLocation/QPlaceImage create mode 100644 include/QtLocation/QPlaceManager create mode 100644 include/QtLocation/QPlaceManagerEngine create mode 100644 include/QtLocation/QPlaceMatchReply create mode 100644 include/QtLocation/QPlaceMatchRequest create mode 100644 include/QtLocation/QPlaceProposedSearchResult create mode 100644 include/QtLocation/QPlaceRatings create mode 100644 include/QtLocation/QPlaceReply create mode 100644 include/QtLocation/QPlaceResult create mode 100644 include/QtLocation/QPlaceReview create mode 100644 include/QtLocation/QPlaceSearchReply create mode 100644 include/QtLocation/QPlaceSearchRequest create mode 100644 include/QtLocation/QPlaceSearchResult create mode 100644 include/QtLocation/QPlaceSearchSuggestionReply create mode 100644 include/QtLocation/QPlaceSupplier create mode 100644 include/QtLocation/QPlaceUser create mode 100644 include/QtLocation/QtLocation create mode 100644 include/QtLocation/QtLocationVersion create mode 100644 include/QtLocation/headers.pri create mode 100644 include/QtLocation/placemacro.h create mode 100644 include/QtLocation/qgeocodereply.h create mode 100644 include/QtLocation/qgeocodingmanager.h create mode 100644 include/QtLocation/qgeocodingmanagerengine.h create mode 100644 include/QtLocation/qgeomaneuver.h create mode 100644 include/QtLocation/qgeoroute.h create mode 100644 include/QtLocation/qgeoroutereply.h create mode 100644 include/QtLocation/qgeorouterequest.h create mode 100644 include/QtLocation/qgeoroutesegment.h create mode 100644 include/QtLocation/qgeoroutingmanager.h create mode 100644 include/QtLocation/qgeoroutingmanagerengine.h create mode 100644 include/QtLocation/qgeoserviceprovider.h create mode 100644 include/QtLocation/qgeoserviceproviderfactory.h create mode 100644 include/QtLocation/qlocation.h create mode 100644 include/QtLocation/qlocationglobal.h create mode 100644 include/QtLocation/qplace.h create mode 100644 include/QtLocation/qplaceattribute.h create mode 100644 include/QtLocation/qplacecategory.h create mode 100644 include/QtLocation/qplacecontactdetail.h create mode 100644 include/QtLocation/qplacecontent.h create mode 100644 include/QtLocation/qplacecontentreply.h create mode 100644 include/QtLocation/qplacecontentrequest.h create mode 100644 include/QtLocation/qplacedetailsreply.h create mode 100644 include/QtLocation/qplaceeditorial.h create mode 100644 include/QtLocation/qplaceicon.h create mode 100644 include/QtLocation/qplaceidreply.h create mode 100644 include/QtLocation/qplaceimage.h create mode 100644 include/QtLocation/qplacemanager.h create mode 100644 include/QtLocation/qplacemanagerengine.h create mode 100644 include/QtLocation/qplacematchreply.h create mode 100644 include/QtLocation/qplacematchrequest.h create mode 100644 include/QtLocation/qplaceproposedsearchresult.h create mode 100644 include/QtLocation/qplaceratings.h create mode 100644 include/QtLocation/qplacereply.h create mode 100644 include/QtLocation/qplaceresult.h create mode 100644 include/QtLocation/qplacereview.h create mode 100644 include/QtLocation/qplacesearchreply.h create mode 100644 include/QtLocation/qplacesearchrequest.h create mode 100644 include/QtLocation/qplacesearchresult.h create mode 100644 include/QtLocation/qplacesearchsuggestionreply.h create mode 100644 include/QtLocation/qplacesupplier.h create mode 100644 include/QtLocation/qplaceuser.h create mode 100644 include/QtLocation/qtlocationversion.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qclipperutils_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeoaddress_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeolocation_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qdoublematrix4x4_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector2d_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector3d_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeoaddress_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeocircle_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinate_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinateobject_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeolocation_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeopath_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfo_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfosource_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeorectangle_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qgeoshape_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qlocationdata_simulator_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qlocationutils_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qnmeapositioninfosource_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qpositioningglobal_p.h create mode 100644 include/QtPositioning/5.11.3/QtPositioning/private/qwebmercator_p.h create mode 100644 include/QtPositioning/QGeoAddress create mode 100644 include/QtPositioning/QGeoAreaMonitorInfo create mode 100644 include/QtPositioning/QGeoAreaMonitorSource create mode 100644 include/QtPositioning/QGeoCircle create mode 100644 include/QtPositioning/QGeoCoordinate create mode 100644 include/QtPositioning/QGeoLocation create mode 100644 include/QtPositioning/QGeoPath create mode 100644 include/QtPositioning/QGeoPolygon create mode 100644 include/QtPositioning/QGeoPolygonPrivate create mode 100644 include/QtPositioning/QGeoPositionInfo create mode 100644 include/QtPositioning/QGeoPositionInfoSource create mode 100644 include/QtPositioning/QGeoPositionInfoSourceFactory create mode 100644 include/QtPositioning/QGeoRectangle create mode 100644 include/QtPositioning/QGeoSatelliteInfo create mode 100644 include/QtPositioning/QGeoSatelliteInfoSource create mode 100644 include/QtPositioning/QGeoShape create mode 100644 include/QtPositioning/QNmeaPositionInfoSource create mode 100644 include/QtPositioning/QtPositioning create mode 100644 include/QtPositioning/QtPositioningVersion create mode 100644 include/QtPositioning/headers.pri create mode 100644 include/QtPositioning/qgeoaddress.h create mode 100644 include/QtPositioning/qgeoareamonitorinfo.h create mode 100644 include/QtPositioning/qgeoareamonitorsource.h create mode 100644 include/QtPositioning/qgeocircle.h create mode 100644 include/QtPositioning/qgeocoordinate.h create mode 100644 include/QtPositioning/qgeolocation.h create mode 100644 include/QtPositioning/qgeopath.h create mode 100644 include/QtPositioning/qgeopolygon.h create mode 100644 include/QtPositioning/qgeopositioninfo.h create mode 100644 include/QtPositioning/qgeopositioninfosource.h create mode 100644 include/QtPositioning/qgeopositioninfosourcefactory.h create mode 100644 include/QtPositioning/qgeorectangle.h create mode 100644 include/QtPositioning/qgeosatelliteinfo.h create mode 100644 include/QtPositioning/qgeosatelliteinfosource.h create mode 100644 include/QtPositioning/qgeoshape.h create mode 100644 include/QtPositioning/qnmeapositioninfosource.h create mode 100644 include/QtPositioning/qpositioningglobal.h create mode 100644 include/QtPositioning/qtpositioningversion.h create mode 100644 include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativeposition_p.h create mode 100644 include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativepositionsource_p.h create mode 100644 include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qpositioningquickglobal_p.h create mode 100644 include/QtPositioningQuick/QtPositioningQuick create mode 100644 include/QtPositioningQuick/QtPositioningQuickVersion create mode 100644 include/QtPositioningQuick/headers.pri create mode 100644 include/QtPositioningQuick/qpositioningquickglobal.h create mode 100644 include/QtPositioningQuick/qtpositioningquickversion.h create mode 100644 qtlocation.pro create mode 100644 src/3rdparty/clip2tri/LICENSE create mode 100644 src/3rdparty/clip2tri/clip2tri.cpp create mode 100644 src/3rdparty/clip2tri/clip2tri.h create mode 100644 src/3rdparty/clip2tri/clip2tri.pro create mode 100644 src/3rdparty/clip2tri/qt_attribution.json create mode 100644 src/3rdparty/clipper/LICENSE create mode 100644 src/3rdparty/clipper/clipper.cpp create mode 100644 src/3rdparty/clipper/clipper.h create mode 100644 src/3rdparty/clipper/clipper.pro create mode 100644 src/3rdparty/clipper/qt_attribution.json create mode 100644 src/3rdparty/earcut/LICENSE create mode 100644 src/3rdparty/earcut/earcut.hpp create mode 100644 src/3rdparty/earcut/qt_attribution.json create mode 100644 src/3rdparty/poly2tri/AUTHORS create mode 100644 src/3rdparty/poly2tri/LICENSE create mode 100644 src/3rdparty/poly2tri/common/shapes.cpp create mode 100644 src/3rdparty/poly2tri/common/shapes.h create mode 100644 src/3rdparty/poly2tri/common/utils.h create mode 100644 src/3rdparty/poly2tri/poly2tri.h create mode 100644 src/3rdparty/poly2tri/poly2tri.pro create mode 100644 src/3rdparty/poly2tri/qt_attribution.json create mode 100644 src/3rdparty/poly2tri/sweep/advancing_front.cpp create mode 100644 src/3rdparty/poly2tri/sweep/advancing_front.h create mode 100644 src/3rdparty/poly2tri/sweep/cdt.cpp create mode 100644 src/3rdparty/poly2tri/sweep/cdt.h create mode 100644 src/3rdparty/poly2tri/sweep/sweep.cpp create mode 100644 src/3rdparty/poly2tri/sweep/sweep.h create mode 100644 src/3rdparty/poly2tri/sweep/sweep_context.cpp create mode 100644 src/3rdparty/poly2tri/sweep/sweep_context.h create mode 100644 src/3rdparty/zlib_dependency.pri create mode 100644 src/imports/imports.pro create mode 100644 src/imports/location/location.cpp create mode 100644 src/imports/location/location.pro create mode 100644 src/imports/location/plugin.json create mode 100644 src/imports/location/plugins.qmltypes create mode 100644 src/imports/location/qmldir create mode 100644 src/imports/locationlabs/locationlabs.cpp create mode 100644 src/imports/locationlabs/locationlabs.pro create mode 100644 src/imports/locationlabs/plugin.json create mode 100644 src/imports/locationlabs/plugins.qmltypes create mode 100644 src/imports/locationlabs/qmldir create mode 100644 src/imports/positioning/locationsingleton.cpp create mode 100644 src/imports/positioning/locationsingleton.h create mode 100644 src/imports/positioning/plugin.json create mode 100644 src/imports/positioning/plugins.qmltypes create mode 100644 src/imports/positioning/positioning.cpp create mode 100644 src/imports/positioning/positioning.pro create mode 100644 src/imports/positioning/qmldir create mode 100644 src/imports/positioning/qquickgeocoordinateanimation.cpp create mode 100644 src/imports/positioning/qquickgeocoordinateanimation_p.h create mode 100644 src/imports/positioning/qquickgeocoordinateanimation_p_p.h create mode 100644 src/location/configure.json create mode 100644 src/location/declarativemaps/declarativemaps.pri create mode 100644 src/location/declarativemaps/error_messages.cpp create mode 100644 src/location/declarativemaps/error_messages_p.h create mode 100644 src/location/declarativemaps/locationvaluetypehelper.cpp create mode 100644 src/location/declarativemaps/locationvaluetypehelper_p.h create mode 100644 src/location/declarativemaps/mapitemviewdelegateincubator_p.h create mode 100644 src/location/declarativemaps/qdeclarativecirclemapitem.cpp create mode 100644 src/location/declarativemaps/qdeclarativecirclemapitem_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeocodemodel.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeocodemodel_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomaneuver.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomaneuver_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomap.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomap_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomapitembase.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomapitembase_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomapitemgroup.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomapitemgroup_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomapitemview.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomapitemview_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomapparameter.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomapparameter_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomapquickitem.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomapquickitem_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeomaptype.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeomaptype_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeoroute.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeoroute_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeoroutemodel.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeoroutemodel_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeoroutesegment.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeoroutesegment_p.h create mode 100644 src/location/declarativemaps/qdeclarativegeoserviceprovider.cpp create mode 100644 src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h create mode 100644 src/location/declarativemaps/qdeclarativepolygonmapitem.cpp create mode 100644 src/location/declarativemaps/qdeclarativepolygonmapitem_p.h create mode 100644 src/location/declarativemaps/qdeclarativepolylinemapitem.cpp create mode 100644 src/location/declarativemaps/qdeclarativepolylinemapitem_p.h create mode 100644 src/location/declarativemaps/qdeclarativerectanglemapitem.cpp create mode 100644 src/location/declarativemaps/qdeclarativerectanglemapitem_p.h create mode 100644 src/location/declarativemaps/qdeclarativeroutemapitem.cpp create mode 100644 src/location/declarativemaps/qdeclarativeroutemapitem_p.h create mode 100644 src/location/declarativemaps/qgeomapitemgeometry.cpp create mode 100644 src/location/declarativemaps/qgeomapitemgeometry_p.h create mode 100644 src/location/declarativemaps/qgeomapobject.cpp create mode 100644 src/location/declarativemaps/qgeomapobject_p.h create mode 100644 src/location/declarativemaps/qgeomapobject_p_p.h create mode 100644 src/location/declarativemaps/qparameterizableobject.cpp create mode 100644 src/location/declarativemaps/qparameterizableobject_p.h create mode 100644 src/location/declarativemaps/qquickgeomapgesturearea.cpp create mode 100644 src/location/declarativemaps/qquickgeomapgesturearea_p.h create mode 100644 src/location/declarativeplaces/declarativeplaces.pri create mode 100644 src/location/declarativeplaces/qdeclarativecategory.cpp create mode 100644 src/location/declarativeplaces/qdeclarativecategory_p.h create mode 100644 src/location/declarativeplaces/qdeclarativecontactdetail.cpp create mode 100644 src/location/declarativeplaces/qdeclarativecontactdetail_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeperiod_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeplace.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeplace_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeplaceattribute.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeplaceattribute_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeplaceeditorialmodel.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeplaceicon.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeplaceicon_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeplaceimagemodel.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeplaceuser.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeplaceuser_p.h create mode 100644 src/location/declarativeplaces/qdeclarativeratings.cpp create mode 100644 src/location/declarativeplaces/qdeclarativeratings_p.h create mode 100644 src/location/declarativeplaces/qdeclarativereviewmodel.cpp create mode 100644 src/location/declarativeplaces/qdeclarativereviewmodel_p.h create mode 100644 src/location/declarativeplaces/qdeclarativesearchmodelbase.cpp create mode 100644 src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h create mode 100644 src/location/declarativeplaces/qdeclarativesearchresultmodel.cpp create mode 100644 src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h create mode 100644 src/location/declarativeplaces/qdeclarativesearchsuggestionmodel.cpp create mode 100644 src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h create mode 100644 src/location/declarativeplaces/qdeclarativesupplier.cpp create mode 100644 src/location/declarativeplaces/qdeclarativesupplier_p.h create mode 100644 src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp create mode 100644 src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h create mode 100644 src/location/doc/images/api-mapcircle.png create mode 100644 src/location/doc/images/api-mapitemgroup.png create mode 100644 src/location/doc/images/api-mappolygon.png create mode 100644 src/location/doc/images/api-mappolyline.png create mode 100644 src/location/doc/images/api-mapquickitem-anchor.png create mode 100644 src/location/doc/images/api-mapquickitem.png create mode 100644 src/location/doc/images/api-maprectangle.png create mode 100644 src/location/doc/images/mapsdemo-finished.png create mode 100644 src/location/doc/images/mapsdemo-routing.png create mode 100644 src/location/doc/images/mapsdemo-searchgui.png create mode 100644 src/location/doc/images/mapsdemo-verybasic.png create mode 100644 src/location/doc/qtlocation.qdocconf create mode 100644 src/location/doc/snippets/cpp/cpp.pro create mode 100644 src/location/doc/snippets/cpp/cppqml.cpp create mode 100644 src/location/doc/snippets/cpp/main.cpp create mode 100644 src/location/doc/snippets/declarative/content/Cell.qml create mode 100644 src/location/doc/snippets/declarative/declarative-location.qml create mode 100644 src/location/doc/snippets/declarative/declarative.pro create mode 100644 src/location/doc/snippets/declarative/maps.qml create mode 100644 src/location/doc/snippets/declarative/marker.png create mode 100644 src/location/doc/snippets/declarative/nmealog.txt create mode 100644 src/location/doc/snippets/declarative/places.qml create mode 100644 src/location/doc/snippets/declarative/places_loader.qml create mode 100644 src/location/doc/snippets/declarative/plugin.qml create mode 100644 src/location/doc/snippets/declarative/routing.qml create mode 100644 src/location/doc/snippets/places/main.cpp create mode 100644 src/location/doc/snippets/places/places.pro create mode 100644 src/location/doc/snippets/places/requesthandler.h create mode 100644 src/location/doc/snippets/snippets.pro create mode 100644 src/location/doc/src/cpp-qml.qdoc create mode 100644 src/location/doc/src/example-parameters.qdocinc create mode 100644 src/location/doc/src/maps.qdoc create mode 100644 src/location/doc/src/place-caveats.qdocinc create mode 100644 src/location/doc/src/place-crossref.qdocinc create mode 100644 src/location/doc/src/place-definition.qdocinc create mode 100644 src/location/doc/src/places.qdoc create mode 100644 src/location/doc/src/plugins/esri.qdoc create mode 100644 src/location/doc/src/plugins/itemsoverlay.qdoc create mode 100644 src/location/doc/src/plugins/mapbox.qdoc create mode 100644 src/location/doc/src/plugins/mapboxgl.qdoc create mode 100644 src/location/doc/src/plugins/nokia.qdoc create mode 100644 src/location/doc/src/plugins/osm.qdoc create mode 100644 src/location/doc/src/plugins/places-backend.qdoc create mode 100644 src/location/doc/src/qml-maps.qdoc create mode 100644 src/location/doc/src/qtlocation-changes.qdoc create mode 100644 src/location/doc/src/qtlocation-cpp.qdoc create mode 100644 src/location/doc/src/qtlocation-examples.qdoc create mode 100644 src/location/doc/src/qtlocation-geoservices.qdoc create mode 100644 src/location/doc/src/qtlocation-qml.qdoc create mode 100644 src/location/doc/src/qtlocation.qdoc create mode 100644 src/location/doc/src/src.pro create mode 100644 src/location/labs/labs.pri create mode 100644 src/location/labs/qdeclarativenavigator.cpp create mode 100644 src/location/labs/qdeclarativenavigator_p.h create mode 100644 src/location/labs/qdeclarativenavigator_p_p.h create mode 100644 src/location/labs/qgeotiledmaplabs.cpp create mode 100644 src/location/labs/qgeotiledmaplabs_p.h create mode 100644 src/location/labs/qmapcircleobject.cpp create mode 100644 src/location/labs/qmapcircleobject_p.h create mode 100644 src/location/labs/qmapcircleobject_p_p.h create mode 100644 src/location/labs/qmapiconobject.cpp create mode 100644 src/location/labs/qmapiconobject_p.h create mode 100644 src/location/labs/qmapiconobject_p_p.h create mode 100644 src/location/labs/qmapobjectview.cpp create mode 100644 src/location/labs/qmapobjectview_p.h create mode 100644 src/location/labs/qmapobjectview_p_p.h create mode 100644 src/location/labs/qmappolygonobject.cpp create mode 100644 src/location/labs/qmappolygonobject_p.h create mode 100644 src/location/labs/qmappolygonobject_p_p.h create mode 100644 src/location/labs/qmappolylineobject.cpp create mode 100644 src/location/labs/qmappolylineobject_p.h create mode 100644 src/location/labs/qmappolylineobject_p_p.h create mode 100644 src/location/labs/qmaprouteobject.cpp create mode 100644 src/location/labs/qmaprouteobject_p.h create mode 100644 src/location/labs/qmaprouteobject_p_p.h create mode 100644 src/location/labs/qsg/qgeomapobjectqsgsupport.cpp create mode 100644 src/location/labs/qsg/qgeomapobjectqsgsupport_p.h create mode 100644 src/location/labs/qsg/qmapcircleobjectqsg.cpp create mode 100644 src/location/labs/qsg/qmapcircleobjectqsg_p_p.h create mode 100644 src/location/labs/qsg/qmapiconobjectqsg.cpp create mode 100644 src/location/labs/qsg/qmapiconobjectqsg_p_p.h create mode 100644 src/location/labs/qsg/qmappolygonobjectqsg.cpp create mode 100644 src/location/labs/qsg/qmappolygonobjectqsg_p_p.h create mode 100644 src/location/labs/qsg/qmappolylineobjectqsg.cpp create mode 100644 src/location/labs/qsg/qmappolylineobjectqsg_p_p.h create mode 100644 src/location/labs/qsg/qmaprouteobjectqsg.cpp create mode 100644 src/location/labs/qsg/qmaprouteobjectqsg_p_p.h create mode 100644 src/location/labs/qsg/qqsgmapobject.cpp create mode 100644 src/location/labs/qsg/qqsgmapobject_p.h create mode 100644 src/location/location.pro create mode 100644 src/location/maps/maps.pri create mode 100644 src/location/maps/qabstractgeotilecache.cpp create mode 100644 src/location/maps/qabstractgeotilecache_p.h create mode 100644 src/location/maps/qcache3q_p.h create mode 100644 src/location/maps/qgeocameracapabilities.cpp create mode 100644 src/location/maps/qgeocameracapabilities_p.h create mode 100644 src/location/maps/qgeocameradata.cpp create mode 100644 src/location/maps/qgeocameradata_p.h create mode 100644 src/location/maps/qgeocameratiles.cpp create mode 100644 src/location/maps/qgeocameratiles_p.h create mode 100644 src/location/maps/qgeocodereply.cpp create mode 100644 src/location/maps/qgeocodereply.h create mode 100644 src/location/maps/qgeocodereply_p.h create mode 100644 src/location/maps/qgeocodingmanager.cpp create mode 100644 src/location/maps/qgeocodingmanager.h create mode 100644 src/location/maps/qgeocodingmanager_p.h create mode 100644 src/location/maps/qgeocodingmanagerengine.cpp create mode 100644 src/location/maps/qgeocodingmanagerengine.h create mode 100644 src/location/maps/qgeocodingmanagerengine_p.h create mode 100644 src/location/maps/qgeofiletilecache.cpp create mode 100644 src/location/maps/qgeofiletilecache_p.h create mode 100644 src/location/maps/qgeomaneuver.cpp create mode 100644 src/location/maps/qgeomaneuver.h create mode 100644 src/location/maps/qgeomaneuver_p.h create mode 100644 src/location/maps/qgeomap.cpp create mode 100644 src/location/maps/qgeomap_p.h create mode 100644 src/location/maps/qgeomap_p_p.h create mode 100644 src/location/maps/qgeomapparameter.cpp create mode 100644 src/location/maps/qgeomapparameter_p.h create mode 100644 src/location/maps/qgeomappingmanager.cpp create mode 100644 src/location/maps/qgeomappingmanager_p.h create mode 100644 src/location/maps/qgeomappingmanager_p_p.h create mode 100644 src/location/maps/qgeomappingmanagerengine.cpp create mode 100644 src/location/maps/qgeomappingmanagerengine_p.h create mode 100644 src/location/maps/qgeomappingmanagerengine_p_p.h create mode 100644 src/location/maps/qgeomaptype.cpp create mode 100644 src/location/maps/qgeomaptype_p.h create mode 100644 src/location/maps/qgeomaptype_p_p.h create mode 100644 src/location/maps/qgeoprojection.cpp create mode 100644 src/location/maps/qgeoprojection_p.h create mode 100644 src/location/maps/qgeoroute.cpp create mode 100644 src/location/maps/qgeoroute.h create mode 100644 src/location/maps/qgeoroute_p.h create mode 100644 src/location/maps/qgeorouteparser.cpp create mode 100644 src/location/maps/qgeorouteparser_p.h create mode 100644 src/location/maps/qgeorouteparser_p_p.h create mode 100644 src/location/maps/qgeorouteparserosrmv4.cpp create mode 100644 src/location/maps/qgeorouteparserosrmv4_p.h create mode 100644 src/location/maps/qgeorouteparserosrmv5.cpp create mode 100644 src/location/maps/qgeorouteparserosrmv5_p.h create mode 100644 src/location/maps/qgeoroutereply.cpp create mode 100644 src/location/maps/qgeoroutereply.h create mode 100644 src/location/maps/qgeoroutereply_p.h create mode 100644 src/location/maps/qgeorouterequest.cpp create mode 100644 src/location/maps/qgeorouterequest.h create mode 100644 src/location/maps/qgeorouterequest_p.h create mode 100644 src/location/maps/qgeoroutesegment.cpp create mode 100644 src/location/maps/qgeoroutesegment.h create mode 100644 src/location/maps/qgeoroutesegment_p.h create mode 100644 src/location/maps/qgeoroutingmanager.cpp create mode 100644 src/location/maps/qgeoroutingmanager.h create mode 100644 src/location/maps/qgeoroutingmanager_p.h create mode 100644 src/location/maps/qgeoroutingmanagerengine.cpp create mode 100644 src/location/maps/qgeoroutingmanagerengine.h create mode 100644 src/location/maps/qgeoroutingmanagerengine_p.h create mode 100644 src/location/maps/qgeoserviceprovider.cpp create mode 100644 src/location/maps/qgeoserviceprovider.h create mode 100644 src/location/maps/qgeoserviceprovider_p.h create mode 100644 src/location/maps/qgeoserviceproviderfactory.cpp create mode 100644 src/location/maps/qgeoserviceproviderfactory.h create mode 100644 src/location/maps/qgeotiledmap.cpp create mode 100644 src/location/maps/qgeotiledmap_p.h create mode 100644 src/location/maps/qgeotiledmap_p_p.h create mode 100644 src/location/maps/qgeotiledmappingmanagerengine.cpp create mode 100644 src/location/maps/qgeotiledmappingmanagerengine_p.h create mode 100644 src/location/maps/qgeotiledmappingmanagerengine_p_p.h create mode 100644 src/location/maps/qgeotiledmapreply.cpp create mode 100644 src/location/maps/qgeotiledmapreply_p.h create mode 100644 src/location/maps/qgeotiledmapreply_p_p.h create mode 100644 src/location/maps/qgeotiledmapscene.cpp create mode 100644 src/location/maps/qgeotiledmapscene_p.h create mode 100644 src/location/maps/qgeotilefetcher.cpp create mode 100644 src/location/maps/qgeotilefetcher_p.h create mode 100644 src/location/maps/qgeotilefetcher_p_p.h create mode 100644 src/location/maps/qgeotilerequestmanager.cpp create mode 100644 src/location/maps/qgeotilerequestmanager_p.h create mode 100644 src/location/maps/qgeotilespec.cpp create mode 100644 src/location/maps/qgeotilespec_p.h create mode 100644 src/location/maps/qgeotilespec_p_p.h create mode 100644 src/location/maps/qnavigationmanager.cpp create mode 100644 src/location/maps/qnavigationmanager_p.h create mode 100644 src/location/maps/qnavigationmanagerengine.cpp create mode 100644 src/location/maps/qnavigationmanagerengine_p.h create mode 100644 src/location/places/placemacro.h create mode 100644 src/location/places/places.pri create mode 100644 src/location/places/qplace.cpp create mode 100644 src/location/places/qplace.h create mode 100644 src/location/places/qplace_p.h create mode 100644 src/location/places/qplaceattribute.cpp create mode 100644 src/location/places/qplaceattribute.h create mode 100644 src/location/places/qplaceattribute_p.h create mode 100644 src/location/places/qplacecategory.cpp create mode 100644 src/location/places/qplacecategory.h create mode 100644 src/location/places/qplacecategory_p.h create mode 100644 src/location/places/qplacecontactdetail.cpp create mode 100644 src/location/places/qplacecontactdetail.h create mode 100644 src/location/places/qplacecontactdetail_p.h create mode 100644 src/location/places/qplacecontent.cpp create mode 100644 src/location/places/qplacecontent.h create mode 100644 src/location/places/qplacecontent_p.h create mode 100644 src/location/places/qplacecontentreply.cpp create mode 100644 src/location/places/qplacecontentreply.h create mode 100644 src/location/places/qplacecontentrequest.cpp create mode 100644 src/location/places/qplacecontentrequest.h create mode 100644 src/location/places/qplacecontentrequest_p.h create mode 100644 src/location/places/qplacedetailsreply.cpp create mode 100644 src/location/places/qplacedetailsreply.h create mode 100644 src/location/places/qplaceeditorial.cpp create mode 100644 src/location/places/qplaceeditorial.h create mode 100644 src/location/places/qplaceeditorial_p.h create mode 100644 src/location/places/qplaceicon.cpp create mode 100644 src/location/places/qplaceicon.h create mode 100644 src/location/places/qplaceicon_p.h create mode 100644 src/location/places/qplaceidreply.cpp create mode 100644 src/location/places/qplaceidreply.h create mode 100644 src/location/places/qplaceimage.cpp create mode 100644 src/location/places/qplaceimage.h create mode 100644 src/location/places/qplaceimage_p.h create mode 100644 src/location/places/qplacemanager.cpp create mode 100644 src/location/places/qplacemanager.h create mode 100644 src/location/places/qplacemanagerengine.cpp create mode 100644 src/location/places/qplacemanagerengine.h create mode 100644 src/location/places/qplacemanagerengine_p.h create mode 100644 src/location/places/qplacematchreply.cpp create mode 100644 src/location/places/qplacematchreply.h create mode 100644 src/location/places/qplacematchrequest.cpp create mode 100644 src/location/places/qplacematchrequest.h create mode 100644 src/location/places/qplaceproposedsearchresult.cpp create mode 100644 src/location/places/qplaceproposedsearchresult.h create mode 100644 src/location/places/qplaceproposedsearchresult_p.h create mode 100644 src/location/places/qplaceratings.cpp create mode 100644 src/location/places/qplaceratings.h create mode 100644 src/location/places/qplaceratings_p.h create mode 100644 src/location/places/qplacereply.cpp create mode 100644 src/location/places/qplacereply.h create mode 100644 src/location/places/qplacereply_p.h create mode 100644 src/location/places/qplaceresult.cpp create mode 100644 src/location/places/qplaceresult.h create mode 100644 src/location/places/qplaceresult_p.h create mode 100644 src/location/places/qplacereview.cpp create mode 100644 src/location/places/qplacereview.h create mode 100644 src/location/places/qplacereview_p.h create mode 100644 src/location/places/qplacesearchreply.cpp create mode 100644 src/location/places/qplacesearchreply.h create mode 100644 src/location/places/qplacesearchrequest.cpp create mode 100644 src/location/places/qplacesearchrequest.h create mode 100644 src/location/places/qplacesearchresult.cpp create mode 100644 src/location/places/qplacesearchresult.h create mode 100644 src/location/places/qplacesearchresult_p.h create mode 100644 src/location/places/qplacesearchsuggestionreply.cpp create mode 100644 src/location/places/qplacesearchsuggestionreply.h create mode 100644 src/location/places/qplacesupplier.cpp create mode 100644 src/location/places/qplacesupplier.h create mode 100644 src/location/places/qplacesupplier_p.h create mode 100644 src/location/places/qplaceuser.cpp create mode 100644 src/location/places/qplaceuser.h create mode 100644 src/location/places/qplaceuser_p.h create mode 100644 src/location/places/unsupportedreplies_p.h create mode 100644 src/location/qlocation.cpp create mode 100644 src/location/qlocation.h create mode 100644 src/location/qlocationglobal.h create mode 100644 src/location/qlocationglobal_p.h create mode 100644 src/locationlabs/qlocationlabsglobal_p.h create mode 100644 src/plugins/geoservices/esri/esri.pro create mode 100644 src/plugins/geoservices/esri/esri.qrc create mode 100644 src/plugins/geoservices/esri/esri_plugin.json create mode 100644 src/plugins/geoservices/esri/geocodereply_esri.cpp create mode 100644 src/plugins/geoservices/esri/geocodereply_esri.h create mode 100644 src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp create mode 100644 src/plugins/geoservices/esri/geocodingmanagerengine_esri.h create mode 100644 src/plugins/geoservices/esri/geomapsource.cpp create mode 100644 src/plugins/geoservices/esri/geomapsource.h create mode 100644 src/plugins/geoservices/esri/georoutejsonparser_esri.cpp create mode 100644 src/plugins/geoservices/esri/georoutejsonparser_esri.h create mode 100644 src/plugins/geoservices/esri/georoutereply_esri.cpp create mode 100644 src/plugins/geoservices/esri/georoutereply_esri.h create mode 100644 src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp create mode 100644 src/plugins/geoservices/esri/georoutingmanagerengine_esri.h create mode 100644 src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp create mode 100644 src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h create mode 100644 src/plugins/geoservices/esri/geotiledmap_esri.cpp create mode 100644 src/plugins/geoservices/esri/geotiledmap_esri.h create mode 100644 src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp create mode 100644 src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h create mode 100644 src/plugins/geoservices/esri/geotiledmapreply_esri.cpp create mode 100644 src/plugins/geoservices/esri/geotiledmapreply_esri.h create mode 100644 src/plugins/geoservices/esri/geotilefetcher_esri.cpp create mode 100644 src/plugins/geoservices/esri/geotilefetcher_esri.h create mode 100644 src/plugins/geoservices/esri/maps.json create mode 100644 src/plugins/geoservices/geoservices.pro create mode 100644 src/plugins/geoservices/itemsoverlay/itemsoverlay.pro create mode 100644 src/plugins/geoservices/itemsoverlay/itemsoverlay_plugin.json create mode 100644 src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp create mode 100644 src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.h create mode 100644 src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.cpp create mode 100644 src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.h create mode 100644 src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.cpp create mode 100644 src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.h create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/LICENSE.txt create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/aerialway.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/airfield.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/airport.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/alcohol-shop.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/america-football.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/amusement-park.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/aquarium.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/art-gallery.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/attraction.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/bakery.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/bank.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/bar.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/barrier.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/baseball.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/basketball.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/bbq.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/beer.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/bicycle-share.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/bicycle.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/blood-bank.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/buddhism.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/building-alt1.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/building.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/bus.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/cafe.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/campsite.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/car.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/castle.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/cemetery.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/cinema.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/circle-stroked.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/circle.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/city.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/clothing-store.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/college.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/commercial.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/cricket.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/cross.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/dam.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/danger.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/defibrillator.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/dentist.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/doctor.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/dog-park.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/drinking-water.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/embassy.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/emergency-phone.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/entrance-alt1.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/entrance.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/farm.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/fast-food.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/fence.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/ferry.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/fire-station.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/florist.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/fuel.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/gaming.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/garden-center.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/garden.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/gift.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/golf.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/grocery.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/hairdresser.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/harbor.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/heart.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/heliport.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/home.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/horse-riding.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/hospital.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/ice-cream.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/industry.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/information.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/karaoke.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/landmark.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/landuse.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/laundry.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/library.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/lighthouse.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/lodging.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/logging.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/marker-stroked.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/marker.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/mobile-phone.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/monument.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/mountain.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/museum.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/music.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/natural.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/park-alt1.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/park.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/parking-garage.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/parking.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/pharmacy.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/picnic-site.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/pitch.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/place-of-worship.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/playground.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/police.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/post.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/prison.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/rail-light.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/rail-metro.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/rail.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/ranger-station.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/recycling.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/religious-christian.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/religious-jewish.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/religious-muslim.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/residential-community.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/restaurant.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/roadblock.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/rocket.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/school.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/scooter.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/shelter.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/shop.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/skiing.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/slaughterhouse.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/snowmobile.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/soccer.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/square-stroked.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/square.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/stadium.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/star-stroked.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/star.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/suitcase.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/sushi.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/swimming.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/teahouse.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/telephone.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/tennis.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/theatre.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/toilet.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/town-hall.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/town.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/triangle-stroked.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/triangle.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/veterinary.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/village.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/volcano.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/warehouse.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/waste-basket.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/water.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/wetland.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/wheelchair.svg create mode 100644 src/plugins/geoservices/mapbox/maki-4.0.0/zoo.svg create mode 100644 src/plugins/geoservices/mapbox/mapbox.pro create mode 100644 src/plugins/geoservices/mapbox/mapbox.qrc create mode 100644 src/plugins/geoservices/mapbox/mapbox_plugin.json create mode 100644 src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeocodereplymapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeomapreplymapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeoroutereplymapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h create mode 100644 src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h create mode 100644 src/plugins/geoservices/mapbox/qmapboxcommon.cpp create mode 100644 src/plugins/geoservices/mapbox/qmapboxcommon.h create mode 100644 src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.h create mode 100644 src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.h create mode 100644 src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qplacesearchreplymapbox.h create mode 100644 src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp create mode 100644 src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.h create mode 100644 src/plugins/geoservices/mapboxgl/logo.png create mode 100644 src/plugins/geoservices/mapboxgl/mapboxgl.pro create mode 100644 src/plugins/geoservices/mapboxgl/mapboxgl.qrc create mode 100644 src/plugins/geoservices/mapboxgl/mapboxgl_plugin.json create mode 100644 src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp create mode 100644 src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h create mode 100644 src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h create mode 100644 src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp create mode 100644 src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h create mode 100644 src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp create mode 100644 src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.h create mode 100644 src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp create mode 100644 src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h create mode 100644 src/plugins/geoservices/mapboxgl/qsgmapboxglnode.cpp create mode 100644 src/plugins/geoservices/mapboxgl/qsgmapboxglnode.h create mode 100644 src/plugins/geoservices/nokia/logo.png create mode 100644 src/plugins/geoservices/nokia/marclanguagecodes.h create mode 100644 src/plugins/geoservices/nokia/nokia.pro create mode 100644 src/plugins/geoservices/nokia/nokia.qrc create mode 100644 src/plugins/geoservices/nokia/nokia_plugin.json create mode 100644 src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.cpp create mode 100644 src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.h create mode 100644 src/plugins/geoservices/nokia/placesv2/placesv2.pri create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.cpp create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.h create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.h create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.h create mode 100644 src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.cpp create mode 100644 src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.h create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.h create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp create mode 100644 src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.h create mode 100644 src/plugins/geoservices/nokia/qgeocodejsonparser.cpp create mode 100644 src/plugins/geoservices/nokia/qgeocodejsonparser.h create mode 100644 src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeocodereply_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeoerror_messages.cpp create mode 100644 src/plugins/geoservices/nokia/qgeoerror_messages.h create mode 100644 src/plugins/geoservices/nokia/qgeofiletilecachenokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeofiletilecachenokia.h create mode 100644 src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.cpp create mode 100644 src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.h create mode 100644 src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeomapreply_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeomapversion.cpp create mode 100644 src/plugins/geoservices/nokia/qgeomapversion.h create mode 100644 src/plugins/geoservices/nokia/qgeonetworkaccessmanager.h create mode 100644 src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeoroutereply_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp create mode 100644 src/plugins/geoservices/nokia/qgeoroutexmlparser.h create mode 100644 src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeotiledmap_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp create mode 100644 src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h create mode 100644 src/plugins/geoservices/nokia/qgeouriprovider.cpp create mode 100644 src/plugins/geoservices/nokia/qgeouriprovider.h create mode 100644 src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp create mode 100644 src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h create mode 100644 src/plugins/geoservices/nokia/uri_constants.cpp create mode 100644 src/plugins/geoservices/nokia/uri_constants.h create mode 100644 src/plugins/geoservices/osm/osm.pro create mode 100644 src/plugins/geoservices/osm/osm_plugin.json create mode 100644 src/plugins/geoservices/osm/providers/5.8/cycle create mode 100644 src/plugins/geoservices/osm/providers/5.8/hiking create mode 100644 src/plugins/geoservices/osm/providers/5.8/night-transit create mode 100644 src/plugins/geoservices/osm/providers/5.8/satellite create mode 100644 src/plugins/geoservices/osm/providers/5.8/street create mode 100644 src/plugins/geoservices/osm/providers/5.8/street-hires create mode 100644 src/plugins/geoservices/osm/providers/5.8/terrain create mode 100644 src/plugins/geoservices/osm/providers/5.8/transit create mode 100644 src/plugins/geoservices/osm/qgeocodereplyosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeocodereplyosm.h create mode 100644 src/plugins/geoservices/osm/qgeocodingmanagerengineosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeocodingmanagerengineosm.h create mode 100644 src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeofiletilecacheosm.h create mode 100644 src/plugins/geoservices/osm/qgeomapreplyosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeomapreplyosm.h create mode 100644 src/plugins/geoservices/osm/qgeoroutereplyosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeoroutereplyosm.h create mode 100644 src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.h create mode 100644 src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.h create mode 100644 src/plugins/geoservices/osm/qgeotiledmaposm.cpp create mode 100644 src/plugins/geoservices/osm/qgeotiledmaposm.h create mode 100644 src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h create mode 100644 src/plugins/geoservices/osm/qgeotilefetcherosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeotilefetcherosm.h create mode 100644 src/plugins/geoservices/osm/qgeotileproviderosm.cpp create mode 100644 src/plugins/geoservices/osm/qgeotileproviderosm.h create mode 100644 src/plugins/geoservices/osm/qplacecategoriesreplyosm.cpp create mode 100644 src/plugins/geoservices/osm/qplacecategoriesreplyosm.h create mode 100644 src/plugins/geoservices/osm/qplacemanagerengineosm.cpp create mode 100644 src/plugins/geoservices/osm/qplacemanagerengineosm.h create mode 100644 src/plugins/geoservices/osm/qplacesearchreplyosm.cpp create mode 100644 src/plugins/geoservices/osm/qplacesearchreplyosm.h create mode 100644 src/plugins/plugins.pro create mode 100644 src/plugins/position/android/android.pro create mode 100644 src/plugins/position/android/jar/AndroidManifest.xml create mode 100644 src/plugins/position/android/jar/jar.pro create mode 100644 src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java create mode 100644 src/plugins/position/android/src/jnipositioning.cpp create mode 100644 src/plugins/position/android/src/jnipositioning.h create mode 100644 src/plugins/position/android/src/plugin.json create mode 100644 src/plugins/position/android/src/positionfactory_android.cpp create mode 100644 src/plugins/position/android/src/positionfactory_android.h create mode 100644 src/plugins/position/android/src/qgeopositioninfosource_android.cpp create mode 100644 src/plugins/position/android/src/qgeopositioninfosource_android_p.h create mode 100644 src/plugins/position/android/src/qgeosatelliteinfosource_android.cpp create mode 100644 src/plugins/position/android/src/qgeosatelliteinfosource_android_p.h create mode 100644 src/plugins/position/android/src/src.pro create mode 100644 src/plugins/position/corelocation/corelocation.pro create mode 100644 src/plugins/position/corelocation/plugin.json create mode 100644 src/plugins/position/corelocation/qgeopositioninfosource_cl.mm create mode 100644 src/plugins/position/corelocation/qgeopositioninfosource_cl_p.h create mode 100644 src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.h create mode 100644 src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.mm create mode 100644 src/plugins/position/geoclue/geoclue.pro create mode 100644 src/plugins/position/geoclue/geocluetypes.cpp create mode 100644 src/plugins/position/geoclue/geocluetypes.h create mode 100644 src/plugins/position/geoclue/org.freedesktop.Geoclue.Master.xml create mode 100644 src/plugins/position/geoclue/org.freedesktop.Geoclue.MasterClient.xml create mode 100644 src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml create mode 100644 src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml create mode 100644 src/plugins/position/geoclue/org.freedesktop.Geoclue.Velocity.xml create mode 100644 src/plugins/position/geoclue/org.freedesktop.Geoclue.xml create mode 100644 src/plugins/position/geoclue/plugin.json create mode 100644 src/plugins/position/geoclue/qgeocluemaster.cpp create mode 100644 src/plugins/position/geoclue/qgeocluemaster.h create mode 100644 src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp create mode 100644 src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h create mode 100644 src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.cpp create mode 100644 src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.h create mode 100644 src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.cpp create mode 100644 src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.h create mode 100644 src/plugins/position/gypsy/gypsy.pro create mode 100644 src/plugins/position/gypsy/plugin.json create mode 100644 src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.cpp create mode 100644 src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.h create mode 100644 src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy.cpp create mode 100644 src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy_p.h create mode 100644 src/plugins/position/position.pro create mode 100644 src/plugins/position/positionpoll/plugin.json create mode 100644 src/plugins/position/positionpoll/positionpoll.pro create mode 100644 src/plugins/position/positionpoll/positionpollfactory.cpp create mode 100644 src/plugins/position/positionpoll/positionpollfactory.h create mode 100644 src/plugins/position/positionpoll/qgeoareamonitor_polling.cpp create mode 100644 src/plugins/position/positionpoll/qgeoareamonitor_polling.h create mode 100644 src/plugins/position/serialnmea/plugin.json create mode 100644 src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp create mode 100644 src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.h create mode 100644 src/plugins/position/serialnmea/serialnmea.pro create mode 100644 src/plugins/position/simulator/plugin.json create mode 100644 src/plugins/position/simulator/qgeopositioninfosource_simulator.cpp create mode 100644 src/plugins/position/simulator/qgeopositioninfosource_simulator_p.h create mode 100644 src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.cpp create mode 100644 src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.h create mode 100644 src/plugins/position/simulator/qgeosatelliteinfosource_simulator.cpp create mode 100644 src/plugins/position/simulator/qgeosatelliteinfosource_simulator_p.h create mode 100644 src/plugins/position/simulator/qlocationconnection_simulator.cpp create mode 100644 src/plugins/position/simulator/qlocationconnection_simulator_p.h create mode 100644 src/plugins/position/simulator/simulator.pro create mode 100644 src/plugins/position/winrt/plugin.json create mode 100644 src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp create mode 100644 src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h create mode 100644 src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp create mode 100644 src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h create mode 100644 src/plugins/position/winrt/winrt.pro create mode 100644 src/positioning/configure.json create mode 100644 src/positioning/doc/qtpositioning.qdocconf create mode 100644 src/positioning/doc/snippets/cpp/cpp.pro create mode 100644 src/positioning/doc/snippets/cpp/cppqml.cpp create mode 100644 src/positioning/doc/snippets/cpp/main.cpp create mode 100644 src/positioning/doc/snippets/doc_src_qtpositioning.qml create mode 100644 src/positioning/doc/snippets/snippets.pro create mode 100644 src/positioning/doc/src/cpp-position.qdoc create mode 100644 src/positioning/doc/src/cpp-qml-positioning.qdoc create mode 100644 src/positioning/doc/src/qml-position.qdoc create mode 100644 src/positioning/doc/src/qtpositioning-examples.qdoc create mode 100644 src/positioning/doc/src/qtpositioning-plugins.qdoc create mode 100644 src/positioning/doc/src/qtpositioning-qml.qdoc create mode 100644 src/positioning/doc/src/qtpositioning.qdoc create mode 100644 src/positioning/positioning.pro create mode 100644 src/positioning/qclipperutils.cpp create mode 100644 src/positioning/qclipperutils_p.h create mode 100644 src/positioning/qdeclarativegeoaddress.cpp create mode 100644 src/positioning/qdeclarativegeoaddress_p.h create mode 100644 src/positioning/qdeclarativegeolocation.cpp create mode 100644 src/positioning/qdeclarativegeolocation_p.h create mode 100644 src/positioning/qdoublematrix4x4.cpp create mode 100644 src/positioning/qdoublematrix4x4_p.h create mode 100644 src/positioning/qdoublevector2d.cpp create mode 100644 src/positioning/qdoublevector2d_p.h create mode 100644 src/positioning/qdoublevector3d.cpp create mode 100644 src/positioning/qdoublevector3d_p.h create mode 100644 src/positioning/qgeoaddress.cpp create mode 100644 src/positioning/qgeoaddress.h create mode 100644 src/positioning/qgeoaddress_p.h create mode 100644 src/positioning/qgeoareamonitorinfo.cpp create mode 100644 src/positioning/qgeoareamonitorinfo.h create mode 100644 src/positioning/qgeoareamonitorsource.cpp create mode 100644 src/positioning/qgeoareamonitorsource.h create mode 100644 src/positioning/qgeocircle.cpp create mode 100644 src/positioning/qgeocircle.h create mode 100644 src/positioning/qgeocircle_p.h create mode 100644 src/positioning/qgeocoordinate.cpp create mode 100644 src/positioning/qgeocoordinate.h create mode 100644 src/positioning/qgeocoordinate_p.h create mode 100644 src/positioning/qgeocoordinateobject.cpp create mode 100644 src/positioning/qgeocoordinateobject_p.h create mode 100644 src/positioning/qgeolocation.cpp create mode 100644 src/positioning/qgeolocation.h create mode 100644 src/positioning/qgeolocation_p.h create mode 100644 src/positioning/qgeopath.cpp create mode 100644 src/positioning/qgeopath.h create mode 100644 src/positioning/qgeopath_p.h create mode 100644 src/positioning/qgeopolygon.cpp create mode 100644 src/positioning/qgeopolygon.h create mode 100644 src/positioning/qgeopositioninfo.cpp create mode 100644 src/positioning/qgeopositioninfo.h create mode 100644 src/positioning/qgeopositioninfo_p.h create mode 100644 src/positioning/qgeopositioninfosource.cpp create mode 100644 src/positioning/qgeopositioninfosource.h create mode 100644 src/positioning/qgeopositioninfosource_p.h create mode 100644 src/positioning/qgeopositioninfosourcefactory.cpp create mode 100644 src/positioning/qgeopositioninfosourcefactory.h create mode 100644 src/positioning/qgeorectangle.cpp create mode 100644 src/positioning/qgeorectangle.h create mode 100644 src/positioning/qgeorectangle_p.h create mode 100644 src/positioning/qgeosatelliteinfo.cpp create mode 100644 src/positioning/qgeosatelliteinfo.h create mode 100644 src/positioning/qgeosatelliteinfosource.cpp create mode 100644 src/positioning/qgeosatelliteinfosource.h create mode 100644 src/positioning/qgeoshape.cpp create mode 100644 src/positioning/qgeoshape.h create mode 100644 src/positioning/qgeoshape_p.h create mode 100644 src/positioning/qlocationdata_simulator.cpp create mode 100644 src/positioning/qlocationdata_simulator_p.h create mode 100644 src/positioning/qlocationutils.cpp create mode 100644 src/positioning/qlocationutils_p.h create mode 100644 src/positioning/qnmeapositioninfosource.cpp create mode 100644 src/positioning/qnmeapositioninfosource.h create mode 100644 src/positioning/qnmeapositioninfosource_p.h create mode 100644 src/positioning/qpositioningglobal.h create mode 100644 src/positioning/qpositioningglobal_p.h create mode 100644 src/positioning/qwebmercator.cpp create mode 100644 src/positioning/qwebmercator_p.h create mode 100644 src/positioningquick/positioningquick.pro create mode 100644 src/positioningquick/qdeclarativeposition.cpp create mode 100644 src/positioningquick/qdeclarativeposition_p.h create mode 100644 src/positioningquick/qdeclarativepositionsource.cpp create mode 100644 src/positioningquick/qdeclarativepositionsource_p.h create mode 100644 src/positioningquick/qpositioningquickglobal.h create mode 100644 src/positioningquick/qpositioningquickglobal_p.h create mode 100644 src/src.pro create mode 100644 sync.profile create mode 100644 tests/applications/positioning_backend/main.cpp create mode 100644 tests/applications/positioning_backend/positioning_backend.pro create mode 100644 tests/applications/positioning_backend/widget.cpp create mode 100644 tests/applications/positioning_backend/widget.h create mode 100644 tests/applications/positioning_backend/widget.ui create mode 100644 tests/auto/auto.pro create mode 100644 tests/auto/bic/data/QtPositioning.5.10.0.linux-gcc-amd64.txt create mode 100644 tests/auto/bic/data/QtPositioning.5.11.0.linux-gcc-amd64.txt create mode 100644 tests/auto/bic/data/QtPositioning.5.3.0.linux-gcc-amd64.txt create mode 100644 tests/auto/bic/data/QtPositioning.5.4.0.linux-gcc-amd64.txt create mode 100644 tests/auto/bic/data/QtPositioning.5.6.0.linux-gcc-amd64.txt create mode 100644 tests/auto/bic/data/QtPositioning.5.7.0.linux-gcc-amd64.txt create mode 100644 tests/auto/bic/data/QtPositioning.5.8.0.linux-gcc-amd64.txt create mode 100644 tests/auto/bic/data/QtPositioning.5.9.0.linux-gcc-amd64.txt create mode 100644 tests/auto/cmake/CMakeLists.txt create mode 100644 tests/auto/cmake/cmake.pro create mode 100644 tests/auto/declarative_core/declarative_core.pro create mode 100644 tests/auto/declarative_core/main.cpp create mode 100644 tests/auto/declarative_core/tst_address.qml create mode 100644 tests/auto/declarative_core/tst_category.qml create mode 100644 tests/auto/declarative_core/tst_categorymodel.qml create mode 100644 tests/auto/declarative_core/tst_contactdetail.qml create mode 100644 tests/auto/declarative_core/tst_coordinate.qml create mode 100644 tests/auto/declarative_core/tst_editorialmodel.qml create mode 100644 tests/auto/declarative_core/tst_geocoding.qml create mode 100644 tests/auto/declarative_core/tst_imagemodel.qml create mode 100644 tests/auto/declarative_core/tst_place.qml create mode 100644 tests/auto/declarative_core/tst_placeattribute.qml create mode 100644 tests/auto/declarative_core/tst_placeicon.qml create mode 100644 tests/auto/declarative_core/tst_placesearchmodel.qml create mode 100644 tests/auto/declarative_core/tst_placesearchsuggestionmodel.qml create mode 100644 tests/auto/declarative_core/tst_plugin.qml create mode 100644 tests/auto/declarative_core/tst_plugin_error.qml create mode 100644 tests/auto/declarative_core/tst_position.qml create mode 100644 tests/auto/declarative_core/tst_positionsource.qml create mode 100644 tests/auto/declarative_core/tst_ratings.qml create mode 100644 tests/auto/declarative_core/tst_reviewmodel.qml create mode 100644 tests/auto/declarative_core/tst_routing.qml create mode 100644 tests/auto/declarative_core/tst_supplier.qml create mode 100644 tests/auto/declarative_core/tst_user.qml create mode 100644 tests/auto/declarative_core/utils.js create mode 100644 tests/auto/declarative_geoshape/declarative_geoshape.pro create mode 100644 tests/auto/declarative_geoshape/main.cpp create mode 100644 tests/auto/declarative_geoshape/tst_locationsingleton.qml create mode 100644 tests/auto/declarative_ui/BLACKLIST create mode 100644 tests/auto/declarative_ui/ItemGroup.qml create mode 100644 tests/auto/declarative_ui/declarative_ui.pro create mode 100644 tests/auto/declarative_ui/main.cpp create mode 100644 tests/auto/declarative_ui/tst_map.qml create mode 100644 tests/auto/declarative_ui/tst_map_coordinateanimation.qml create mode 100644 tests/auto/declarative_ui/tst_map_error.qml create mode 100644 tests/auto/declarative_ui/tst_map_flick.qml create mode 100644 tests/auto/declarative_ui/tst_map_item.qml create mode 100644 tests/auto/declarative_ui/tst_map_item_details.qml create mode 100644 tests/auto/declarative_ui/tst_map_item_fit_viewport.qml create mode 100644 tests/auto/declarative_ui/tst_map_itemview.qml create mode 100644 tests/auto/declarative_ui/tst_map_keepgrab.qml create mode 100644 tests/auto/declarative_ui/tst_map_maptype.qml create mode 100644 tests/auto/declarative_ui/tst_map_mouse.qml create mode 100644 tests/auto/declarative_ui/tst_map_pinch.qml.QTBUG-47970 create mode 100644 tests/auto/doublevectors/doublevectors.pro create mode 100644 tests/auto/doublevectors/tst_doublevectors.cpp create mode 100644 tests/auto/geotestplugin/geotestplugin.json create mode 100644 tests/auto/geotestplugin/geotestplugin.pro create mode 100644 tests/auto/geotestplugin/place_data.json create mode 100644 tests/auto/geotestplugin/qgeocodingmanagerengine_test.h create mode 100644 tests/auto/geotestplugin/qgeomappingmanagerengine_test.h create mode 100644 tests/auto/geotestplugin/qgeoroutingmanagerengine_test.h create mode 100644 tests/auto/geotestplugin/qgeoserviceproviderplugin_test.cpp create mode 100644 tests/auto/geotestplugin/qgeoserviceproviderplugin_test.h create mode 100644 tests/auto/geotestplugin/qgeotiledmap_test.cpp create mode 100644 tests/auto/geotestplugin/qgeotiledmap_test.h create mode 100644 tests/auto/geotestplugin/qgeotiledmappingmanagerengine_test.h create mode 100644 tests/auto/geotestplugin/qgeotilefetcher_test.h create mode 100644 tests/auto/geotestplugin/qplacemanagerengine_test.h create mode 100644 tests/auto/geotestplugin/testdata.qrc create mode 100644 tests/auto/maptype/maptype.pro create mode 100644 tests/auto/maptype/tst_maptype.cpp create mode 100644 tests/auto/nokia_services/nokia_services.pro create mode 100644 tests/auto/nokia_services/places_semiauto/places_semiauto.pro create mode 100644 tests/auto/nokia_services/places_semiauto/tst_places.cpp create mode 100644 tests/auto/nokia_services/routing/error-no-route.xml create mode 100644 tests/auto/nokia_services/routing/invalid-response-half-way-through.xml create mode 100644 tests/auto/nokia_services/routing/invalid-response-no-calculateroute-tag.xml create mode 100644 tests/auto/nokia_services/routing/invalid-response-no-route-tag.xml create mode 100644 tests/auto/nokia_services/routing/invalid-response-trash.xml create mode 100644 tests/auto/nokia_services/routing/littered-with-new-tags.xml create mode 100644 tests/auto/nokia_services/routing/multiple-routes-in-response.xml create mode 100644 tests/auto/nokia_services/routing/optim-fastest.xml create mode 100644 tests/auto/nokia_services/routing/optim-shortest.xml create mode 100644 tests/auto/nokia_services/routing/routing.pro create mode 100644 tests/auto/nokia_services/routing/travelmode-car.xml create mode 100644 tests/auto/nokia_services/routing/travelmode-pedestrian.xml create mode 100644 tests/auto/nokia_services/routing/travelmode-public-transport.xml create mode 100644 tests/auto/nokia_services/routing/tst_routing.cpp create mode 100644 tests/auto/placemanager_utils/placemanager_utils.cpp create mode 100644 tests/auto/placemanager_utils/placemanager_utils.h create mode 100644 tests/auto/placesplugin_unsupported/placesplugin.json create mode 100644 tests/auto/placesplugin_unsupported/placesplugin_unsupported.pro create mode 100644 tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.cpp create mode 100644 tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.h create mode 100644 tests/auto/positionplugin/plugin.cpp create mode 100644 tests/auto/positionplugin/plugin.json create mode 100644 tests/auto/positionplugin/positionplugin.pro create mode 100644 tests/auto/positionplugintest/positionplugintest.pro create mode 100644 tests/auto/positionplugintest/tst_positionplugin.cpp create mode 100644 tests/auto/qgeoaddress/qgeoaddress.pro create mode 100644 tests/auto/qgeoaddress/tst_qgeoaddress.cpp create mode 100644 tests/auto/qgeoareamonitor/logfilepositionsource.cpp create mode 100644 tests/auto/qgeoareamonitor/logfilepositionsource.h create mode 100644 tests/auto/qgeoareamonitor/qgeoareamonitor.pro create mode 100644 tests/auto/qgeoareamonitor/simplelog.txt create mode 100644 tests/auto/qgeoareamonitor/tst_qgeoareamonitor.cpp create mode 100644 tests/auto/qgeocameracapabilities/qgeocameracapabilities.pro create mode 100644 tests/auto/qgeocameracapabilities/tst_qgeocameracapabilities.cpp create mode 100644 tests/auto/qgeocameradata/qgeocameradata.pro create mode 100644 tests/auto/qgeocameradata/tst_qgeocameradata.cpp create mode 100644 tests/auto/qgeocameratiles/qgeocameratiles.pro create mode 100644 tests/auto/qgeocameratiles/tst_qgeocameratiles.cpp create mode 100644 tests/auto/qgeocircle/qgeocircle.pro create mode 100644 tests/auto/qgeocircle/tst_qgeocircle.cpp create mode 100644 tests/auto/qgeocodereply/qgeocodereply.pro create mode 100644 tests/auto/qgeocodereply/tst_qgeocodereply.cpp create mode 100644 tests/auto/qgeocodereply/tst_qgeocodereply.h create mode 100644 tests/auto/qgeocodingmanager/qgeocodingmanager.pro create mode 100644 tests/auto/qgeocodingmanager/tst_qgeocodingmanager.cpp create mode 100644 tests/auto/qgeocodingmanager/tst_qgeocodingmanager.h create mode 100644 tests/auto/qgeocodingmanagerplugins/geocoding_plugin.json create mode 100644 tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerengine_test.h create mode 100644 tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerplugins.pro create mode 100644 tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.cpp create mode 100644 tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.h create mode 100644 tests/auto/qgeocoordinate/qgeocoordinate.pro create mode 100644 tests/auto/qgeocoordinate/tst_qgeocoordinate.cpp create mode 100644 tests/auto/qgeolocation/qgeolocation.pro create mode 100644 tests/auto/qgeolocation/tst_qgeolocation.cpp create mode 100644 tests/auto/qgeolocation/tst_qgeolocation.h create mode 100644 tests/auto/qgeomaneuver/qgeomaneuver.pro create mode 100644 tests/auto/qgeomaneuver/tst_qgeomaneuver.cpp create mode 100644 tests/auto/qgeomaneuver/tst_qgeomaneuver.h create mode 100644 tests/auto/qgeopath/qgeopath.pro create mode 100644 tests/auto/qgeopath/tst_qgeopath.cpp create mode 100644 tests/auto/qgeopolygon/qgeopolygon.pro create mode 100644 tests/auto/qgeopolygon/tst_qgeopolygon.cpp create mode 100644 tests/auto/qgeopositioninfo/qgeopositioninfo.pro create mode 100644 tests/auto/qgeopositioninfo/tst_qgeopositioninfo.cpp create mode 100644 tests/auto/qgeopositioninfosource/qgeopositioninfosource.pro create mode 100644 tests/auto/qgeopositioninfosource/testqgeopositioninfosource.cpp create mode 100644 tests/auto/qgeopositioninfosource/testqgeopositioninfosource_p.h create mode 100644 tests/auto/qgeopositioninfosource/tst_qgeopositioninfosource.cpp create mode 100644 tests/auto/qgeorectangle/qgeorectangle.pro create mode 100644 tests/auto/qgeorectangle/tst_qgeorectangle.cpp create mode 100644 tests/auto/qgeoroute/qgeoroute.pro create mode 100644 tests/auto/qgeoroute/tst_qgeoroute.cpp create mode 100644 tests/auto/qgeoroute/tst_qgeoroute.h create mode 100644 tests/auto/qgeoroutereply/qgeoroutereply.pro create mode 100644 tests/auto/qgeoroutereply/tst_qgeoroutereply.cpp create mode 100644 tests/auto/qgeoroutereply/tst_qgeoroutereply.h create mode 100644 tests/auto/qgeorouterequest/qgeorouterequest.pro create mode 100644 tests/auto/qgeorouterequest/tst_qgeorouterequest.cpp create mode 100644 tests/auto/qgeorouterequest/tst_qgeorouterequest.h create mode 100644 tests/auto/qgeoroutesegment/qgeoroutesegment.pro create mode 100644 tests/auto/qgeoroutesegment/tst_qgeoroutesegment.cpp create mode 100644 tests/auto/qgeoroutesegment/tst_qgeoroutesegment.h create mode 100644 tests/auto/qgeoroutexmlparser/fixtures.qrc create mode 100644 tests/auto/qgeoroutexmlparser/qgeoroutexmlparser.pro create mode 100644 tests/auto/qgeoroutexmlparser/route1.xml create mode 100644 tests/auto/qgeoroutexmlparser/route2.xml create mode 100644 tests/auto/qgeoroutexmlparser/tst_qgeoroutexmlparser.cpp create mode 100644 tests/auto/qgeoroutingmanager/qgeoroutingmanager.pro create mode 100644 tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.cpp create mode 100644 tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.h create mode 100644 tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerengine_test.h create mode 100644 tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerplugins.pro create mode 100644 tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.cpp create mode 100644 tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.h create mode 100644 tests/auto/qgeoroutingmanagerplugins/routing_plugin.json create mode 100644 tests/auto/qgeosatelliteinfo/qgeosatelliteinfo.pro create mode 100644 tests/auto/qgeosatelliteinfo/tst_qgeosatelliteinfo.cpp create mode 100644 tests/auto/qgeosatelliteinfosource/qgeosatelliteinfosource.pro create mode 100644 tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource.cpp create mode 100644 tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource_p.h create mode 100644 tests/auto/qgeosatelliteinfosource/tst_qgeosatelliteinfosource.cpp create mode 100644 tests/auto/qgeoserviceprovider/qgeoserviceprovider.pro create mode 100644 tests/auto/qgeoserviceprovider/tst_qgeoserviceprovider.cpp create mode 100644 tests/auto/qgeoshape/qgeoshape.pro create mode 100644 tests/auto/qgeoshape/tst_qgeoshape.cpp create mode 100644 tests/auto/qgeotiledmap/BLACKLIST create mode 100644 tests/auto/qgeotiledmap/qgeotiledmap.pro create mode 100644 tests/auto/qgeotiledmap/tst_qgeotiledmap.cpp create mode 100644 tests/auto/qgeotiledmapscene/qgeotiledmapscene.pro create mode 100644 tests/auto/qgeotiledmapscene/tst_qgeotiledmapscene.cpp create mode 100644 tests/auto/qgeotilespec/qgeotilespec.pro create mode 100644 tests/auto/qgeotilespec/tst_qgeotilespec.cpp create mode 100644 tests/auto/qmlinterface/data/TestAddress.qml create mode 100644 tests/auto/qmlinterface/data/TestCategory.qml create mode 100644 tests/auto/qmlinterface/data/TestContactDetail.qml create mode 100644 tests/auto/qmlinterface/data/TestIcon.qml create mode 100644 tests/auto/qmlinterface/data/TestLocation.qml create mode 100644 tests/auto/qmlinterface/data/TestPlace.qml create mode 100644 tests/auto/qmlinterface/data/TestPlaceAttribute.qml create mode 100644 tests/auto/qmlinterface/data/TestRatings.qml create mode 100644 tests/auto/qmlinterface/data/TestSupplier.qml create mode 100644 tests/auto/qmlinterface/data/TestUser.qml create mode 100644 tests/auto/qmlinterface/qmlinterface.pro create mode 100644 tests/auto/qmlinterface/tst_qmlinterface.cpp create mode 100644 tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/dummynmeapositioninfosource.pro create mode 100644 tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/tst_dummynmeapositioninfosource.cpp create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource.pro create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/qnmeapositioninfosource_realtime.pro create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/tst_qnmeapositioninfosource_realtime.cpp create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/qnmeapositioninfosource_realtime_generic.pro create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/tst_qnmeapositioninfosource_realtime_generic.cpp create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/qnmeapositioninfosource_simulation.pro create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/tst_qnmeapositioninfosource_simulation.cpp create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/qnmeapositioninfosource_simulation_generic.pro create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/tst_qnmeapositioninfosource_simulation_generic.cpp create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.cpp create mode 100644 tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.h create mode 100644 tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp create mode 100644 tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h create mode 100644 tests/auto/qplace/qplace.pro create mode 100644 tests/auto/qplace/tst_qplace.cpp create mode 100644 tests/auto/qplaceattribute/qplaceattribute.pro create mode 100644 tests/auto/qplaceattribute/tst_qplaceattribute.cpp create mode 100644 tests/auto/qplacecategory/qplacecategory.pro create mode 100644 tests/auto/qplacecategory/tst_qplacecategory.cpp create mode 100644 tests/auto/qplacecontactdetail/qplacecontactdetail.pro create mode 100644 tests/auto/qplacecontactdetail/tst_qplacecontactdetail.cpp create mode 100644 tests/auto/qplacecontentrequest/qplacecontentrequest.pro create mode 100644 tests/auto/qplacecontentrequest/tst_qplacecontentrequest.cpp create mode 100644 tests/auto/qplacedetailsreply/qplacedetailsreply.pro create mode 100644 tests/auto/qplacedetailsreply/tst_qplacedetailsreply.cpp create mode 100644 tests/auto/qplaceeditorial/qplaceeditorial.pro create mode 100644 tests/auto/qplaceeditorial/tst_qplaceeditorial.cpp create mode 100644 tests/auto/qplaceimage/qplaceimage.pro create mode 100644 tests/auto/qplaceimage/tst_qplaceimage.cpp create mode 100644 tests/auto/qplacemanager/qplacemanager.pro create mode 100644 tests/auto/qplacemanager/tst_qplacemanager.cpp create mode 100644 tests/auto/qplacemanager_nokia/qplacemanager_nokia.pro create mode 100644 tests/auto/qplacemanager_nokia/tst_qplacemanager_nokia.cpp create mode 100644 tests/auto/qplacemanager_unsupported/qplacemanager_unsupported.pro create mode 100644 tests/auto/qplacemanager_unsupported/tst_qplacemanager_unsupported.cpp create mode 100644 tests/auto/qplacematchreply/qplacematchreply.pro create mode 100644 tests/auto/qplacematchreply/tst_qplacematchreply.cpp create mode 100644 tests/auto/qplacematchrequest/qplacematchrequest.pro create mode 100644 tests/auto/qplacematchrequest/tst_qplacematchrequest.cpp create mode 100644 tests/auto/qplaceperiod/qplaceperiod.pro create mode 100644 tests/auto/qplaceperiod/tst_qplaceperiod.cpp create mode 100644 tests/auto/qplaceratings/qplaceratings.pro create mode 100644 tests/auto/qplaceratings/tst_qplaceratings.cpp create mode 100644 tests/auto/qplacereply/qplacereply.pro create mode 100644 tests/auto/qplacereply/tst_qplacereply.cpp create mode 100644 tests/auto/qplaceresult/qplaceresult.pro create mode 100644 tests/auto/qplaceresult/tst_qplaceresult.cpp create mode 100644 tests/auto/qplacereview/qplacereview.pro create mode 100644 tests/auto/qplacereview/tst_qplacereview.cpp create mode 100644 tests/auto/qplacesearchreply/qplacesearchreply.pro create mode 100644 tests/auto/qplacesearchreply/tst_qplacesearchreply.cpp create mode 100644 tests/auto/qplacesearchrequest/qplacesearchrequest.pro create mode 100644 tests/auto/qplacesearchrequest/tst_qplacesearchrequest.cpp create mode 100644 tests/auto/qplacesearchresult/qplacesearchresult.pro create mode 100644 tests/auto/qplacesearchresult/tst_qplacesearchresult.cpp create mode 100644 tests/auto/qplacesearchsuggestionreply/qplacesearchsuggestionreply.pro create mode 100644 tests/auto/qplacesearchsuggestionreply/tst_qplacesearchsuggestionreply.cpp create mode 100644 tests/auto/qplacesupplier/qplacesupplier.pro create mode 100644 tests/auto/qplacesupplier/tst_qplacesupplier.cpp create mode 100644 tests/auto/qplaceuser/qplaceuser.pro create mode 100644 tests/auto/qplaceuser/tst_qplaceuser.cpp create mode 100644 tests/auto/qproposedsearchresult/qproposedsearchresult.pro create mode 100644 tests/auto/qproposedsearchresult/tst_qproposedsearchresult.cpp create mode 100644 tests/auto/utils/qlocationtestutils.cpp create mode 100644 tests/auto/utils/qlocationtestutils_p.h create mode 100644 tests/global/global.cfg create mode 100644 tests/plugins/declarativetestplugin/declarativetestplugin.pro create mode 100644 tests/plugins/declarativetestplugin/locationtest.cpp create mode 100644 tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel.cpp create mode 100644 tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel_p.h create mode 100644 tests/plugins/declarativetestplugin/qdeclarativepinchgenerator.cpp create mode 100644 tests/plugins/declarativetestplugin/qdeclarativepinchgenerator_p.h create mode 100644 tests/plugins/declarativetestplugin/qmldir create mode 100644 tests/plugins/declarativetestplugin/testhelper.h create mode 100644 tests/plugins/imports.pri create mode 100644 tests/tests.pro diff --git a/.QT-ENTERPRISE-LICENSE-AGREEMENT b/.QT-ENTERPRISE-LICENSE-AGREEMENT new file mode 100644 index 0000000..3cbd6af --- /dev/null +++ b/.QT-ENTERPRISE-LICENSE-AGREEMENT @@ -0,0 +1,1089 @@ +QT LICENSE AGREEMENT +Agreement version 4.1 +This License Agreement (“Agreement”) is a legal agreement between The Qt Company +(as defined below) and the Licensee (as defined below) for the license of +Licensed Software (as defined below). Capitalized terms used herein are defined +in Section 1. +WHEREAS: + +(A) Licensee wishes to use the Licensed Software for the purpose of developing + and distributing Applications and/or Devices; and +(B) The Qt Company is willing to grant the Licensee a right to use Licensed + Software for such purpose pursuant to term and conditions of this Agreement. + +NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS: + +1. DEFINITIONS +“Affiliate” of a Party shall mean an entity (i) which is directly or indirectly +controlling such Party; (ii) which is under the same direct or indirect +ownership or control as such Party; or (iii) which is directly or indirectly +owned or controlled by such Party. For these purposes, an entity shall be +treated as being controlled by another if that other entity has fifty percent +(50 %) or more of the votes in such entity, is able to direct its affairs and/or +to control the composition of its board of directors or equivalent body. + +“Add-on Products” shall mean The Qt Company’s specific add-on software products +(for example Qt Safe Renderer, Qt for Automation, Qt Application Manager), which +are not licensed as part of The Qt Company’s standard offering, but shall be +included into the scope of Licensed Software only if so specifically agreed +between the Parties. + +“Applications” shall mean Licensee's software products created using the +Licensed Software, which may include the Redistributables, or part thereof. + +“Contractor(s)” shall mean third party consultants, distributors and contractors +performing services to a Party under applicable contractual arrangement. + +“Customer(s)” shall mean Licensee’s end users to whom Licensee, directly or +indirectly, distributes copies of the Redistributables. + +“Deployment Platforms” shall mean operating systems specified in the License +Certificate, in which the Redistributables can be distributed pursuant to the +terms and conditions of this Agreement. + +“Designated User(s)” shall mean the employee(s) of Licensee or Licensee’s +Affiliates acting within the scope of their employment or Licensee's Contractors +acting within the scope of their services for Licensee and on behalf of +Licensee. Designated Users shall be named in the License Certificate. + +“Development License” shall mean the license needed by the Licensee for each +Designated User to use the Licensed Software under the license grant described +in Section 3.1 of this Agreement. Development Licenses are available separately +for Qt for Application Development (desktop) and Qt for Device Creation +(embedded) products, each product having its designated scope and purpose of +use. Distribution Licenses are always connected to Qt for Device Creation +product only. + +“Development Platforms” shall mean those operating systems specified in the +License Certificate, in which the Licensed Software can be used under the +Development License, but not distributed in any form or used for any other +purpose. + +“Devices” shall mean hardware devices or products that 1) are manufactured +and/or distributed by the Licensee or its Affiliates or Contractors, and 2) +(i) incorporate or integrate the Redistributables or parts thereof; or (ii) do +not incorporate or integrate the Redistributables at the time of distribution, +but where, when used by a Customer, the main user interface or substantial +functionality of such device is provided by Application(s) or otherwise depends +on the Licensed Software. Devices shall be specified in Appendix 2 or in a +quote. + +“Distribution License(s)” shall mean the license required for distribution of +Redistributables in connection with Devices pursuant to license grant described +in Section 3.3 of this Agreement. + +“Distribution License Packs” shall mean set of prepaid Distribution Licenses for +distribution of Redistributables, as defined in The Qt Company’s standard price +list, quote, Purchase Order confirmation or in an appendix hereto, as the case +may be. + +“Intellectual Property Rights” shall mean patents (including utility models), +design patents, and designs (whether or not capable of registration), chip +topography rights and other like protection, copyrights, trademarks, service +marks, trade names, logos or other words or symbols and any other form of +statutory protection of any kind and applications for any of the foregoing as +well as any trade secrets. + +“License Certificate” shall mean a certificate generated by The Qt Company for +each Designated User respectively upon them downloading the Licensed Software. +License Certificate will be available under respective Designated User’s Qt +Account at account.qt.io and it will specify the Designated User, the +Development Platforms, Deployment Platforms and the License Term. The terms of +the License Certificate are considered part of this Agreement and shall be +updated from time to time to reflect any agreed changes to the foregoing terms +relating to Designated User’s rights to the Licensed Software. + +“License Fee” shall mean the fee charged to the Licensee for rights granted +under the terms of this Agreement. + +“License Term” shall mean the agreed validity period of the Development License +of the respective Designated User, during which time the Designated User is +entitled to use the Licensed Software, as set forth in the respective License +Certificate. + +“Licensed Software” shall mean either +(i) Qt for Application Development or +(ii) Qt for Device Creation, and/or +(iii) Qt 3D Studio, and/or +(iv) Qt Design Studio, and/or +(v) selected Add-on Products, if any, depending on which product(s) the + Licensee has purchased under this Agreement, + +as well as corresponding online or electronic documentation, associated media +and printed materials, including the source code, example programs and the +documentation, licensed to the Licensee under this Agreement. Licensed Software +does not include Third Party Software (as defined in Section 4) or Open Source +Qt. The Qt Company may, in the course of its development activities, at its free +and absolute discretion and without any obligation to send or publish any +notifications to the Licensee or in general, make changes, additions or +deletions in the components and functionalities of the Licensed Software, +provided that no such changes, additions or deletions will affect the already +released version of the Licensed Software, but only upcoming version(s). + +“Licensee” shall mean the individual or legal entity that is party to this +Agreement, as identified on the signature page hereof. + +“Licensee’s Records” shall mean books and records that are likely to contain +information bearing on Licensee’s compliance with this Agreement or the payments +due to The Qt Company under this Agreement, including, but not limited to: +assembly logs, sales records and distribution records. + +“Modified Software” shall have the meaning as set forth in Section 2.3. + +“Online Services” shall mean any services or access to systems made available by +The Qt Company to the Licensee over the Internet relating to the Licensed +Software or for the purpose of use by the Licensee of the Licensed Software or +Support. Use of any such Online Services is discretionary for the Licensee and +some of them may be subject to additional fees. + +“Open Source Qt” shall mean the non-commercial Qt computer software products, +licensed under the terms of the GNU Lesser General Public License, version 2.1 +or later (“LGPL”) or the GNU General Public License, version 2.0 or later +(“GPL”). For clarity, Open Source Qt shall not be provided nor governed under +this Agreement. + +”Party” or “Parties” shall mean Licensee and/or The Qt Company. + +“Qt 3D Studio” shall mean all versions of The Qt Company’s Qt 3D Studio, a 3D +user interface design and development environment for rapid designing and +prototyping of animated user interfaces. + +“Qt Design Studio” shall mean all versions of The Qt Company’s Qt Design Studio +tool, a 2D user interface design and development environment for rapid designing +and prototyping of animated user interfaces. + +“Qt for Application Development” shall mean The Qt Company’s productized +offering, which consist of all versions of +(i) Qt Toolkit, and +(ii) Qt Tools/Applications. + +“Qt for Device Creation” shall mean The Qt Company’s productized offering, +which consist of all versions of +(i) Qt for Application Development, and +(ii) Software components specific to embedded software development as set forth + in Appendix 1, Sections 1b and 1d. + +“Qt Toolkit” shall mean the modules defined in Appendix 1, Section 1a. + +“Qt Tools/Applications” shall mean the tools defined in Appendix 1, Section 1c. + +"Redistributables" shall mean the portions of the Licensed Software set forth in +Appendix 1, Section 2 that may be distributed pursuant to the terms of this +Agreement in object code form only, including any relevant documentation. Where +relevant, any reference to Licensed Software in this Agreement shall include and +refer also to Redistributables. + +“Renewal Term” shall mean an extension of previous License Term as agreed +between the Parties. + +“Submitted Modified Software” shall have the meaning as set forth in +Section 2.3. + +“Support” shall mean standard developer support that is provided by The Qt +Company to assist Designated Users in using the Licensed Software in accordance +with The Qt Company’s standard support terms and as further defined in +Section 8 hereunder. + +“Taxes” shall have the meaning set forth in Section 10.5. + +“Term” shall have the meaning set forth in Section 12. + +“The Qt Company” shall mean: +(i) in the event Licensee is an individual residing in the United States or a + legal entity incorporated in the United States or having its headquarters + in the United States, The Qt Company Inc., a Delaware corporation with its + office at 2350 Mission College Blvd., Suite 1020, Santa Clara, CA 95054, + USA.; or +(ii) in the event the Licensee is an individual residing outside of the United + States or a legal entity incorporated outside of the United States or + having its registered office outside of the United States, The Qt Company + Ltd., a Finnish company with its registered office at Bertel Jungin aukio + D3A, 02600 Espoo, Finland. + +"Third Party Software " shall have the meaning set forth in Section 4. + +“Updates” shall mean a release or version of the Licensed Software containing +bug fixes, error corrections and other changes that are generally made available +to users of the Licensed Software that have contracted for Support. Updates are +generally depicted as a change to the digits following the decimal in the +Licensed Software version number. The Qt Company shall make Updates available to +the Licensee under the Support. Updates shall be considered as part of the +Licensed Software hereunder. + +“Upgrades” shall mean a release or version of the Licensed Software containing +enhancements and new features and are generally depicted as a change to the +first digit of the Licensed Software version number. In the event Upgrades are +provided to the Licensee under this Agreement, they shall be considered as part +of the Licensed Software hereunder. + +2. OWNERSHIP +2.1 Ownership of The Qt Company +The Licensed Software is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. The Licensed +Software is licensed, not sold. All The Qt Company's Intellectual Property +Rights are and shall remain the exclusive property of The Qt Company or its +licensors respectively. + +2.2 Ownership of Licensee +All the Licensee's Intellectual Property Rights are and shall remain the +exclusive property of the Licensee or its licensors respectively. All +Intellectual Property Rights to the Modified Software, Applications and Devices +shall remain with the Licensee and no rights thereto shall be granted by the +Licensee to The Qt Company under this Agreement (except as set forth in Section +2.3 below). + +2.3 Modified Software +Licensee may create bug-fixes, error corrections, patches or modifications to +the Licensed Software (“Modified Software”). Such Modified Software may break +the source or binary compatibility with the Licensed Software (including without +limitation through changing the application programming interfaces ("API") or by +adding, changing or deleting any variable, method, or class signature in the +Licensed Software and/or any inter-process protocols, services or standards in +the Licensed Software libraries). To the extent that Licensee’s Modified +Software so breaks source or binary compatibility with the Licensed Software, +Licensee acknowledges that The Qt Company's ability to provide Support may be +prevented or limited and Licensee's ability to make use of Updates may be +restricted. Licensee may, at its sole and absolute discretion, choose to submit +Modified Software to The Qt Company (“Submitted Modified Software”) in +connection with Licensee’s Support request, service request or otherwise. In the +event Licensee does so, then, Licensee hereby grants The Qt Company a +sublicensable, assignable, irrevocable, perpetual, worldwide, non-exclusive, +royalty-free and fully paid-up license, under all of Licensee’s Intellectual +Property Rights, to reproduce, adapt, translate, modify, and prepare derivative +works of, publicly display, publicly perform, sublicense, make available and +distribute such Submitted Modified Software as The Qt Company sees fit at its +free and absolute discretion. + +3. LICENSES GRANTED +3.1 Development with Licensed Software +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable license, valid for the +License Term, to use, modify and copy the Licensed Software by Designated Users +on the Development Platforms for the sole purposes of designing, developing, +demonstrating and testing Application(s) and/or Devices, and to provide thereto +related support and other related services to end-user Customers. Licensee may +install copies of the Licensed Software on an unlimited number of computers +provided that (i) only the Designated Users may use the Licensed Software, and +(ii) all Designated Users must have a valid Development License to use Licensed +Software. Licensee may at any time designate another Designated User to replace +a then-current Designated User by notifying The Qt Company in writing, provided +that any Designated User may be replaced only once during any six-month period. +Upon expiry of the initially agreed License Term, the respective License Terms +shall be automatically extended to one or more Renewal Term(s), unless and until +either Party notifies the other Party in writing that it does not wish to +continue the License Term, such notification to be provided to the other Party +no less than ninety (90) days before expiry of the respective License Term. +Unless otherwise agreed between the Parties, Renewal Term shall be of equal +length with the initial Term. Any such Renewal Term shall be subject to License +Fees agreed between the Parties or, if no advance agreement exists, subject to +The Qt Company’s standard pricing applicable at the commencement date of any +such Renewal Term. + +3.2 Distribution of Applications +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through its Contractors, Redistributables as installed, +incorporated or integrated into Applications for execution on the Deployment +Platforms, and (ii) grant sublicenses to Redistributables, as distributed +hereunder, for Customers solely for Customer’s internal use and to the extent +necessary in order for the Customers to use the Applications for their +respective intended purposes. +Right to distribute the Redistributables as part of an Application as provided +herein is not royalty-bearing but is conditional upon the Licensee having paid +the agreed Development Licenses from The Qt Company before distributing any +Redistributables to Customers. + +3.3 Distribution of Devices +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through one or more tiers of Contractors, +Redistributables as installed, incorporated or integrated, or intended to be +installed, incorporated or integrated into Devices for execution on the +Deployment Platforms, and (ii) grant sublicenses to Redistributables, as +distributed hereunder, for Customers solely for Customer’s internal use and to +the extent necessary in order for the Customers to use the Devices for their +respective intended purposes. +Right to distribute the Redistributables with Devices as provided herein is +conditional upon the Licensee having purchased and paid the appropriate amount +of Development Licenses for Qt for Device Creation product and Distribution +Licenses from The Qt Company before distributing any Redistributables to +Customers. + +3.4 Further Requirements +The licenses granted above in this Section 3 by The Qt Company to Licensee are +conditional and subject to Licensee's compliance with the following terms: +(i) Licensee shall not remove or alter any copyright, trademark or other + proprietary rights notice contained in any portion of the Licensed + Software; +(ii) Applications must add primary and substantial functionality to the + Licensed Software; +(iii) Applications may not pass on functionality which in any way makes it + possible for others to create software with the Licensed Software; + provided however that Licensee may use the Licensed Software's scripting + and QML ("Qt Quick") functionality solely in order to enable scripting, + themes and styles that augment the functionality and appearance of the + Application(s) without adding primary and substantial functionality to + the Application(s); +(iv) Applications must not compete with the Licensed Software; +(v) Licensee shall not use The Qt Company's or any of its suppliers' names, + logos, or trademarks to market Applications, except that Licensee may use + “Built with Qt” logo to indicate that Application(s) was developed using + the Licensed Software; +(vi) Licensee shall not distribute, sublicense or disclose source code of + Licensed Software to any third party (provided however that Licensee may + appoint employee(s) of Contractors as Designated Users to use Licensed + Software pursuant to this Agreement). Such right may be available for the + Licensee subject to a separate software development kit (“SDK”) license + agreement to be concluded with The Qt Company; +(vii) Licensee shall not grant the Customers a right to (i) make copies of the + Redistributables except when and to the extent required to use the + Applications and/or Devices for their intended purpose, (ii) modify the + Redistributables or create derivative works thereof, (iii) decompile, + disassemble or otherwise reverse engineer Redistributables, or (iv) + redistribute any copy or portion of the Redistributables to any third + party, except as part of the onward sale of the Device on which the + Redistributables are installed; +(viii) Licensee shall not and shall cause that its Affiliates or Contractors + shall not a) in any way combine, incorporate or integrate Licensed + Software with, or use Licensed Software for creation of, any software + created with or incorporating Open Source Qt, or b) incorporate or + integrate Applications into a hardware device or product other than a + Device, unless Licensee has received an advance written permission from + The Qt Company to do so. Absent such written permission, any and all + distribution by the Licensee during the Term of a hardware device or + product a) which incorporate or integrate any part of Licensed Software + or Open Source Qt; or b) where the main user interface or substantial + functionality is provided by software built with Licensed Software or + Open Source Qt or otherwise depends on the Licensed Software or Open + Source Qt, shall be considered as a Device distribution under this + Agreement and dependent on compliance thereof (including but not limited + to obligation to pay applicable License Fees for such distribution). + Notwithstanding what is provided above in this sub-section (viii), + Licensee is entitled to use and combine Qt 3D Studio and/or Qt Design + Studio with Open Source Qt (“Combination”) for its internal evaluation + purposes, provided that Licensee shall in no way transfer, publish, + disclose, display or otherwise make available any software or work + resulting from such Combination; +(ix) Licensee shall cause all of its Affiliates and Contractors entitled to + make use of the licenses granted under this Agreement, to be + contractually bound to comply with the relevant terms of this Agreement + and not to use the Licensed Software beyond the terms hereof and for any + purposes other than operating within the scope of their services for + Licensee. Licensee shall be responsible for any and all actions and + omissions of its Affiliates and Contractors relating to the Licensed + Software and use thereof (including but not limited to payment of all + applicable License Fees); +(x) Except when and to the extent explicitly provided in this Section 3, + Licensee shall not transfer, publish, disclose, display or otherwise + make available the Licensed Software; +; and +(xi) Licensee shall not attempt or enlist a third party to conduct or attempt + to conduct any of the above. + +Above terms shall not be applicable if and to the extent they conflict with any +mandatory provisions of any applicable laws. Any use of Licensed Software beyond +the provisions of this Agreement is strictly prohibited and requires an +additional license from The Qt Company. + +4. THIRD PARTY SOFTWARE +The Licensed Software may provide links to third party libraries or code +(collectively "Third Party Software") to implement various functions. Third +Party Software does not comprise part of the Licensed Software. In some cases, +access to Third Party Software may be included in the Licensed Software. Such +Third Party Software will be listed in the ".../src/3rdparty" source tree +delivered with the Licensed Software or documented in the Licensed Software, as +such may be amended from time to time. Licensee acknowledges that use or +distribution of Third Party Software is in all respects subject to applicable +license terms of applicable third party right holders. + +5. PRE-RELEASE CODE +The Licensed Software may contain pre-release code and functionality marked or +otherwise stated as “Technology Preview”, “Alpha”, “Beta” or similar +designation. Such pre-release code may be present in order to provide +experimental support for new platforms or preliminary versions of one or more +new functionalities. The pre-release code may not be at the level of performance +and compatibility of a final, generally available, product offering of the +Licensed Software. The pre-release parts of the Licensed Software may not +operate correctly, may contain errors and may be substantially modified by The +Qt Company prior to the first commercial product release, if any. The Qt Company +is under no obligation to make pre-release code commercially available, or +provide any Support or Updates relating thereto. The Qt Company assumes no +liability whatsoever regarding any pre-release code, but any use thereof is +exclusively at Licensee’s own risk and expense. For clarity, Licensee is +entitled to use such pre-release code pursuant to Section 3, just like other +Licensed Software, provided however that in the event Add-on Products are +included and available as such pre-release code, Licensee’s right to use such +Add-on Products is nevertheless subject to and conditional upon conclusion of +separate agreement with The Qt Company. + +6. LIMITED WARRANTY AND WARRANTY DISCLAIMER +The Qt Company hereby represents and warrants that it has the power and +authority to grant the rights and licenses granted to Licensee under this +Agreement. Except as set forth above, the Licensed Software is licensed to +Licensee "as is" and Licensee’s exclusive remedy and The Qt Company’s entire +liability for errors in the Licensed Software shall be limited, at The Qt +Company’s option, to correction of the error, replacement of the Licensed +Software or return of the applicable fees paid for the defective Licensed +Software for the time period during which the License is not able to utilize the +Licensed Software under the terms of this Agreement. + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF +ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND +NON-INFRINGEMENT WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT +WARRANT THAT THE LICENSED SOFTWARE WILL SATISFY LICENSEE’S REQUIREMENTS OR THAT +IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE +UNINTERRUPTED. ALL USE OF AND RELIANCE ON THE LICENSED SOFTWARE IS AT THE SOLE +RISK OF AND RESPONSIBILITY OF LICENSEE. + +7. INDEMNIFICATION AND LIMITATION OF LIABILITY +7.1 Limitation of Liability +EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II) +BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO +EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT, +LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL, +CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND, +HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT. PARTIES +SPECIFICALLY AGREE THAT LICENSEE’S OBLIGATION TO PAY LICENSE AND OTHER FEES +CORRESPONDING TO ACTUAL USAGE OF LICENSED SOFTWARE HEREUNDER SHALL BE CONSIDERED +AS A DIRECT DAMAGE. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL +MISCONDUCT, AND (II) BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY +APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY’S TOTAL AGGREGATE LIABILITY UNDER +THIS AGREEMENT EXCEED THE AGGREGATE LICENSE FEES PAID OR PAYABLE TO THE QT +COMPANY FROM LICENSEE DURING THE PERIOD OF TWELVE (12) MONTHS IMMEDIATELY +PRECEDING THE EVENT RESULTING IN SUCH LIABILITY. THE PROVISIONS OF THIS SECTION +7 ALLOCATE THE RISKS UNDER THIS AGREEMENT BETWEEN THE QT COMPANY AND LICENSEE +AND THE PARTIES HAVE RELIED UPON THE LIMITATIONS SET FORTH HEREIN IN DETERMINING +WHETHER TO ENTER INTO THIS AGREEMENT. + +7.2 Licensee´s Indemnification +Licensee shall indemnify and hold harmless The Qt Company from and against any +claim, injury, judgment, settlement, loss or expense, including attorneys' fees +related to: (a) Licensee’s misrepresentation in connection with The Qt Company +or the Licensed Software or breach of this Agreement, (b) the Application or +Device (except where such cause of liability is solely attributable to the +Licensed Software). + +8. SUPPORT, UPDATES AND ONLINE SERVICES +Upon due payment of the agreed License Fees the Licensee will be eligible to +receive Support and Updates and to use the Online Services during the License +Term, provided, however, that in the event the License Term is longer than 36 +months, Support is provided only for the first 12 months, unless the Parties +specifically otherwise agree. Unless otherwise decided by The Company at its +free and absolute discretion, Upgrades will not be included in the Support but +may be available subject to additional fees. From time to time The Qt Company +may change the Support terms, provided that during the respective ongoing +License Term the level of Support provided by The Qt Company may not be reduced +without the consent of the Licensee. Unless otherwise agreed, The Qt Company +shall not be responsible for providing any service or support to Customers. + +9. CONFIDENTIALITY +Each Party acknowledges that during the Term of this Agreement each Party may +receive information about the other Party's business, business methods, business +plans, customers, business relations, technology, and other information, +including the terms of this Agreement, that is confidential and of great value +to the other Party, and the value of which would be significantly reduced if +disclosed to third parties (“Confidential Information”). Accordingly, when a +Party (the “Receiving Party”) receives Confidential Information from the other +Party (the “Disclosing Party”), the Receiving Party shall only disclose such +information to employees and Contractors on a need to know basis, and shall +cause its employees and employees of its Affiliates to: (i) maintain any and all +Confidential Information in confidence; (ii) not disclose the Confidential +Information to a third party without the Disclosing Party's prior written +approval; and (iii) not, directly or indirectly, use the Confidential +Information for any purpose other than for exercising its rights and fulfilling +its responsibilities pursuant to this Agreement. Each Party shall take +reasonable measures to protect the Confidential Information of the other Party, +which measures shall not be less than the measures taken by such Party to +protect its own confidential and proprietary information. Obligation of +confidentiality shall not apply to information that (i) is or becomes generally +known to the public through no act or omission of the Receiving Party; (ii) was +in the Receiving Party's lawful possession prior to the disclosure hereunder and +was not subject to limitations on disclosure or use; (iii) is developed +independently by employees or Contractors of the Receiving Party or other +persons working for the Receiving Party who have not had access to the +Confidential Information of the Disclosing Party, as proven by the written +records of the Receiving Party; (iv) is lawfully disclosed to the Receiving +Party without restrictions, by a third party not under an obligation of +confidentiality; or (v) the Receiving Party is legally compelled to disclose, in +which case the Receiving Party shall notify the Disclosing Party of such +compelled disclosure and assert the privileged and confidential nature of the +information and cooperate fully with the Disclosing Party to limit the scope of +disclosure and the dissemination of disclosed Confidential Information to the +minimum extent necessary. The obligations under this Section 9 shall continue to +remain in force for a period of five (5) years after the last disclosure, and, +with respect to trade secrets, for so long as such trade secrets are protected +under applicable trade secret laws. + +10. FEES, DELIVERY AND PAYMENT +10.1 License Fees +License Fees are described in The Qt Company’s standard price list, quote or +Purchase Order confirmation or in an appendix hereto, as the case may be. The +License Fees shall not be refunded or claimed as a credit in any event or for +any reason whatsoever. + +10.2 Ordering Licenses +Licensee may purchase Development Licenses and Distribution Licenses pursuant to +agreed pricing terms or, if no specific pricing terms have been agreed upon, at +The Qt Company's standard pricing terms applicable at the time of purchase. +Licensee shall submit all purchase orders for Development Licenses and +Distribution Licenses to The Qt Company by email or any other method acceptable +to The Qt Company (each such order is referred to herein as a “Purchase Order”) +for confirmation, whereupon the Purchase Order shall become binding between the +Parties. + +10.3 Distribution License Packs +Unless otherwise agreed, Distribution Licenses shall be purchased by way of +Distribution License Packs. Upon due payment of the ordered Distribution License +Pack(s), the Licensee will have an account of Distribution Licenses available +for installing, bundling or integrating (all jointly “installing”) the +Redistributables with the Devices or for otherwise distributing the +Redistributables in accordance with this Agreement. Each time Licensee +“installs” or distributes a copy of Redistributables, then one Distribution +License is used, and Licensee’s account of available Distribution Licenses is +decreased accordingly. Licensee may “install” copies of the Redistributables so +long as Licensee has Distribution Licenses remaining on its account. +Redistributables will be deemed to have been “installed” into a Device when one +of the following circumstances shall have occurred: a) the Redistributables have +been loaded onto the Device and used outside of the Licensee’s premises or b) +the Device has been fully tested and placed into Licensee's inventory (or sold) +for the first time (i.e., Licensee will not be required to use (or pay for) more +than one Distribution License for each individual Device, e.g. in a situation +where a Device is returned to Licensee's inventory after delivery to a +distributor or sale to a Customer). In addition, if Licensee includes a back-up +copy of the Redistributables on a CD-ROM or other storage medium along with the +product, that backup copy of the Redistributables will not be deemed to have +been “installed” and will not require an additional Distribution License. + +10.4 Payment Terms +License Fees and any other charges under this Agreement shall be paid by +Licensee no later than thirty (30) days from the date of the applicable invoice +from The Qt Company. The Qt Company will submit an invoice to Licensee after the +date of this Agreement and/or after The Qt Company receives a Purchase Order +from Licensee. A late payment charge of the lower of (a) one percent per month; +or (b) the interest rate stipulated by applicable law, shall be charged on any +unpaid balances that remain past due. The Qt Company shall have the right to +suspend, terminate or withhold grants of all rights to the Licensed Software +hereunder, including but not limited to the Developer License, Distribution +License, and Support, should Licensee fail to make payment in timely fashion. + +10.5 Taxes +All License Fees and other charges payable hereunder are gross amounts but +exclusive of any value added tax, use tax, sales tax and other taxes, duties or +tariffs (“Taxes”). Such applicable Taxes shall be paid by Licensee, or, where +applicable, in lieu of payment of such Taxes, Licensee shall provide an +exemption certificate to The Qt Company and any applicable authority. + +11 RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS +11.1 Licensee’s Record-keeping +Licensee shall at all times maintain accurate and up-to-date written records of +Licensee’s activities related to the use of Licensed Software and distribution +of Redistributables. The records shall be adequate to determine Licensee’s +compliance with the provisions of this Agreement and to demonstrate the number +of Designated Users and Redistributables distributed by Licensee. The records +shall conform to good accounting practices reasonably acceptable to The Qt +Company. Licensee shall, within thirty (30) days from receiving The Qt Company’s +request to that effect, deliver to The Qt Company a report on Licensee’s usage +of Licensed Software, such report to contain information, in sufficient detail, +on (i) amount of users working with Licensed Software, (ii) copies of +Redistributables distributed by Licensee during that calendar quarter, (iii) +number of undistributed copies of Redistributables and corresponding number of +unused Distribution Licenses remaining on Licensee’s account, and (iv) any other +information as The Qt Company may reasonably require from time to time. + +11.2. The Qt Company’s Audit Rights +The Qt Company or an independent auditor acting on behalf of The Qt Company’s, +may, upon at least five (5) business days’ prior written notice and at its +expense, audit Licensee with respect to the use of the Redistributables, but not +more frequently than once during each 6-month period. Such audit may be +conducted by mail, electronic means or through an in-person visit to Licensee’s +place of business. Any such in-person audit shall be conducted during regular +business hours at Licensee's facilities and shall not unreasonably interfere +with Licensee's business activities. The Qt Company or the independent auditor +acting on behalf of The Qt Company shall be entitled to inspect Licensee’s +Records. All such Licensee’s Records and use thereof shall be subject to an +obligation of confidentiality under this Agreement. If an audit reveals that +Licensee is using the Licensed Software beyond scope of the licenses Licensee +has paid for, Licensee agrees to immediately pay The Qt Company any amounts owed +for such unauthorized use. +In addition, in the event the audit reveals a material violation of the terms of +this Agreement (underpayment of more than 5% of License Fees shall always be +deemed a material violation for purposes of this section), then the Licensee +shall pay The Qt Company's reasonable cost of conducting such audit. + +12 TERM AND TERMINATION +12.1 Term +This Agreement shall enter into force upon due acceptance by both Parties and +remain in force for as long as there is any Development License(s) in force +(“Term”), unless and until terminated pursuant to the terms of this Section 12. + +12.2 Termination by The Qt Company +The Qt Company shall have the right to terminate this Agreement upon thirty (30) +days prior written notice if the Licensee is in material breach of any +obligation of this Agreement and fails to remedy such breach within such notice +period. + +12.3 Mutual Right to Terminate +Either Party shall have the right to terminate this Agreement immediately upon +written notice in the event that the other Party becomes insolvent, files for +any form of bankruptcy, makes any assignment for the benefit of creditors, has a +receiver, administrative receiver or officer appointed over the whole or a +substantial part of its assets, ceases to conduct business, or an act equivalent +to any of the above occurs under the laws of the jurisdiction of the other +Party. + +12.4 Parties´ Rights and Duties upon Termination +Upon expiry or termination of the Agreement Licensee shall cease and shall cause +all Designated Users (including those of its Affiliates’ and Contractors’) to +cease using the Licensed Software and distribution of the Redistributables under +this Agreement. +Notwithstanding the above, in the event the Agreement expires or is terminated: +(i) as a result of The Qt Company choosing not to renew the Development + License(s) as set forth in Section 3.1, then all valid licenses possessed + by the Licensee at such date shall be extended to be valid in perpetuity + under the terms of this Agreement and Licensee is entitled to purchase + additional licenses as set forth in Section 10.2; or +(ii) for reason other than by The Qt Company pursuant to item (i) above or + pursuant to Section 12.2, then the Licensee is entitled, for a period of + six (6) months after the effective date of termination, to continue + distribution of Devices under the Distribution Licenses paid but unused at + such effective date of termination. Upon any such termination the Licensee + shall destroy or return to The Qt Company all copies of the Licensed + Software and all related materials and will certify the same to The Qt + Company upon its request, provided however that Licensee may retain and + exploit such copies of the Licensed Software as it may reasonably require + in providing continued support to Customers. +Expiry or termination of this Agreement for any reason whatsoever shall not +relieve Licensee of its obligation to pay any License Fees accrued or payable to +The Qt Company prior to the effective date of termination, and Licensee shall +immediately pay to The Qt Company all such fees upon the effective date of +termination. Termination of this Agreement shall not affect any rights of +Customers to continue use of Applications and Devices (and therein incorporated +Redistributables). + +12.5 Extension in case of bankruptcy +In the event The Qt Company is declared bankrupt under a final, non-cancellable +decision by relevant court of law, and this Agreement is not, at the date of +expiry of the Development License(s) pursuant to Section 3.1, assigned to party, +who has assumed The Qt Company’s position as a legitimate licensor of Licensed +Software under this Agreement, then all valid licenses possessed by the Licensee +at such date of expiry, and which the Licensee has not notified for expiry, +shall be extended to be valid in perpetuity under the terms of this Agreement. + +13. GOVERNING LAW AND LEGAL VENUE +In the event this Agreement is in the name of The Qt Company Inc., a Delaware +Corporation, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of the State of California, USA, excluding its choice of law + provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any dispute, claim or controversy arising out of or relating to this + Agreement or the breach, termination, enforcement, interpretation or + validity thereof, including the determination of the scope or + applicability of this Agreement to arbitrate, shall be determined by + arbitration in San Francisco, USA, before one arbitrator. The arbitration + shall be administered by JAMS pursuant to JAMS' Streamlined Arbitration + Rules and Procedures. Judgment on the Award may be entered in any court + having jurisdiction. This Section shall not preclude parties from seeking + provisional remedies in aid of arbitration from a court of appropriate + jurisdiction. +In the event this Agreement is in the name of The Qt Company Ltd., a Finnish +Company, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of Finland, excluding its choice of law provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any disputes, controversy or claim arising out of or relating to this + Agreement, or the breach, termination or validity thereof shall be shall + be finally settled by arbitration in accordance with the Arbitration Rules + of Finland Chamber of Commerce. The arbitration tribunal shall consist of + one (1), or if either Party so requires, of three (3), arbitrators. The + award shall be final and binding and enforceable in any court of competent + jurisdiction. The arbitration shall be held in Helsinki, Finland and the + process shall be conducted in the English language. This Section shall not + preclude parties from seeking provisional remedies in aid of arbitration + from a court of appropriate jurisdiction. + +14. GENERAL PROVISIONS +14.1 No Assignment +Except in the case of a merger or sale of substantially all of its corporate +assets, Licensee shall not be entitled to assign or transfer all or any of its +rights, benefits and obligations under this Agreement without the prior written +consent of The Qt Company, which shall not be unreasonably withheld or delayed. +The Qt Company shall be entitled to freely assign or transfer any of its rights, +benefits or obligations under this Agreement. + +14.2 No Third Party Representations +Licensee shall make no representations or warranties concerning the Licensed +Software on behalf of The Qt Company. Any representation or warranty Licensee +makes or purports to make on The Qt Company’s behalf shall be void as to The Qt +Company. + +14.3 Surviving Sections +Any terms and conditions that by their nature or otherwise reasonably should +survive termination of this Agreement shall so be deemed to survive. + +14.4 Entire Agreement +This Agreement, the exhibits hereto, the License Certificate and any applicable +Purchase Order constitute the complete agreement between the Parties and +supersedes all prior or contemporaneous discussions, representations, and +proposals, written or oral, with respect to the subject matters discussed +herein. +In the event of any conflict or inconsistency between this Agreement and any +Purchase Order, the terms of this Agreement will prevail over the terms of the +Purchase Order with respect to such conflict or inconsistency. +Parties specifically acknowledge and agree that this Agreement prevails over any +click-to-accept or similar agreements the Designated Users may need to accept +online upon download of the Licensed Software, as may be required by The Qt +Company’s applicable processes relating to Licensed Software. + +14.5 Modifications +No modification of this Agreement shall be effective unless contained in a +writing executed by an authorized representative of each Party. No term or +condition contained in Licensee's Purchase Order shall apply unless expressly +accepted by The Qt Company in writing. + +14.6 Force Majeure +Except for the payment obligations hereunder, neither Party shall be liable to +the other for any delay or non-performance of its obligations hereunder in the +event and to the extent that such delay or non-performance is due to an event of +act of God, terrorist attack or other similar unforeseeable catastrophic event +that prevents either Party for fulfilling its obligations under this Agreement +and which such Party cannot avoid or circumvent (“Force Majeure Event”). If the +Force Majeure Event results in a delay or non-performance of a Party for a +period of three (3) months or longer, then either Party shall have the right to +terminate this Agreement with immediate effect without any liability (except for +the obligations of payment arising prior to the event of Force Majeure) towards +the other Party. + +14.7 Notices +Any notice given by one Party to the other shall be deemed properly given and +deemed received if specifically acknowledged by the receiving Party in writing +or when successfully delivered to the recipient by hand, fax, or special courier +during normal business hours on a business day to the addresses specified for +each Party on the signature page. Each communication and document made or +delivered by one Party to the other Party pursuant to this Agreement shall be in +the English language. + +14.8 Export Control +Licensee acknowledges that the Redistributables may be subject to export control +restrictions under the applicable laws of respective countries. Licensee shall +fully comply with all applicable export license restrictions and requirements as +well as with all laws and regulations relating to the Redistributables and +exercise of licenses hereunder and shall procure all necessary governmental +authorizations, including without limitation, all necessary licenses, approvals, +permissions or consents, where necessary for the re-exportation of the +Redistributables, Applications and/or Devices. + +14.9 No Implied License +There are no implied licenses or other implied rights granted under this +Agreement, and all rights, save for those expressly granted hereunder, shall +remain with The Qt Company and its licensors. In addition, no licenses or +immunities are granted to the combination of the Licensed Software with any +other software or hardware not delivered by The Qt Company under this Agreement. + +14.10 Attorney Fees +The prevailing Party in any action to enforce this Agreement shall be entitled +to recover its attorney’s fees and costs in connection with such action. + +14.11 Severability +If any provision of this Agreement shall be adjudged by any court of competent +jurisdiction to be unenforceable or invalid, that provision shall be limited or +eliminated to the minimum extent necessary so that this Agreement shall +otherwise remain in full force and effect and enforceable. + + + +APPENDICES +The Agreement includes Appendix 1 as shown below. In addition, the Agreement may +include one or more of the Appendices 3-5 listed below depending on the +product(s) purchased by the Licensee, what is stated in the quote or invoice, +and/or what is stated on the License Certificate. + +  +APPENDIX 1: LICENSED SOFTWARE +1a. Licensed Software - Qt Toolkit +Module Description +Qt Core Core non-graphical classes used by other modules. +Qt GUI Base classes for graphical user interface (GUI) + components. +Qt Multimedia Classes for audio, video and camera functionality. +Qt Multimedia Widgets Widget-based classes for implementing multimedia + functionality. +Qt Network Classes to make network programming easier and more + portable. +Qt QML Classes for QML and JavaScript languages. +Qt Quick A declarative framework for building highly dynamic + applications with custom user interfaces. +Qt Quick Controls 2 Provides lightweight QML types for creating + performant user interfaces for desktop, embedded, and + mobile devices. +Qt Quick Dialogs Types for creating and interacting with system + dialogs from a Qt Quick application. +Qt Quick Layouts Layouts are items that are used to arrange Qt Quick 2 + based items in the user interface. +Qt Quick Test A unit test framework for QML applications. +Qt SQL Classes for database integration using SQL. +Qt Test Classes for unit testing Qt applications and + libraries. +Qt Widgets Classes to extend Qt GUI with C++ widgets. +Active Qt Classes for applications which use ActiveX and COM +Qt 3D Functionality for near-realtime simulation systems + with support for 2D and 3D rendering. +Qt Android Extras Provides platform-specific APIs for Android. +Qt Bluetooth Provides access to Bluetooth hardware. +Qt Canvas 3D Enables OpenGL-like 3D drawing calls from Qt Quick + applications using JavaScript. +Qt Concurrent Classes for writing multi-threaded programs without + using low-level threading primitives. +Qt D-Bus Classes for inter-process communication over the + D-Bus protocol. +Qt Gamepad Enables Qt applications to support the use of gamepad + hardware. +Qt Graphical Effects Graphical effects for use with Qt Quick 2. +Qt Help Classes for integrating documentation into + applications, similar to Qt Assistant. +Qt Image Formats Plugins for additional image formats: TIFF, MNG, TGA, + WBMP. +Qt Location Displays map, navigation, and place content in a QML + application. +Qt Mac Extras Provides platform-specific APIs for macOS. +Qt Network Authorization Provides support for OAuth-based authorization to + online services. +Qt NFC Provides access to Near-Field communication (NFC) + hardware. +Qt Platform Headers Provides classes that encapsulate platform-specific + information. +Qt Positioning Provides access to position, satellite and area + monitoring classes. +Qt Print Support Classes to make printing easier and more portable. +Qt Purchasing Enables in-app purchase of products in Qt + applications. +Qt for Python Python bindings for Qt. +Qt Quick Controls Reusable Qt Quick based UI controls to create classic + desktop-style user interfaces. +Qt Quick Extras Provides a specialized set of controls that can be + used to build interfaces in Qt Quick. +Qt Quick Widgets Provides a C++ widget class for displaying a Qt + Quick user interface. +Qt SCXML Provides classes and tools for creating state + machines from SCXML files. +Qt Sensors Provides access to sensor hardware and motion gesture + recognition. +Qt Serial Bus Provides access to serial industrial bus interface. +Qt Serial Port Provides access to hardware and virtual serial ports. +Qt Speech Provides support for accessibility features such as + text-to-speech. +Qt SVG Classes for displaying the contents of SVG files. +Qt UI Tools Classes for loading QWidget based forms created in Qt + Designer dynamically, at runtime. +Qt WebChannel Provides access to QObject or QML objects from HTML + clients for seamless integration of Qt applications + with HTML/JavaScript clients. +Qt WebEngine Classes and functions for embedding web content in + applications using the Chromium browser project. +Qt WebSockets Provides WebSocket communication. +Qt WebView Displays web content in a QML application by using + APIs native to the platform. +Qt Windows Extras Provides platform-specific APIs for Windows. +Qt X11 Extras Provides platform-specific APIs for X11. +Qt XML C++ implementations of SAX and DOM. +Qt XML Patterns Support for XPath, XQuery, XSLT and XML schema + validation. +Qt Wayland Compositor Provides a framework to develop a Wayland compositor. +Qt Charts UI Components for displaying charts. +Qt Data Visualization UI Components for creating 3D data visualizations. +Qt Virtual Keyboard A framework for implementing different input methods + as well as a QML virtual keyboard. + +1b. Licensed software – Embedded software development libraries +Module Description +Boot 2 Qt stack Yocto based Embedded Linux stack for selected + target hardware +Qt OTA Client-side capability for device image + updates Over The Air. +Device Utilities Collection of API’s to manage the device; + E.g. display, WiFi and Bluetooth settings. +Qt Debugging Bridge (QDB) Daemon Enables host-target deployment, debugging, + profiling and other features over USB. Up to + developer to decide if this is left in the + final solution. + +1c. Licensed Software - Qt Tools/Applications +Tool Description +Qt Creator The integrated development environment for Qt. +Qt Designer Qt tool for designing and building graphical user interfaces. +Qt Linguist Tool used to add translations to Qt applications. +Qt Assistant Tool for viewing online documentation in Qt help-file format. +Qmake Utility tool used to automate the generation of make files. +uic User interface compiler for the Qt GUI toolkit. +rcc Resource compiler used for embedding resources into Qt + applications. +lupdate Tool that finds the translatable strings in the specified source, + header and Qt Designer interface files, and produces or updates + translation files. +lrelease Tool that produces translation files in the compact binary format + used by localized Qt applications. +qlalr Qt parser generator tool. +qdoc Configurable documentation generation tool. +qmlscene QML launcher tool +qmlviewer QML launcher tool + +1d. Licensed software –Qt Tools/Applications specific to embedded software +development +Tool Description +Target toolchains Cross compilation toolchains for + supported target devices and operating + systems +Qt Debugging Bridge (QDB) Host Tools Enables deployment, debugging, + profiling and other features over USB + from development host PC to target + device. +qtconfig-gui Qt Lite Configurator tool graphical + interface +Qt Emulator Qt emulator + +2. Parts of the Licensed Software that are permitted for distribution in +object-code form only (“Redistributables”) under this Agreement: + +2a. Qt for Application Development +(i) The Licensed Software's Qt Toolkit libraries defined in 1a +(ii) The Licensed Software's installer framework + +2b. Qt for Device Creation +(i) Qt for Application Development Redistributables defined in 2a +(ii) The Licensed Software’s Embedded software development libraries defined in + 1b + +2c. Qt 3D Studio +The Licensed Software’s Qt 3D Studio Runtime (“Qt53DStudioRuntime2”) + + +APPENDIX 3: ADDITIONS TO LICENSED SOFTWARE +In addition to what is provided under the definition of the Licensed Software, +Parties agree that Licensed Software shall also include the following additional +software products of The Qt Company if included in the quote / invoice: +Qt for Automation + - MQTT software protocol libraries + - KNX software protocol libraries + - OPCUA (open source backend) + - OPCUA (Unified Automation backend) +Qt Safe Renderer + - Qt Safe Renderer library +Qt Application Manager + - Qt Application Manager library with Qt Creator integration + +All the above is considered as Redistributables and subject to applicable +provisions and limitations including but not limited to what is defined in +Section 3. + + +APPENDIX 4: SMALL BUSINESS AND START-UP APPENDIX +The provisions of this Appendix 4 are applicable for Start-up Companies and for +the Evaluation Term. +For the purpose of this Appendix 4, the following additional definitions shall +be applicable: + “Trial Term” shall mean a period of twelve (12) months. + “Start-up Company” means a company with a maximum annual revenue, including + funding, equivalent to 100,000 USD (in applicable currency) during a respective + calendar year, as evidenced by duly audited records of the Licensee and + approved by The Qt Company. + +During the Trial Term, Section 3 shall apply with following modifications +(“Trial Term Modifications”): + - Licenses granted under Sections 3.1 and 3.2 shall be free of any charge. For + clarity, License for distribution of Devices pursuant to Section 3.3 is + subject to applicable License Fee for necessary Distribution Licenses; + - Development License under Section 3.1 is limited to a maximum of three (3) + Designated Users; and + - Support is available subject to availability, as judged by The Qt Company at + its free and absolute discretion. + +Upon expiry of the Trial Term: + a) This Appendix 4 is terminated, Trial Term Modifications cease to remain in + force, Licensee’s Development Licenses shall be automatically converted into + licenses subject to a License Fee (in the amount specified in the quote or + in Appendix 2 and payable with a 30-day payment term) and Licensee’s rights + and obligations under this Agreement shall continue to remain in force under + the standard provisions of the Agreement, unless the Licensee notifies The + Qt Company in writing no less than ninety (90) days before such expiry date + that Licensee does not agree to such continuance, in which event the + Agreement, and all rights of the Licensee thereunder, shall expire; provided + however that + b) in the event the Licensee still qualifies as a Start-up Company, the + Licensee has an option (“Option”), instead of what is stated in item a) + above, to extend the Trial Term renewal is limited to one time and total + duration of Trial Terms thus to 24 months after the effective date. Licensee + shall notify The Qt Company in writing no less than ninety (90) days before + the expiry date, if Licensee wish to exercise the Option. + + +APPENDIX 5: NON-COMMERCIAL USE APPENDIX +The provisions of this Appendix 5 are applicable for non-commercial use of the +Licensed Software by the Licensee. +For the purpose of this Appendix 5, the following additional definitions +(replacing the relevant definition of the Agreement, where applicable) shall be +applicable: + “Demo Units” shall mean (i) hardware development platform, which incorporates + the Licensed Software along with Licensee’s software and/or hardware, and + (ii) prototype versions of Applications or Devices. + “Designated User(s)” shall mean the employees and students of the Licensee. + “Licensee Products” shall mean Applications and/or Devices. + “Permitted Purpose” shall mean (i) Licensee’s internal evaluation and testing + of Licensed Software, (ii) building Demo Units as well as (iii) educational + use. + “Term” shall mean a period of twelve (12) months or any such other period as + may be agreed between the Parties. + +For the purpose of this Appendix 5, the following changes shall be agreed with +respect to relevant Sections of the Agreement: + I. Recital (A) shall be replaced in its entirety to read as follows: “(A) + Licensee wishes to use the Licensed Software for the Permitted Purpose.” + II. Section 3.1 shall be replaced in its entirety to read as follows: + “The Qt Company grants to Licensee a personal, non-exclusive, + non-transferable, revocable, royalty-free license, valid for the Term, to + use, modify and copy the Licensed Software solely for the Permitted + Purpose. Licensee may install copies of the Licensed Software on an + unlimited number of computers provided that only Designated Users may use + the Licensed Software. Licensee may demonstrate the Demo Units, provided + that such demonstrations must be conducted by Licensee, and the Demo Units + must remain in Licensee’s possession and under Licensee’s control at all + times. For clarity, this Agreement does not (i) entitle Licensee to use + Licensed Software to create Applications or Devices (other than prototypes + thereof) or (ii) carry any distribution rights to Licensee, but such + rights are subject to and conditional upon conclusion of a separate + license agreement with The Qt Company.” + III. Sections 3.2, 3.3, 8 and 10 shall be deleted. + IV. Section 3.4 shall be replaced in its entirety to read as follows: + “Licensee shall not: + - remove or alter any copyright, trademark or other proprietary rights + notice contained in any portion of the Licensed Software; + - transfer, publish, sublicense, disclose, display or otherwise make + the Licensed Software available to any third party (except that + Licensee may demonstrate the Demo Units pursuant to Section 3.1); + - in any way combine, incorporate or integrate Licensed Software with, or + use Licensed Software for creation of, any software created with or + incorporating Open Source Qt; + Licensee shall cause all Designated Users who make use of the licenses + granted under this Agreement, to be contractually bound to comply with + the relevant terms of this Agreement and not to use the Licensed + Software beyond the terms hereof. Licensee shall be responsible for any + and all actions and omissions of its Designated Users relating to the + Licensed Software and use thereof. Any use of Licensed Software beyond + the provisions of this Agreement is strictly prohibited and requires an + additional license from The Qt Company.” + V. Section 12 shall be replaced in its entirety to read as follows: + “This Agreement shall enter into force upon due acceptance by both Parties + and remain in force for the Term, unless and until terminated pursuant to + the terms of Section 12. Upon termination of the Agreement, Licensee shall + cease using the Licensed Software. All other copies of Licensed Software + in the possession or control of Licensee must be erased or destroyed. An + officer of Licensee must, upon request, promptly deliver to The Qt Company + a written confirmation that this has occurred.” + +Except for the modifications specified above, this Appendix carries no change to +the terms of the Agreement which shall remain in full force. diff --git a/.QT-FOR-APPLICATION-DEVELOPMENT-LICENSE-AGREEMENT b/.QT-FOR-APPLICATION-DEVELOPMENT-LICENSE-AGREEMENT new file mode 100644 index 0000000..3cbd6af --- /dev/null +++ b/.QT-FOR-APPLICATION-DEVELOPMENT-LICENSE-AGREEMENT @@ -0,0 +1,1089 @@ +QT LICENSE AGREEMENT +Agreement version 4.1 +This License Agreement (“Agreement”) is a legal agreement between The Qt Company +(as defined below) and the Licensee (as defined below) for the license of +Licensed Software (as defined below). Capitalized terms used herein are defined +in Section 1. +WHEREAS: + +(A) Licensee wishes to use the Licensed Software for the purpose of developing + and distributing Applications and/or Devices; and +(B) The Qt Company is willing to grant the Licensee a right to use Licensed + Software for such purpose pursuant to term and conditions of this Agreement. + +NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS: + +1. DEFINITIONS +“Affiliate” of a Party shall mean an entity (i) which is directly or indirectly +controlling such Party; (ii) which is under the same direct or indirect +ownership or control as such Party; or (iii) which is directly or indirectly +owned or controlled by such Party. For these purposes, an entity shall be +treated as being controlled by another if that other entity has fifty percent +(50 %) or more of the votes in such entity, is able to direct its affairs and/or +to control the composition of its board of directors or equivalent body. + +“Add-on Products” shall mean The Qt Company’s specific add-on software products +(for example Qt Safe Renderer, Qt for Automation, Qt Application Manager), which +are not licensed as part of The Qt Company’s standard offering, but shall be +included into the scope of Licensed Software only if so specifically agreed +between the Parties. + +“Applications” shall mean Licensee's software products created using the +Licensed Software, which may include the Redistributables, or part thereof. + +“Contractor(s)” shall mean third party consultants, distributors and contractors +performing services to a Party under applicable contractual arrangement. + +“Customer(s)” shall mean Licensee’s end users to whom Licensee, directly or +indirectly, distributes copies of the Redistributables. + +“Deployment Platforms” shall mean operating systems specified in the License +Certificate, in which the Redistributables can be distributed pursuant to the +terms and conditions of this Agreement. + +“Designated User(s)” shall mean the employee(s) of Licensee or Licensee’s +Affiliates acting within the scope of their employment or Licensee's Contractors +acting within the scope of their services for Licensee and on behalf of +Licensee. Designated Users shall be named in the License Certificate. + +“Development License” shall mean the license needed by the Licensee for each +Designated User to use the Licensed Software under the license grant described +in Section 3.1 of this Agreement. Development Licenses are available separately +for Qt for Application Development (desktop) and Qt for Device Creation +(embedded) products, each product having its designated scope and purpose of +use. Distribution Licenses are always connected to Qt for Device Creation +product only. + +“Development Platforms” shall mean those operating systems specified in the +License Certificate, in which the Licensed Software can be used under the +Development License, but not distributed in any form or used for any other +purpose. + +“Devices” shall mean hardware devices or products that 1) are manufactured +and/or distributed by the Licensee or its Affiliates or Contractors, and 2) +(i) incorporate or integrate the Redistributables or parts thereof; or (ii) do +not incorporate or integrate the Redistributables at the time of distribution, +but where, when used by a Customer, the main user interface or substantial +functionality of such device is provided by Application(s) or otherwise depends +on the Licensed Software. Devices shall be specified in Appendix 2 or in a +quote. + +“Distribution License(s)” shall mean the license required for distribution of +Redistributables in connection with Devices pursuant to license grant described +in Section 3.3 of this Agreement. + +“Distribution License Packs” shall mean set of prepaid Distribution Licenses for +distribution of Redistributables, as defined in The Qt Company’s standard price +list, quote, Purchase Order confirmation or in an appendix hereto, as the case +may be. + +“Intellectual Property Rights” shall mean patents (including utility models), +design patents, and designs (whether or not capable of registration), chip +topography rights and other like protection, copyrights, trademarks, service +marks, trade names, logos or other words or symbols and any other form of +statutory protection of any kind and applications for any of the foregoing as +well as any trade secrets. + +“License Certificate” shall mean a certificate generated by The Qt Company for +each Designated User respectively upon them downloading the Licensed Software. +License Certificate will be available under respective Designated User’s Qt +Account at account.qt.io and it will specify the Designated User, the +Development Platforms, Deployment Platforms and the License Term. The terms of +the License Certificate are considered part of this Agreement and shall be +updated from time to time to reflect any agreed changes to the foregoing terms +relating to Designated User’s rights to the Licensed Software. + +“License Fee” shall mean the fee charged to the Licensee for rights granted +under the terms of this Agreement. + +“License Term” shall mean the agreed validity period of the Development License +of the respective Designated User, during which time the Designated User is +entitled to use the Licensed Software, as set forth in the respective License +Certificate. + +“Licensed Software” shall mean either +(i) Qt for Application Development or +(ii) Qt for Device Creation, and/or +(iii) Qt 3D Studio, and/or +(iv) Qt Design Studio, and/or +(v) selected Add-on Products, if any, depending on which product(s) the + Licensee has purchased under this Agreement, + +as well as corresponding online or electronic documentation, associated media +and printed materials, including the source code, example programs and the +documentation, licensed to the Licensee under this Agreement. Licensed Software +does not include Third Party Software (as defined in Section 4) or Open Source +Qt. The Qt Company may, in the course of its development activities, at its free +and absolute discretion and without any obligation to send or publish any +notifications to the Licensee or in general, make changes, additions or +deletions in the components and functionalities of the Licensed Software, +provided that no such changes, additions or deletions will affect the already +released version of the Licensed Software, but only upcoming version(s). + +“Licensee” shall mean the individual or legal entity that is party to this +Agreement, as identified on the signature page hereof. + +“Licensee’s Records” shall mean books and records that are likely to contain +information bearing on Licensee’s compliance with this Agreement or the payments +due to The Qt Company under this Agreement, including, but not limited to: +assembly logs, sales records and distribution records. + +“Modified Software” shall have the meaning as set forth in Section 2.3. + +“Online Services” shall mean any services or access to systems made available by +The Qt Company to the Licensee over the Internet relating to the Licensed +Software or for the purpose of use by the Licensee of the Licensed Software or +Support. Use of any such Online Services is discretionary for the Licensee and +some of them may be subject to additional fees. + +“Open Source Qt” shall mean the non-commercial Qt computer software products, +licensed under the terms of the GNU Lesser General Public License, version 2.1 +or later (“LGPL”) or the GNU General Public License, version 2.0 or later +(“GPL”). For clarity, Open Source Qt shall not be provided nor governed under +this Agreement. + +”Party” or “Parties” shall mean Licensee and/or The Qt Company. + +“Qt 3D Studio” shall mean all versions of The Qt Company’s Qt 3D Studio, a 3D +user interface design and development environment for rapid designing and +prototyping of animated user interfaces. + +“Qt Design Studio” shall mean all versions of The Qt Company’s Qt Design Studio +tool, a 2D user interface design and development environment for rapid designing +and prototyping of animated user interfaces. + +“Qt for Application Development” shall mean The Qt Company’s productized +offering, which consist of all versions of +(i) Qt Toolkit, and +(ii) Qt Tools/Applications. + +“Qt for Device Creation” shall mean The Qt Company’s productized offering, +which consist of all versions of +(i) Qt for Application Development, and +(ii) Software components specific to embedded software development as set forth + in Appendix 1, Sections 1b and 1d. + +“Qt Toolkit” shall mean the modules defined in Appendix 1, Section 1a. + +“Qt Tools/Applications” shall mean the tools defined in Appendix 1, Section 1c. + +"Redistributables" shall mean the portions of the Licensed Software set forth in +Appendix 1, Section 2 that may be distributed pursuant to the terms of this +Agreement in object code form only, including any relevant documentation. Where +relevant, any reference to Licensed Software in this Agreement shall include and +refer also to Redistributables. + +“Renewal Term” shall mean an extension of previous License Term as agreed +between the Parties. + +“Submitted Modified Software” shall have the meaning as set forth in +Section 2.3. + +“Support” shall mean standard developer support that is provided by The Qt +Company to assist Designated Users in using the Licensed Software in accordance +with The Qt Company’s standard support terms and as further defined in +Section 8 hereunder. + +“Taxes” shall have the meaning set forth in Section 10.5. + +“Term” shall have the meaning set forth in Section 12. + +“The Qt Company” shall mean: +(i) in the event Licensee is an individual residing in the United States or a + legal entity incorporated in the United States or having its headquarters + in the United States, The Qt Company Inc., a Delaware corporation with its + office at 2350 Mission College Blvd., Suite 1020, Santa Clara, CA 95054, + USA.; or +(ii) in the event the Licensee is an individual residing outside of the United + States or a legal entity incorporated outside of the United States or + having its registered office outside of the United States, The Qt Company + Ltd., a Finnish company with its registered office at Bertel Jungin aukio + D3A, 02600 Espoo, Finland. + +"Third Party Software " shall have the meaning set forth in Section 4. + +“Updates” shall mean a release or version of the Licensed Software containing +bug fixes, error corrections and other changes that are generally made available +to users of the Licensed Software that have contracted for Support. Updates are +generally depicted as a change to the digits following the decimal in the +Licensed Software version number. The Qt Company shall make Updates available to +the Licensee under the Support. Updates shall be considered as part of the +Licensed Software hereunder. + +“Upgrades” shall mean a release or version of the Licensed Software containing +enhancements and new features and are generally depicted as a change to the +first digit of the Licensed Software version number. In the event Upgrades are +provided to the Licensee under this Agreement, they shall be considered as part +of the Licensed Software hereunder. + +2. OWNERSHIP +2.1 Ownership of The Qt Company +The Licensed Software is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. The Licensed +Software is licensed, not sold. All The Qt Company's Intellectual Property +Rights are and shall remain the exclusive property of The Qt Company or its +licensors respectively. + +2.2 Ownership of Licensee +All the Licensee's Intellectual Property Rights are and shall remain the +exclusive property of the Licensee or its licensors respectively. All +Intellectual Property Rights to the Modified Software, Applications and Devices +shall remain with the Licensee and no rights thereto shall be granted by the +Licensee to The Qt Company under this Agreement (except as set forth in Section +2.3 below). + +2.3 Modified Software +Licensee may create bug-fixes, error corrections, patches or modifications to +the Licensed Software (“Modified Software”). Such Modified Software may break +the source or binary compatibility with the Licensed Software (including without +limitation through changing the application programming interfaces ("API") or by +adding, changing or deleting any variable, method, or class signature in the +Licensed Software and/or any inter-process protocols, services or standards in +the Licensed Software libraries). To the extent that Licensee’s Modified +Software so breaks source or binary compatibility with the Licensed Software, +Licensee acknowledges that The Qt Company's ability to provide Support may be +prevented or limited and Licensee's ability to make use of Updates may be +restricted. Licensee may, at its sole and absolute discretion, choose to submit +Modified Software to The Qt Company (“Submitted Modified Software”) in +connection with Licensee’s Support request, service request or otherwise. In the +event Licensee does so, then, Licensee hereby grants The Qt Company a +sublicensable, assignable, irrevocable, perpetual, worldwide, non-exclusive, +royalty-free and fully paid-up license, under all of Licensee’s Intellectual +Property Rights, to reproduce, adapt, translate, modify, and prepare derivative +works of, publicly display, publicly perform, sublicense, make available and +distribute such Submitted Modified Software as The Qt Company sees fit at its +free and absolute discretion. + +3. LICENSES GRANTED +3.1 Development with Licensed Software +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable license, valid for the +License Term, to use, modify and copy the Licensed Software by Designated Users +on the Development Platforms for the sole purposes of designing, developing, +demonstrating and testing Application(s) and/or Devices, and to provide thereto +related support and other related services to end-user Customers. Licensee may +install copies of the Licensed Software on an unlimited number of computers +provided that (i) only the Designated Users may use the Licensed Software, and +(ii) all Designated Users must have a valid Development License to use Licensed +Software. Licensee may at any time designate another Designated User to replace +a then-current Designated User by notifying The Qt Company in writing, provided +that any Designated User may be replaced only once during any six-month period. +Upon expiry of the initially agreed License Term, the respective License Terms +shall be automatically extended to one or more Renewal Term(s), unless and until +either Party notifies the other Party in writing that it does not wish to +continue the License Term, such notification to be provided to the other Party +no less than ninety (90) days before expiry of the respective License Term. +Unless otherwise agreed between the Parties, Renewal Term shall be of equal +length with the initial Term. Any such Renewal Term shall be subject to License +Fees agreed between the Parties or, if no advance agreement exists, subject to +The Qt Company’s standard pricing applicable at the commencement date of any +such Renewal Term. + +3.2 Distribution of Applications +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through its Contractors, Redistributables as installed, +incorporated or integrated into Applications for execution on the Deployment +Platforms, and (ii) grant sublicenses to Redistributables, as distributed +hereunder, for Customers solely for Customer’s internal use and to the extent +necessary in order for the Customers to use the Applications for their +respective intended purposes. +Right to distribute the Redistributables as part of an Application as provided +herein is not royalty-bearing but is conditional upon the Licensee having paid +the agreed Development Licenses from The Qt Company before distributing any +Redistributables to Customers. + +3.3 Distribution of Devices +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through one or more tiers of Contractors, +Redistributables as installed, incorporated or integrated, or intended to be +installed, incorporated or integrated into Devices for execution on the +Deployment Platforms, and (ii) grant sublicenses to Redistributables, as +distributed hereunder, for Customers solely for Customer’s internal use and to +the extent necessary in order for the Customers to use the Devices for their +respective intended purposes. +Right to distribute the Redistributables with Devices as provided herein is +conditional upon the Licensee having purchased and paid the appropriate amount +of Development Licenses for Qt for Device Creation product and Distribution +Licenses from The Qt Company before distributing any Redistributables to +Customers. + +3.4 Further Requirements +The licenses granted above in this Section 3 by The Qt Company to Licensee are +conditional and subject to Licensee's compliance with the following terms: +(i) Licensee shall not remove or alter any copyright, trademark or other + proprietary rights notice contained in any portion of the Licensed + Software; +(ii) Applications must add primary and substantial functionality to the + Licensed Software; +(iii) Applications may not pass on functionality which in any way makes it + possible for others to create software with the Licensed Software; + provided however that Licensee may use the Licensed Software's scripting + and QML ("Qt Quick") functionality solely in order to enable scripting, + themes and styles that augment the functionality and appearance of the + Application(s) without adding primary and substantial functionality to + the Application(s); +(iv) Applications must not compete with the Licensed Software; +(v) Licensee shall not use The Qt Company's or any of its suppliers' names, + logos, or trademarks to market Applications, except that Licensee may use + “Built with Qt” logo to indicate that Application(s) was developed using + the Licensed Software; +(vi) Licensee shall not distribute, sublicense or disclose source code of + Licensed Software to any third party (provided however that Licensee may + appoint employee(s) of Contractors as Designated Users to use Licensed + Software pursuant to this Agreement). Such right may be available for the + Licensee subject to a separate software development kit (“SDK”) license + agreement to be concluded with The Qt Company; +(vii) Licensee shall not grant the Customers a right to (i) make copies of the + Redistributables except when and to the extent required to use the + Applications and/or Devices for their intended purpose, (ii) modify the + Redistributables or create derivative works thereof, (iii) decompile, + disassemble or otherwise reverse engineer Redistributables, or (iv) + redistribute any copy or portion of the Redistributables to any third + party, except as part of the onward sale of the Device on which the + Redistributables are installed; +(viii) Licensee shall not and shall cause that its Affiliates or Contractors + shall not a) in any way combine, incorporate or integrate Licensed + Software with, or use Licensed Software for creation of, any software + created with or incorporating Open Source Qt, or b) incorporate or + integrate Applications into a hardware device or product other than a + Device, unless Licensee has received an advance written permission from + The Qt Company to do so. Absent such written permission, any and all + distribution by the Licensee during the Term of a hardware device or + product a) which incorporate or integrate any part of Licensed Software + or Open Source Qt; or b) where the main user interface or substantial + functionality is provided by software built with Licensed Software or + Open Source Qt or otherwise depends on the Licensed Software or Open + Source Qt, shall be considered as a Device distribution under this + Agreement and dependent on compliance thereof (including but not limited + to obligation to pay applicable License Fees for such distribution). + Notwithstanding what is provided above in this sub-section (viii), + Licensee is entitled to use and combine Qt 3D Studio and/or Qt Design + Studio with Open Source Qt (“Combination”) for its internal evaluation + purposes, provided that Licensee shall in no way transfer, publish, + disclose, display or otherwise make available any software or work + resulting from such Combination; +(ix) Licensee shall cause all of its Affiliates and Contractors entitled to + make use of the licenses granted under this Agreement, to be + contractually bound to comply with the relevant terms of this Agreement + and not to use the Licensed Software beyond the terms hereof and for any + purposes other than operating within the scope of their services for + Licensee. Licensee shall be responsible for any and all actions and + omissions of its Affiliates and Contractors relating to the Licensed + Software and use thereof (including but not limited to payment of all + applicable License Fees); +(x) Except when and to the extent explicitly provided in this Section 3, + Licensee shall not transfer, publish, disclose, display or otherwise + make available the Licensed Software; +; and +(xi) Licensee shall not attempt or enlist a third party to conduct or attempt + to conduct any of the above. + +Above terms shall not be applicable if and to the extent they conflict with any +mandatory provisions of any applicable laws. Any use of Licensed Software beyond +the provisions of this Agreement is strictly prohibited and requires an +additional license from The Qt Company. + +4. THIRD PARTY SOFTWARE +The Licensed Software may provide links to third party libraries or code +(collectively "Third Party Software") to implement various functions. Third +Party Software does not comprise part of the Licensed Software. In some cases, +access to Third Party Software may be included in the Licensed Software. Such +Third Party Software will be listed in the ".../src/3rdparty" source tree +delivered with the Licensed Software or documented in the Licensed Software, as +such may be amended from time to time. Licensee acknowledges that use or +distribution of Third Party Software is in all respects subject to applicable +license terms of applicable third party right holders. + +5. PRE-RELEASE CODE +The Licensed Software may contain pre-release code and functionality marked or +otherwise stated as “Technology Preview”, “Alpha”, “Beta” or similar +designation. Such pre-release code may be present in order to provide +experimental support for new platforms or preliminary versions of one or more +new functionalities. The pre-release code may not be at the level of performance +and compatibility of a final, generally available, product offering of the +Licensed Software. The pre-release parts of the Licensed Software may not +operate correctly, may contain errors and may be substantially modified by The +Qt Company prior to the first commercial product release, if any. The Qt Company +is under no obligation to make pre-release code commercially available, or +provide any Support or Updates relating thereto. The Qt Company assumes no +liability whatsoever regarding any pre-release code, but any use thereof is +exclusively at Licensee’s own risk and expense. For clarity, Licensee is +entitled to use such pre-release code pursuant to Section 3, just like other +Licensed Software, provided however that in the event Add-on Products are +included and available as such pre-release code, Licensee’s right to use such +Add-on Products is nevertheless subject to and conditional upon conclusion of +separate agreement with The Qt Company. + +6. LIMITED WARRANTY AND WARRANTY DISCLAIMER +The Qt Company hereby represents and warrants that it has the power and +authority to grant the rights and licenses granted to Licensee under this +Agreement. Except as set forth above, the Licensed Software is licensed to +Licensee "as is" and Licensee’s exclusive remedy and The Qt Company’s entire +liability for errors in the Licensed Software shall be limited, at The Qt +Company’s option, to correction of the error, replacement of the Licensed +Software or return of the applicable fees paid for the defective Licensed +Software for the time period during which the License is not able to utilize the +Licensed Software under the terms of this Agreement. + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF +ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND +NON-INFRINGEMENT WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT +WARRANT THAT THE LICENSED SOFTWARE WILL SATISFY LICENSEE’S REQUIREMENTS OR THAT +IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE +UNINTERRUPTED. ALL USE OF AND RELIANCE ON THE LICENSED SOFTWARE IS AT THE SOLE +RISK OF AND RESPONSIBILITY OF LICENSEE. + +7. INDEMNIFICATION AND LIMITATION OF LIABILITY +7.1 Limitation of Liability +EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II) +BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO +EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT, +LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL, +CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND, +HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT. PARTIES +SPECIFICALLY AGREE THAT LICENSEE’S OBLIGATION TO PAY LICENSE AND OTHER FEES +CORRESPONDING TO ACTUAL USAGE OF LICENSED SOFTWARE HEREUNDER SHALL BE CONSIDERED +AS A DIRECT DAMAGE. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL +MISCONDUCT, AND (II) BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY +APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY’S TOTAL AGGREGATE LIABILITY UNDER +THIS AGREEMENT EXCEED THE AGGREGATE LICENSE FEES PAID OR PAYABLE TO THE QT +COMPANY FROM LICENSEE DURING THE PERIOD OF TWELVE (12) MONTHS IMMEDIATELY +PRECEDING THE EVENT RESULTING IN SUCH LIABILITY. THE PROVISIONS OF THIS SECTION +7 ALLOCATE THE RISKS UNDER THIS AGREEMENT BETWEEN THE QT COMPANY AND LICENSEE +AND THE PARTIES HAVE RELIED UPON THE LIMITATIONS SET FORTH HEREIN IN DETERMINING +WHETHER TO ENTER INTO THIS AGREEMENT. + +7.2 Licensee´s Indemnification +Licensee shall indemnify and hold harmless The Qt Company from and against any +claim, injury, judgment, settlement, loss or expense, including attorneys' fees +related to: (a) Licensee’s misrepresentation in connection with The Qt Company +or the Licensed Software or breach of this Agreement, (b) the Application or +Device (except where such cause of liability is solely attributable to the +Licensed Software). + +8. SUPPORT, UPDATES AND ONLINE SERVICES +Upon due payment of the agreed License Fees the Licensee will be eligible to +receive Support and Updates and to use the Online Services during the License +Term, provided, however, that in the event the License Term is longer than 36 +months, Support is provided only for the first 12 months, unless the Parties +specifically otherwise agree. Unless otherwise decided by The Company at its +free and absolute discretion, Upgrades will not be included in the Support but +may be available subject to additional fees. From time to time The Qt Company +may change the Support terms, provided that during the respective ongoing +License Term the level of Support provided by The Qt Company may not be reduced +without the consent of the Licensee. Unless otherwise agreed, The Qt Company +shall not be responsible for providing any service or support to Customers. + +9. CONFIDENTIALITY +Each Party acknowledges that during the Term of this Agreement each Party may +receive information about the other Party's business, business methods, business +plans, customers, business relations, technology, and other information, +including the terms of this Agreement, that is confidential and of great value +to the other Party, and the value of which would be significantly reduced if +disclosed to third parties (“Confidential Information”). Accordingly, when a +Party (the “Receiving Party”) receives Confidential Information from the other +Party (the “Disclosing Party”), the Receiving Party shall only disclose such +information to employees and Contractors on a need to know basis, and shall +cause its employees and employees of its Affiliates to: (i) maintain any and all +Confidential Information in confidence; (ii) not disclose the Confidential +Information to a third party without the Disclosing Party's prior written +approval; and (iii) not, directly or indirectly, use the Confidential +Information for any purpose other than for exercising its rights and fulfilling +its responsibilities pursuant to this Agreement. Each Party shall take +reasonable measures to protect the Confidential Information of the other Party, +which measures shall not be less than the measures taken by such Party to +protect its own confidential and proprietary information. Obligation of +confidentiality shall not apply to information that (i) is or becomes generally +known to the public through no act or omission of the Receiving Party; (ii) was +in the Receiving Party's lawful possession prior to the disclosure hereunder and +was not subject to limitations on disclosure or use; (iii) is developed +independently by employees or Contractors of the Receiving Party or other +persons working for the Receiving Party who have not had access to the +Confidential Information of the Disclosing Party, as proven by the written +records of the Receiving Party; (iv) is lawfully disclosed to the Receiving +Party without restrictions, by a third party not under an obligation of +confidentiality; or (v) the Receiving Party is legally compelled to disclose, in +which case the Receiving Party shall notify the Disclosing Party of such +compelled disclosure and assert the privileged and confidential nature of the +information and cooperate fully with the Disclosing Party to limit the scope of +disclosure and the dissemination of disclosed Confidential Information to the +minimum extent necessary. The obligations under this Section 9 shall continue to +remain in force for a period of five (5) years after the last disclosure, and, +with respect to trade secrets, for so long as such trade secrets are protected +under applicable trade secret laws. + +10. FEES, DELIVERY AND PAYMENT +10.1 License Fees +License Fees are described in The Qt Company’s standard price list, quote or +Purchase Order confirmation or in an appendix hereto, as the case may be. The +License Fees shall not be refunded or claimed as a credit in any event or for +any reason whatsoever. + +10.2 Ordering Licenses +Licensee may purchase Development Licenses and Distribution Licenses pursuant to +agreed pricing terms or, if no specific pricing terms have been agreed upon, at +The Qt Company's standard pricing terms applicable at the time of purchase. +Licensee shall submit all purchase orders for Development Licenses and +Distribution Licenses to The Qt Company by email or any other method acceptable +to The Qt Company (each such order is referred to herein as a “Purchase Order”) +for confirmation, whereupon the Purchase Order shall become binding between the +Parties. + +10.3 Distribution License Packs +Unless otherwise agreed, Distribution Licenses shall be purchased by way of +Distribution License Packs. Upon due payment of the ordered Distribution License +Pack(s), the Licensee will have an account of Distribution Licenses available +for installing, bundling or integrating (all jointly “installing”) the +Redistributables with the Devices or for otherwise distributing the +Redistributables in accordance with this Agreement. Each time Licensee +“installs” or distributes a copy of Redistributables, then one Distribution +License is used, and Licensee’s account of available Distribution Licenses is +decreased accordingly. Licensee may “install” copies of the Redistributables so +long as Licensee has Distribution Licenses remaining on its account. +Redistributables will be deemed to have been “installed” into a Device when one +of the following circumstances shall have occurred: a) the Redistributables have +been loaded onto the Device and used outside of the Licensee’s premises or b) +the Device has been fully tested and placed into Licensee's inventory (or sold) +for the first time (i.e., Licensee will not be required to use (or pay for) more +than one Distribution License for each individual Device, e.g. in a situation +where a Device is returned to Licensee's inventory after delivery to a +distributor or sale to a Customer). In addition, if Licensee includes a back-up +copy of the Redistributables on a CD-ROM or other storage medium along with the +product, that backup copy of the Redistributables will not be deemed to have +been “installed” and will not require an additional Distribution License. + +10.4 Payment Terms +License Fees and any other charges under this Agreement shall be paid by +Licensee no later than thirty (30) days from the date of the applicable invoice +from The Qt Company. The Qt Company will submit an invoice to Licensee after the +date of this Agreement and/or after The Qt Company receives a Purchase Order +from Licensee. A late payment charge of the lower of (a) one percent per month; +or (b) the interest rate stipulated by applicable law, shall be charged on any +unpaid balances that remain past due. The Qt Company shall have the right to +suspend, terminate or withhold grants of all rights to the Licensed Software +hereunder, including but not limited to the Developer License, Distribution +License, and Support, should Licensee fail to make payment in timely fashion. + +10.5 Taxes +All License Fees and other charges payable hereunder are gross amounts but +exclusive of any value added tax, use tax, sales tax and other taxes, duties or +tariffs (“Taxes”). Such applicable Taxes shall be paid by Licensee, or, where +applicable, in lieu of payment of such Taxes, Licensee shall provide an +exemption certificate to The Qt Company and any applicable authority. + +11 RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS +11.1 Licensee’s Record-keeping +Licensee shall at all times maintain accurate and up-to-date written records of +Licensee’s activities related to the use of Licensed Software and distribution +of Redistributables. The records shall be adequate to determine Licensee’s +compliance with the provisions of this Agreement and to demonstrate the number +of Designated Users and Redistributables distributed by Licensee. The records +shall conform to good accounting practices reasonably acceptable to The Qt +Company. Licensee shall, within thirty (30) days from receiving The Qt Company’s +request to that effect, deliver to The Qt Company a report on Licensee’s usage +of Licensed Software, such report to contain information, in sufficient detail, +on (i) amount of users working with Licensed Software, (ii) copies of +Redistributables distributed by Licensee during that calendar quarter, (iii) +number of undistributed copies of Redistributables and corresponding number of +unused Distribution Licenses remaining on Licensee’s account, and (iv) any other +information as The Qt Company may reasonably require from time to time. + +11.2. The Qt Company’s Audit Rights +The Qt Company or an independent auditor acting on behalf of The Qt Company’s, +may, upon at least five (5) business days’ prior written notice and at its +expense, audit Licensee with respect to the use of the Redistributables, but not +more frequently than once during each 6-month period. Such audit may be +conducted by mail, electronic means or through an in-person visit to Licensee’s +place of business. Any such in-person audit shall be conducted during regular +business hours at Licensee's facilities and shall not unreasonably interfere +with Licensee's business activities. The Qt Company or the independent auditor +acting on behalf of The Qt Company shall be entitled to inspect Licensee’s +Records. All such Licensee’s Records and use thereof shall be subject to an +obligation of confidentiality under this Agreement. If an audit reveals that +Licensee is using the Licensed Software beyond scope of the licenses Licensee +has paid for, Licensee agrees to immediately pay The Qt Company any amounts owed +for such unauthorized use. +In addition, in the event the audit reveals a material violation of the terms of +this Agreement (underpayment of more than 5% of License Fees shall always be +deemed a material violation for purposes of this section), then the Licensee +shall pay The Qt Company's reasonable cost of conducting such audit. + +12 TERM AND TERMINATION +12.1 Term +This Agreement shall enter into force upon due acceptance by both Parties and +remain in force for as long as there is any Development License(s) in force +(“Term”), unless and until terminated pursuant to the terms of this Section 12. + +12.2 Termination by The Qt Company +The Qt Company shall have the right to terminate this Agreement upon thirty (30) +days prior written notice if the Licensee is in material breach of any +obligation of this Agreement and fails to remedy such breach within such notice +period. + +12.3 Mutual Right to Terminate +Either Party shall have the right to terminate this Agreement immediately upon +written notice in the event that the other Party becomes insolvent, files for +any form of bankruptcy, makes any assignment for the benefit of creditors, has a +receiver, administrative receiver or officer appointed over the whole or a +substantial part of its assets, ceases to conduct business, or an act equivalent +to any of the above occurs under the laws of the jurisdiction of the other +Party. + +12.4 Parties´ Rights and Duties upon Termination +Upon expiry or termination of the Agreement Licensee shall cease and shall cause +all Designated Users (including those of its Affiliates’ and Contractors’) to +cease using the Licensed Software and distribution of the Redistributables under +this Agreement. +Notwithstanding the above, in the event the Agreement expires or is terminated: +(i) as a result of The Qt Company choosing not to renew the Development + License(s) as set forth in Section 3.1, then all valid licenses possessed + by the Licensee at such date shall be extended to be valid in perpetuity + under the terms of this Agreement and Licensee is entitled to purchase + additional licenses as set forth in Section 10.2; or +(ii) for reason other than by The Qt Company pursuant to item (i) above or + pursuant to Section 12.2, then the Licensee is entitled, for a period of + six (6) months after the effective date of termination, to continue + distribution of Devices under the Distribution Licenses paid but unused at + such effective date of termination. Upon any such termination the Licensee + shall destroy or return to The Qt Company all copies of the Licensed + Software and all related materials and will certify the same to The Qt + Company upon its request, provided however that Licensee may retain and + exploit such copies of the Licensed Software as it may reasonably require + in providing continued support to Customers. +Expiry or termination of this Agreement for any reason whatsoever shall not +relieve Licensee of its obligation to pay any License Fees accrued or payable to +The Qt Company prior to the effective date of termination, and Licensee shall +immediately pay to The Qt Company all such fees upon the effective date of +termination. Termination of this Agreement shall not affect any rights of +Customers to continue use of Applications and Devices (and therein incorporated +Redistributables). + +12.5 Extension in case of bankruptcy +In the event The Qt Company is declared bankrupt under a final, non-cancellable +decision by relevant court of law, and this Agreement is not, at the date of +expiry of the Development License(s) pursuant to Section 3.1, assigned to party, +who has assumed The Qt Company’s position as a legitimate licensor of Licensed +Software under this Agreement, then all valid licenses possessed by the Licensee +at such date of expiry, and which the Licensee has not notified for expiry, +shall be extended to be valid in perpetuity under the terms of this Agreement. + +13. GOVERNING LAW AND LEGAL VENUE +In the event this Agreement is in the name of The Qt Company Inc., a Delaware +Corporation, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of the State of California, USA, excluding its choice of law + provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any dispute, claim or controversy arising out of or relating to this + Agreement or the breach, termination, enforcement, interpretation or + validity thereof, including the determination of the scope or + applicability of this Agreement to arbitrate, shall be determined by + arbitration in San Francisco, USA, before one arbitrator. The arbitration + shall be administered by JAMS pursuant to JAMS' Streamlined Arbitration + Rules and Procedures. Judgment on the Award may be entered in any court + having jurisdiction. This Section shall not preclude parties from seeking + provisional remedies in aid of arbitration from a court of appropriate + jurisdiction. +In the event this Agreement is in the name of The Qt Company Ltd., a Finnish +Company, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of Finland, excluding its choice of law provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any disputes, controversy or claim arising out of or relating to this + Agreement, or the breach, termination or validity thereof shall be shall + be finally settled by arbitration in accordance with the Arbitration Rules + of Finland Chamber of Commerce. The arbitration tribunal shall consist of + one (1), or if either Party so requires, of three (3), arbitrators. The + award shall be final and binding and enforceable in any court of competent + jurisdiction. The arbitration shall be held in Helsinki, Finland and the + process shall be conducted in the English language. This Section shall not + preclude parties from seeking provisional remedies in aid of arbitration + from a court of appropriate jurisdiction. + +14. GENERAL PROVISIONS +14.1 No Assignment +Except in the case of a merger or sale of substantially all of its corporate +assets, Licensee shall not be entitled to assign or transfer all or any of its +rights, benefits and obligations under this Agreement without the prior written +consent of The Qt Company, which shall not be unreasonably withheld or delayed. +The Qt Company shall be entitled to freely assign or transfer any of its rights, +benefits or obligations under this Agreement. + +14.2 No Third Party Representations +Licensee shall make no representations or warranties concerning the Licensed +Software on behalf of The Qt Company. Any representation or warranty Licensee +makes or purports to make on The Qt Company’s behalf shall be void as to The Qt +Company. + +14.3 Surviving Sections +Any terms and conditions that by their nature or otherwise reasonably should +survive termination of this Agreement shall so be deemed to survive. + +14.4 Entire Agreement +This Agreement, the exhibits hereto, the License Certificate and any applicable +Purchase Order constitute the complete agreement between the Parties and +supersedes all prior or contemporaneous discussions, representations, and +proposals, written or oral, with respect to the subject matters discussed +herein. +In the event of any conflict or inconsistency between this Agreement and any +Purchase Order, the terms of this Agreement will prevail over the terms of the +Purchase Order with respect to such conflict or inconsistency. +Parties specifically acknowledge and agree that this Agreement prevails over any +click-to-accept or similar agreements the Designated Users may need to accept +online upon download of the Licensed Software, as may be required by The Qt +Company’s applicable processes relating to Licensed Software. + +14.5 Modifications +No modification of this Agreement shall be effective unless contained in a +writing executed by an authorized representative of each Party. No term or +condition contained in Licensee's Purchase Order shall apply unless expressly +accepted by The Qt Company in writing. + +14.6 Force Majeure +Except for the payment obligations hereunder, neither Party shall be liable to +the other for any delay or non-performance of its obligations hereunder in the +event and to the extent that such delay or non-performance is due to an event of +act of God, terrorist attack or other similar unforeseeable catastrophic event +that prevents either Party for fulfilling its obligations under this Agreement +and which such Party cannot avoid or circumvent (“Force Majeure Event”). If the +Force Majeure Event results in a delay or non-performance of a Party for a +period of three (3) months or longer, then either Party shall have the right to +terminate this Agreement with immediate effect without any liability (except for +the obligations of payment arising prior to the event of Force Majeure) towards +the other Party. + +14.7 Notices +Any notice given by one Party to the other shall be deemed properly given and +deemed received if specifically acknowledged by the receiving Party in writing +or when successfully delivered to the recipient by hand, fax, or special courier +during normal business hours on a business day to the addresses specified for +each Party on the signature page. Each communication and document made or +delivered by one Party to the other Party pursuant to this Agreement shall be in +the English language. + +14.8 Export Control +Licensee acknowledges that the Redistributables may be subject to export control +restrictions under the applicable laws of respective countries. Licensee shall +fully comply with all applicable export license restrictions and requirements as +well as with all laws and regulations relating to the Redistributables and +exercise of licenses hereunder and shall procure all necessary governmental +authorizations, including without limitation, all necessary licenses, approvals, +permissions or consents, where necessary for the re-exportation of the +Redistributables, Applications and/or Devices. + +14.9 No Implied License +There are no implied licenses or other implied rights granted under this +Agreement, and all rights, save for those expressly granted hereunder, shall +remain with The Qt Company and its licensors. In addition, no licenses or +immunities are granted to the combination of the Licensed Software with any +other software or hardware not delivered by The Qt Company under this Agreement. + +14.10 Attorney Fees +The prevailing Party in any action to enforce this Agreement shall be entitled +to recover its attorney’s fees and costs in connection with such action. + +14.11 Severability +If any provision of this Agreement shall be adjudged by any court of competent +jurisdiction to be unenforceable or invalid, that provision shall be limited or +eliminated to the minimum extent necessary so that this Agreement shall +otherwise remain in full force and effect and enforceable. + + + +APPENDICES +The Agreement includes Appendix 1 as shown below. In addition, the Agreement may +include one or more of the Appendices 3-5 listed below depending on the +product(s) purchased by the Licensee, what is stated in the quote or invoice, +and/or what is stated on the License Certificate. + +  +APPENDIX 1: LICENSED SOFTWARE +1a. Licensed Software - Qt Toolkit +Module Description +Qt Core Core non-graphical classes used by other modules. +Qt GUI Base classes for graphical user interface (GUI) + components. +Qt Multimedia Classes for audio, video and camera functionality. +Qt Multimedia Widgets Widget-based classes for implementing multimedia + functionality. +Qt Network Classes to make network programming easier and more + portable. +Qt QML Classes for QML and JavaScript languages. +Qt Quick A declarative framework for building highly dynamic + applications with custom user interfaces. +Qt Quick Controls 2 Provides lightweight QML types for creating + performant user interfaces for desktop, embedded, and + mobile devices. +Qt Quick Dialogs Types for creating and interacting with system + dialogs from a Qt Quick application. +Qt Quick Layouts Layouts are items that are used to arrange Qt Quick 2 + based items in the user interface. +Qt Quick Test A unit test framework for QML applications. +Qt SQL Classes for database integration using SQL. +Qt Test Classes for unit testing Qt applications and + libraries. +Qt Widgets Classes to extend Qt GUI with C++ widgets. +Active Qt Classes for applications which use ActiveX and COM +Qt 3D Functionality for near-realtime simulation systems + with support for 2D and 3D rendering. +Qt Android Extras Provides platform-specific APIs for Android. +Qt Bluetooth Provides access to Bluetooth hardware. +Qt Canvas 3D Enables OpenGL-like 3D drawing calls from Qt Quick + applications using JavaScript. +Qt Concurrent Classes for writing multi-threaded programs without + using low-level threading primitives. +Qt D-Bus Classes for inter-process communication over the + D-Bus protocol. +Qt Gamepad Enables Qt applications to support the use of gamepad + hardware. +Qt Graphical Effects Graphical effects for use with Qt Quick 2. +Qt Help Classes for integrating documentation into + applications, similar to Qt Assistant. +Qt Image Formats Plugins for additional image formats: TIFF, MNG, TGA, + WBMP. +Qt Location Displays map, navigation, and place content in a QML + application. +Qt Mac Extras Provides platform-specific APIs for macOS. +Qt Network Authorization Provides support for OAuth-based authorization to + online services. +Qt NFC Provides access to Near-Field communication (NFC) + hardware. +Qt Platform Headers Provides classes that encapsulate platform-specific + information. +Qt Positioning Provides access to position, satellite and area + monitoring classes. +Qt Print Support Classes to make printing easier and more portable. +Qt Purchasing Enables in-app purchase of products in Qt + applications. +Qt for Python Python bindings for Qt. +Qt Quick Controls Reusable Qt Quick based UI controls to create classic + desktop-style user interfaces. +Qt Quick Extras Provides a specialized set of controls that can be + used to build interfaces in Qt Quick. +Qt Quick Widgets Provides a C++ widget class for displaying a Qt + Quick user interface. +Qt SCXML Provides classes and tools for creating state + machines from SCXML files. +Qt Sensors Provides access to sensor hardware and motion gesture + recognition. +Qt Serial Bus Provides access to serial industrial bus interface. +Qt Serial Port Provides access to hardware and virtual serial ports. +Qt Speech Provides support for accessibility features such as + text-to-speech. +Qt SVG Classes for displaying the contents of SVG files. +Qt UI Tools Classes for loading QWidget based forms created in Qt + Designer dynamically, at runtime. +Qt WebChannel Provides access to QObject or QML objects from HTML + clients for seamless integration of Qt applications + with HTML/JavaScript clients. +Qt WebEngine Classes and functions for embedding web content in + applications using the Chromium browser project. +Qt WebSockets Provides WebSocket communication. +Qt WebView Displays web content in a QML application by using + APIs native to the platform. +Qt Windows Extras Provides platform-specific APIs for Windows. +Qt X11 Extras Provides platform-specific APIs for X11. +Qt XML C++ implementations of SAX and DOM. +Qt XML Patterns Support for XPath, XQuery, XSLT and XML schema + validation. +Qt Wayland Compositor Provides a framework to develop a Wayland compositor. +Qt Charts UI Components for displaying charts. +Qt Data Visualization UI Components for creating 3D data visualizations. +Qt Virtual Keyboard A framework for implementing different input methods + as well as a QML virtual keyboard. + +1b. Licensed software – Embedded software development libraries +Module Description +Boot 2 Qt stack Yocto based Embedded Linux stack for selected + target hardware +Qt OTA Client-side capability for device image + updates Over The Air. +Device Utilities Collection of API’s to manage the device; + E.g. display, WiFi and Bluetooth settings. +Qt Debugging Bridge (QDB) Daemon Enables host-target deployment, debugging, + profiling and other features over USB. Up to + developer to decide if this is left in the + final solution. + +1c. Licensed Software - Qt Tools/Applications +Tool Description +Qt Creator The integrated development environment for Qt. +Qt Designer Qt tool for designing and building graphical user interfaces. +Qt Linguist Tool used to add translations to Qt applications. +Qt Assistant Tool for viewing online documentation in Qt help-file format. +Qmake Utility tool used to automate the generation of make files. +uic User interface compiler for the Qt GUI toolkit. +rcc Resource compiler used for embedding resources into Qt + applications. +lupdate Tool that finds the translatable strings in the specified source, + header and Qt Designer interface files, and produces or updates + translation files. +lrelease Tool that produces translation files in the compact binary format + used by localized Qt applications. +qlalr Qt parser generator tool. +qdoc Configurable documentation generation tool. +qmlscene QML launcher tool +qmlviewer QML launcher tool + +1d. Licensed software –Qt Tools/Applications specific to embedded software +development +Tool Description +Target toolchains Cross compilation toolchains for + supported target devices and operating + systems +Qt Debugging Bridge (QDB) Host Tools Enables deployment, debugging, + profiling and other features over USB + from development host PC to target + device. +qtconfig-gui Qt Lite Configurator tool graphical + interface +Qt Emulator Qt emulator + +2. Parts of the Licensed Software that are permitted for distribution in +object-code form only (“Redistributables”) under this Agreement: + +2a. Qt for Application Development +(i) The Licensed Software's Qt Toolkit libraries defined in 1a +(ii) The Licensed Software's installer framework + +2b. Qt for Device Creation +(i) Qt for Application Development Redistributables defined in 2a +(ii) The Licensed Software’s Embedded software development libraries defined in + 1b + +2c. Qt 3D Studio +The Licensed Software’s Qt 3D Studio Runtime (“Qt53DStudioRuntime2”) + + +APPENDIX 3: ADDITIONS TO LICENSED SOFTWARE +In addition to what is provided under the definition of the Licensed Software, +Parties agree that Licensed Software shall also include the following additional +software products of The Qt Company if included in the quote / invoice: +Qt for Automation + - MQTT software protocol libraries + - KNX software protocol libraries + - OPCUA (open source backend) + - OPCUA (Unified Automation backend) +Qt Safe Renderer + - Qt Safe Renderer library +Qt Application Manager + - Qt Application Manager library with Qt Creator integration + +All the above is considered as Redistributables and subject to applicable +provisions and limitations including but not limited to what is defined in +Section 3. + + +APPENDIX 4: SMALL BUSINESS AND START-UP APPENDIX +The provisions of this Appendix 4 are applicable for Start-up Companies and for +the Evaluation Term. +For the purpose of this Appendix 4, the following additional definitions shall +be applicable: + “Trial Term” shall mean a period of twelve (12) months. + “Start-up Company” means a company with a maximum annual revenue, including + funding, equivalent to 100,000 USD (in applicable currency) during a respective + calendar year, as evidenced by duly audited records of the Licensee and + approved by The Qt Company. + +During the Trial Term, Section 3 shall apply with following modifications +(“Trial Term Modifications”): + - Licenses granted under Sections 3.1 and 3.2 shall be free of any charge. For + clarity, License for distribution of Devices pursuant to Section 3.3 is + subject to applicable License Fee for necessary Distribution Licenses; + - Development License under Section 3.1 is limited to a maximum of three (3) + Designated Users; and + - Support is available subject to availability, as judged by The Qt Company at + its free and absolute discretion. + +Upon expiry of the Trial Term: + a) This Appendix 4 is terminated, Trial Term Modifications cease to remain in + force, Licensee’s Development Licenses shall be automatically converted into + licenses subject to a License Fee (in the amount specified in the quote or + in Appendix 2 and payable with a 30-day payment term) and Licensee’s rights + and obligations under this Agreement shall continue to remain in force under + the standard provisions of the Agreement, unless the Licensee notifies The + Qt Company in writing no less than ninety (90) days before such expiry date + that Licensee does not agree to such continuance, in which event the + Agreement, and all rights of the Licensee thereunder, shall expire; provided + however that + b) in the event the Licensee still qualifies as a Start-up Company, the + Licensee has an option (“Option”), instead of what is stated in item a) + above, to extend the Trial Term renewal is limited to one time and total + duration of Trial Terms thus to 24 months after the effective date. Licensee + shall notify The Qt Company in writing no less than ninety (90) days before + the expiry date, if Licensee wish to exercise the Option. + + +APPENDIX 5: NON-COMMERCIAL USE APPENDIX +The provisions of this Appendix 5 are applicable for non-commercial use of the +Licensed Software by the Licensee. +For the purpose of this Appendix 5, the following additional definitions +(replacing the relevant definition of the Agreement, where applicable) shall be +applicable: + “Demo Units” shall mean (i) hardware development platform, which incorporates + the Licensed Software along with Licensee’s software and/or hardware, and + (ii) prototype versions of Applications or Devices. + “Designated User(s)” shall mean the employees and students of the Licensee. + “Licensee Products” shall mean Applications and/or Devices. + “Permitted Purpose” shall mean (i) Licensee’s internal evaluation and testing + of Licensed Software, (ii) building Demo Units as well as (iii) educational + use. + “Term” shall mean a period of twelve (12) months or any such other period as + may be agreed between the Parties. + +For the purpose of this Appendix 5, the following changes shall be agreed with +respect to relevant Sections of the Agreement: + I. Recital (A) shall be replaced in its entirety to read as follows: “(A) + Licensee wishes to use the Licensed Software for the Permitted Purpose.” + II. Section 3.1 shall be replaced in its entirety to read as follows: + “The Qt Company grants to Licensee a personal, non-exclusive, + non-transferable, revocable, royalty-free license, valid for the Term, to + use, modify and copy the Licensed Software solely for the Permitted + Purpose. Licensee may install copies of the Licensed Software on an + unlimited number of computers provided that only Designated Users may use + the Licensed Software. Licensee may demonstrate the Demo Units, provided + that such demonstrations must be conducted by Licensee, and the Demo Units + must remain in Licensee’s possession and under Licensee’s control at all + times. For clarity, this Agreement does not (i) entitle Licensee to use + Licensed Software to create Applications or Devices (other than prototypes + thereof) or (ii) carry any distribution rights to Licensee, but such + rights are subject to and conditional upon conclusion of a separate + license agreement with The Qt Company.” + III. Sections 3.2, 3.3, 8 and 10 shall be deleted. + IV. Section 3.4 shall be replaced in its entirety to read as follows: + “Licensee shall not: + - remove or alter any copyright, trademark or other proprietary rights + notice contained in any portion of the Licensed Software; + - transfer, publish, sublicense, disclose, display or otherwise make + the Licensed Software available to any third party (except that + Licensee may demonstrate the Demo Units pursuant to Section 3.1); + - in any way combine, incorporate or integrate Licensed Software with, or + use Licensed Software for creation of, any software created with or + incorporating Open Source Qt; + Licensee shall cause all Designated Users who make use of the licenses + granted under this Agreement, to be contractually bound to comply with + the relevant terms of this Agreement and not to use the Licensed + Software beyond the terms hereof. Licensee shall be responsible for any + and all actions and omissions of its Designated Users relating to the + Licensed Software and use thereof. Any use of Licensed Software beyond + the provisions of this Agreement is strictly prohibited and requires an + additional license from The Qt Company.” + V. Section 12 shall be replaced in its entirety to read as follows: + “This Agreement shall enter into force upon due acceptance by both Parties + and remain in force for the Term, unless and until terminated pursuant to + the terms of Section 12. Upon termination of the Agreement, Licensee shall + cease using the Licensed Software. All other copies of Licensed Software + in the possession or control of Licensee must be erased or destroyed. An + officer of Licensee must, upon request, promptly deliver to The Qt Company + a written confirmation that this has occurred.” + +Except for the modifications specified above, this Appendix carries no change to +the terms of the Agreement which shall remain in full force. diff --git a/.QT-FOR-AUTOMATION-LICENSE-AGREEMENT b/.QT-FOR-AUTOMATION-LICENSE-AGREEMENT new file mode 100644 index 0000000..3cbd6af --- /dev/null +++ b/.QT-FOR-AUTOMATION-LICENSE-AGREEMENT @@ -0,0 +1,1089 @@ +QT LICENSE AGREEMENT +Agreement version 4.1 +This License Agreement (“Agreement”) is a legal agreement between The Qt Company +(as defined below) and the Licensee (as defined below) for the license of +Licensed Software (as defined below). Capitalized terms used herein are defined +in Section 1. +WHEREAS: + +(A) Licensee wishes to use the Licensed Software for the purpose of developing + and distributing Applications and/or Devices; and +(B) The Qt Company is willing to grant the Licensee a right to use Licensed + Software for such purpose pursuant to term and conditions of this Agreement. + +NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS: + +1. DEFINITIONS +“Affiliate” of a Party shall mean an entity (i) which is directly or indirectly +controlling such Party; (ii) which is under the same direct or indirect +ownership or control as such Party; or (iii) which is directly or indirectly +owned or controlled by such Party. For these purposes, an entity shall be +treated as being controlled by another if that other entity has fifty percent +(50 %) or more of the votes in such entity, is able to direct its affairs and/or +to control the composition of its board of directors or equivalent body. + +“Add-on Products” shall mean The Qt Company’s specific add-on software products +(for example Qt Safe Renderer, Qt for Automation, Qt Application Manager), which +are not licensed as part of The Qt Company’s standard offering, but shall be +included into the scope of Licensed Software only if so specifically agreed +between the Parties. + +“Applications” shall mean Licensee's software products created using the +Licensed Software, which may include the Redistributables, or part thereof. + +“Contractor(s)” shall mean third party consultants, distributors and contractors +performing services to a Party under applicable contractual arrangement. + +“Customer(s)” shall mean Licensee’s end users to whom Licensee, directly or +indirectly, distributes copies of the Redistributables. + +“Deployment Platforms” shall mean operating systems specified in the License +Certificate, in which the Redistributables can be distributed pursuant to the +terms and conditions of this Agreement. + +“Designated User(s)” shall mean the employee(s) of Licensee or Licensee’s +Affiliates acting within the scope of their employment or Licensee's Contractors +acting within the scope of their services for Licensee and on behalf of +Licensee. Designated Users shall be named in the License Certificate. + +“Development License” shall mean the license needed by the Licensee for each +Designated User to use the Licensed Software under the license grant described +in Section 3.1 of this Agreement. Development Licenses are available separately +for Qt for Application Development (desktop) and Qt for Device Creation +(embedded) products, each product having its designated scope and purpose of +use. Distribution Licenses are always connected to Qt for Device Creation +product only. + +“Development Platforms” shall mean those operating systems specified in the +License Certificate, in which the Licensed Software can be used under the +Development License, but not distributed in any form or used for any other +purpose. + +“Devices” shall mean hardware devices or products that 1) are manufactured +and/or distributed by the Licensee or its Affiliates or Contractors, and 2) +(i) incorporate or integrate the Redistributables or parts thereof; or (ii) do +not incorporate or integrate the Redistributables at the time of distribution, +but where, when used by a Customer, the main user interface or substantial +functionality of such device is provided by Application(s) or otherwise depends +on the Licensed Software. Devices shall be specified in Appendix 2 or in a +quote. + +“Distribution License(s)” shall mean the license required for distribution of +Redistributables in connection with Devices pursuant to license grant described +in Section 3.3 of this Agreement. + +“Distribution License Packs” shall mean set of prepaid Distribution Licenses for +distribution of Redistributables, as defined in The Qt Company’s standard price +list, quote, Purchase Order confirmation or in an appendix hereto, as the case +may be. + +“Intellectual Property Rights” shall mean patents (including utility models), +design patents, and designs (whether or not capable of registration), chip +topography rights and other like protection, copyrights, trademarks, service +marks, trade names, logos or other words or symbols and any other form of +statutory protection of any kind and applications for any of the foregoing as +well as any trade secrets. + +“License Certificate” shall mean a certificate generated by The Qt Company for +each Designated User respectively upon them downloading the Licensed Software. +License Certificate will be available under respective Designated User’s Qt +Account at account.qt.io and it will specify the Designated User, the +Development Platforms, Deployment Platforms and the License Term. The terms of +the License Certificate are considered part of this Agreement and shall be +updated from time to time to reflect any agreed changes to the foregoing terms +relating to Designated User’s rights to the Licensed Software. + +“License Fee” shall mean the fee charged to the Licensee for rights granted +under the terms of this Agreement. + +“License Term” shall mean the agreed validity period of the Development License +of the respective Designated User, during which time the Designated User is +entitled to use the Licensed Software, as set forth in the respective License +Certificate. + +“Licensed Software” shall mean either +(i) Qt for Application Development or +(ii) Qt for Device Creation, and/or +(iii) Qt 3D Studio, and/or +(iv) Qt Design Studio, and/or +(v) selected Add-on Products, if any, depending on which product(s) the + Licensee has purchased under this Agreement, + +as well as corresponding online or electronic documentation, associated media +and printed materials, including the source code, example programs and the +documentation, licensed to the Licensee under this Agreement. Licensed Software +does not include Third Party Software (as defined in Section 4) or Open Source +Qt. The Qt Company may, in the course of its development activities, at its free +and absolute discretion and without any obligation to send or publish any +notifications to the Licensee or in general, make changes, additions or +deletions in the components and functionalities of the Licensed Software, +provided that no such changes, additions or deletions will affect the already +released version of the Licensed Software, but only upcoming version(s). + +“Licensee” shall mean the individual or legal entity that is party to this +Agreement, as identified on the signature page hereof. + +“Licensee’s Records” shall mean books and records that are likely to contain +information bearing on Licensee’s compliance with this Agreement or the payments +due to The Qt Company under this Agreement, including, but not limited to: +assembly logs, sales records and distribution records. + +“Modified Software” shall have the meaning as set forth in Section 2.3. + +“Online Services” shall mean any services or access to systems made available by +The Qt Company to the Licensee over the Internet relating to the Licensed +Software or for the purpose of use by the Licensee of the Licensed Software or +Support. Use of any such Online Services is discretionary for the Licensee and +some of them may be subject to additional fees. + +“Open Source Qt” shall mean the non-commercial Qt computer software products, +licensed under the terms of the GNU Lesser General Public License, version 2.1 +or later (“LGPL”) or the GNU General Public License, version 2.0 or later +(“GPL”). For clarity, Open Source Qt shall not be provided nor governed under +this Agreement. + +”Party” or “Parties” shall mean Licensee and/or The Qt Company. + +“Qt 3D Studio” shall mean all versions of The Qt Company’s Qt 3D Studio, a 3D +user interface design and development environment for rapid designing and +prototyping of animated user interfaces. + +“Qt Design Studio” shall mean all versions of The Qt Company’s Qt Design Studio +tool, a 2D user interface design and development environment for rapid designing +and prototyping of animated user interfaces. + +“Qt for Application Development” shall mean The Qt Company’s productized +offering, which consist of all versions of +(i) Qt Toolkit, and +(ii) Qt Tools/Applications. + +“Qt for Device Creation” shall mean The Qt Company’s productized offering, +which consist of all versions of +(i) Qt for Application Development, and +(ii) Software components specific to embedded software development as set forth + in Appendix 1, Sections 1b and 1d. + +“Qt Toolkit” shall mean the modules defined in Appendix 1, Section 1a. + +“Qt Tools/Applications” shall mean the tools defined in Appendix 1, Section 1c. + +"Redistributables" shall mean the portions of the Licensed Software set forth in +Appendix 1, Section 2 that may be distributed pursuant to the terms of this +Agreement in object code form only, including any relevant documentation. Where +relevant, any reference to Licensed Software in this Agreement shall include and +refer also to Redistributables. + +“Renewal Term” shall mean an extension of previous License Term as agreed +between the Parties. + +“Submitted Modified Software” shall have the meaning as set forth in +Section 2.3. + +“Support” shall mean standard developer support that is provided by The Qt +Company to assist Designated Users in using the Licensed Software in accordance +with The Qt Company’s standard support terms and as further defined in +Section 8 hereunder. + +“Taxes” shall have the meaning set forth in Section 10.5. + +“Term” shall have the meaning set forth in Section 12. + +“The Qt Company” shall mean: +(i) in the event Licensee is an individual residing in the United States or a + legal entity incorporated in the United States or having its headquarters + in the United States, The Qt Company Inc., a Delaware corporation with its + office at 2350 Mission College Blvd., Suite 1020, Santa Clara, CA 95054, + USA.; or +(ii) in the event the Licensee is an individual residing outside of the United + States or a legal entity incorporated outside of the United States or + having its registered office outside of the United States, The Qt Company + Ltd., a Finnish company with its registered office at Bertel Jungin aukio + D3A, 02600 Espoo, Finland. + +"Third Party Software " shall have the meaning set forth in Section 4. + +“Updates” shall mean a release or version of the Licensed Software containing +bug fixes, error corrections and other changes that are generally made available +to users of the Licensed Software that have contracted for Support. Updates are +generally depicted as a change to the digits following the decimal in the +Licensed Software version number. The Qt Company shall make Updates available to +the Licensee under the Support. Updates shall be considered as part of the +Licensed Software hereunder. + +“Upgrades” shall mean a release or version of the Licensed Software containing +enhancements and new features and are generally depicted as a change to the +first digit of the Licensed Software version number. In the event Upgrades are +provided to the Licensee under this Agreement, they shall be considered as part +of the Licensed Software hereunder. + +2. OWNERSHIP +2.1 Ownership of The Qt Company +The Licensed Software is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. The Licensed +Software is licensed, not sold. All The Qt Company's Intellectual Property +Rights are and shall remain the exclusive property of The Qt Company or its +licensors respectively. + +2.2 Ownership of Licensee +All the Licensee's Intellectual Property Rights are and shall remain the +exclusive property of the Licensee or its licensors respectively. All +Intellectual Property Rights to the Modified Software, Applications and Devices +shall remain with the Licensee and no rights thereto shall be granted by the +Licensee to The Qt Company under this Agreement (except as set forth in Section +2.3 below). + +2.3 Modified Software +Licensee may create bug-fixes, error corrections, patches or modifications to +the Licensed Software (“Modified Software”). Such Modified Software may break +the source or binary compatibility with the Licensed Software (including without +limitation through changing the application programming interfaces ("API") or by +adding, changing or deleting any variable, method, or class signature in the +Licensed Software and/or any inter-process protocols, services or standards in +the Licensed Software libraries). To the extent that Licensee’s Modified +Software so breaks source or binary compatibility with the Licensed Software, +Licensee acknowledges that The Qt Company's ability to provide Support may be +prevented or limited and Licensee's ability to make use of Updates may be +restricted. Licensee may, at its sole and absolute discretion, choose to submit +Modified Software to The Qt Company (“Submitted Modified Software”) in +connection with Licensee’s Support request, service request or otherwise. In the +event Licensee does so, then, Licensee hereby grants The Qt Company a +sublicensable, assignable, irrevocable, perpetual, worldwide, non-exclusive, +royalty-free and fully paid-up license, under all of Licensee’s Intellectual +Property Rights, to reproduce, adapt, translate, modify, and prepare derivative +works of, publicly display, publicly perform, sublicense, make available and +distribute such Submitted Modified Software as The Qt Company sees fit at its +free and absolute discretion. + +3. LICENSES GRANTED +3.1 Development with Licensed Software +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable license, valid for the +License Term, to use, modify and copy the Licensed Software by Designated Users +on the Development Platforms for the sole purposes of designing, developing, +demonstrating and testing Application(s) and/or Devices, and to provide thereto +related support and other related services to end-user Customers. Licensee may +install copies of the Licensed Software on an unlimited number of computers +provided that (i) only the Designated Users may use the Licensed Software, and +(ii) all Designated Users must have a valid Development License to use Licensed +Software. Licensee may at any time designate another Designated User to replace +a then-current Designated User by notifying The Qt Company in writing, provided +that any Designated User may be replaced only once during any six-month period. +Upon expiry of the initially agreed License Term, the respective License Terms +shall be automatically extended to one or more Renewal Term(s), unless and until +either Party notifies the other Party in writing that it does not wish to +continue the License Term, such notification to be provided to the other Party +no less than ninety (90) days before expiry of the respective License Term. +Unless otherwise agreed between the Parties, Renewal Term shall be of equal +length with the initial Term. Any such Renewal Term shall be subject to License +Fees agreed between the Parties or, if no advance agreement exists, subject to +The Qt Company’s standard pricing applicable at the commencement date of any +such Renewal Term. + +3.2 Distribution of Applications +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through its Contractors, Redistributables as installed, +incorporated or integrated into Applications for execution on the Deployment +Platforms, and (ii) grant sublicenses to Redistributables, as distributed +hereunder, for Customers solely for Customer’s internal use and to the extent +necessary in order for the Customers to use the Applications for their +respective intended purposes. +Right to distribute the Redistributables as part of an Application as provided +herein is not royalty-bearing but is conditional upon the Licensee having paid +the agreed Development Licenses from The Qt Company before distributing any +Redistributables to Customers. + +3.3 Distribution of Devices +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through one or more tiers of Contractors, +Redistributables as installed, incorporated or integrated, or intended to be +installed, incorporated or integrated into Devices for execution on the +Deployment Platforms, and (ii) grant sublicenses to Redistributables, as +distributed hereunder, for Customers solely for Customer’s internal use and to +the extent necessary in order for the Customers to use the Devices for their +respective intended purposes. +Right to distribute the Redistributables with Devices as provided herein is +conditional upon the Licensee having purchased and paid the appropriate amount +of Development Licenses for Qt for Device Creation product and Distribution +Licenses from The Qt Company before distributing any Redistributables to +Customers. + +3.4 Further Requirements +The licenses granted above in this Section 3 by The Qt Company to Licensee are +conditional and subject to Licensee's compliance with the following terms: +(i) Licensee shall not remove or alter any copyright, trademark or other + proprietary rights notice contained in any portion of the Licensed + Software; +(ii) Applications must add primary and substantial functionality to the + Licensed Software; +(iii) Applications may not pass on functionality which in any way makes it + possible for others to create software with the Licensed Software; + provided however that Licensee may use the Licensed Software's scripting + and QML ("Qt Quick") functionality solely in order to enable scripting, + themes and styles that augment the functionality and appearance of the + Application(s) without adding primary and substantial functionality to + the Application(s); +(iv) Applications must not compete with the Licensed Software; +(v) Licensee shall not use The Qt Company's or any of its suppliers' names, + logos, or trademarks to market Applications, except that Licensee may use + “Built with Qt” logo to indicate that Application(s) was developed using + the Licensed Software; +(vi) Licensee shall not distribute, sublicense or disclose source code of + Licensed Software to any third party (provided however that Licensee may + appoint employee(s) of Contractors as Designated Users to use Licensed + Software pursuant to this Agreement). Such right may be available for the + Licensee subject to a separate software development kit (“SDK”) license + agreement to be concluded with The Qt Company; +(vii) Licensee shall not grant the Customers a right to (i) make copies of the + Redistributables except when and to the extent required to use the + Applications and/or Devices for their intended purpose, (ii) modify the + Redistributables or create derivative works thereof, (iii) decompile, + disassemble or otherwise reverse engineer Redistributables, or (iv) + redistribute any copy or portion of the Redistributables to any third + party, except as part of the onward sale of the Device on which the + Redistributables are installed; +(viii) Licensee shall not and shall cause that its Affiliates or Contractors + shall not a) in any way combine, incorporate or integrate Licensed + Software with, or use Licensed Software for creation of, any software + created with or incorporating Open Source Qt, or b) incorporate or + integrate Applications into a hardware device or product other than a + Device, unless Licensee has received an advance written permission from + The Qt Company to do so. Absent such written permission, any and all + distribution by the Licensee during the Term of a hardware device or + product a) which incorporate or integrate any part of Licensed Software + or Open Source Qt; or b) where the main user interface or substantial + functionality is provided by software built with Licensed Software or + Open Source Qt or otherwise depends on the Licensed Software or Open + Source Qt, shall be considered as a Device distribution under this + Agreement and dependent on compliance thereof (including but not limited + to obligation to pay applicable License Fees for such distribution). + Notwithstanding what is provided above in this sub-section (viii), + Licensee is entitled to use and combine Qt 3D Studio and/or Qt Design + Studio with Open Source Qt (“Combination”) for its internal evaluation + purposes, provided that Licensee shall in no way transfer, publish, + disclose, display or otherwise make available any software or work + resulting from such Combination; +(ix) Licensee shall cause all of its Affiliates and Contractors entitled to + make use of the licenses granted under this Agreement, to be + contractually bound to comply with the relevant terms of this Agreement + and not to use the Licensed Software beyond the terms hereof and for any + purposes other than operating within the scope of their services for + Licensee. Licensee shall be responsible for any and all actions and + omissions of its Affiliates and Contractors relating to the Licensed + Software and use thereof (including but not limited to payment of all + applicable License Fees); +(x) Except when and to the extent explicitly provided in this Section 3, + Licensee shall not transfer, publish, disclose, display or otherwise + make available the Licensed Software; +; and +(xi) Licensee shall not attempt or enlist a third party to conduct or attempt + to conduct any of the above. + +Above terms shall not be applicable if and to the extent they conflict with any +mandatory provisions of any applicable laws. Any use of Licensed Software beyond +the provisions of this Agreement is strictly prohibited and requires an +additional license from The Qt Company. + +4. THIRD PARTY SOFTWARE +The Licensed Software may provide links to third party libraries or code +(collectively "Third Party Software") to implement various functions. Third +Party Software does not comprise part of the Licensed Software. In some cases, +access to Third Party Software may be included in the Licensed Software. Such +Third Party Software will be listed in the ".../src/3rdparty" source tree +delivered with the Licensed Software or documented in the Licensed Software, as +such may be amended from time to time. Licensee acknowledges that use or +distribution of Third Party Software is in all respects subject to applicable +license terms of applicable third party right holders. + +5. PRE-RELEASE CODE +The Licensed Software may contain pre-release code and functionality marked or +otherwise stated as “Technology Preview”, “Alpha”, “Beta” or similar +designation. Such pre-release code may be present in order to provide +experimental support for new platforms or preliminary versions of one or more +new functionalities. The pre-release code may not be at the level of performance +and compatibility of a final, generally available, product offering of the +Licensed Software. The pre-release parts of the Licensed Software may not +operate correctly, may contain errors and may be substantially modified by The +Qt Company prior to the first commercial product release, if any. The Qt Company +is under no obligation to make pre-release code commercially available, or +provide any Support or Updates relating thereto. The Qt Company assumes no +liability whatsoever regarding any pre-release code, but any use thereof is +exclusively at Licensee’s own risk and expense. For clarity, Licensee is +entitled to use such pre-release code pursuant to Section 3, just like other +Licensed Software, provided however that in the event Add-on Products are +included and available as such pre-release code, Licensee’s right to use such +Add-on Products is nevertheless subject to and conditional upon conclusion of +separate agreement with The Qt Company. + +6. LIMITED WARRANTY AND WARRANTY DISCLAIMER +The Qt Company hereby represents and warrants that it has the power and +authority to grant the rights and licenses granted to Licensee under this +Agreement. Except as set forth above, the Licensed Software is licensed to +Licensee "as is" and Licensee’s exclusive remedy and The Qt Company’s entire +liability for errors in the Licensed Software shall be limited, at The Qt +Company’s option, to correction of the error, replacement of the Licensed +Software or return of the applicable fees paid for the defective Licensed +Software for the time period during which the License is not able to utilize the +Licensed Software under the terms of this Agreement. + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF +ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND +NON-INFRINGEMENT WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT +WARRANT THAT THE LICENSED SOFTWARE WILL SATISFY LICENSEE’S REQUIREMENTS OR THAT +IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE +UNINTERRUPTED. ALL USE OF AND RELIANCE ON THE LICENSED SOFTWARE IS AT THE SOLE +RISK OF AND RESPONSIBILITY OF LICENSEE. + +7. INDEMNIFICATION AND LIMITATION OF LIABILITY +7.1 Limitation of Liability +EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II) +BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO +EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT, +LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL, +CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND, +HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT. PARTIES +SPECIFICALLY AGREE THAT LICENSEE’S OBLIGATION TO PAY LICENSE AND OTHER FEES +CORRESPONDING TO ACTUAL USAGE OF LICENSED SOFTWARE HEREUNDER SHALL BE CONSIDERED +AS A DIRECT DAMAGE. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL +MISCONDUCT, AND (II) BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY +APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY’S TOTAL AGGREGATE LIABILITY UNDER +THIS AGREEMENT EXCEED THE AGGREGATE LICENSE FEES PAID OR PAYABLE TO THE QT +COMPANY FROM LICENSEE DURING THE PERIOD OF TWELVE (12) MONTHS IMMEDIATELY +PRECEDING THE EVENT RESULTING IN SUCH LIABILITY. THE PROVISIONS OF THIS SECTION +7 ALLOCATE THE RISKS UNDER THIS AGREEMENT BETWEEN THE QT COMPANY AND LICENSEE +AND THE PARTIES HAVE RELIED UPON THE LIMITATIONS SET FORTH HEREIN IN DETERMINING +WHETHER TO ENTER INTO THIS AGREEMENT. + +7.2 Licensee´s Indemnification +Licensee shall indemnify and hold harmless The Qt Company from and against any +claim, injury, judgment, settlement, loss or expense, including attorneys' fees +related to: (a) Licensee’s misrepresentation in connection with The Qt Company +or the Licensed Software or breach of this Agreement, (b) the Application or +Device (except where such cause of liability is solely attributable to the +Licensed Software). + +8. SUPPORT, UPDATES AND ONLINE SERVICES +Upon due payment of the agreed License Fees the Licensee will be eligible to +receive Support and Updates and to use the Online Services during the License +Term, provided, however, that in the event the License Term is longer than 36 +months, Support is provided only for the first 12 months, unless the Parties +specifically otherwise agree. Unless otherwise decided by The Company at its +free and absolute discretion, Upgrades will not be included in the Support but +may be available subject to additional fees. From time to time The Qt Company +may change the Support terms, provided that during the respective ongoing +License Term the level of Support provided by The Qt Company may not be reduced +without the consent of the Licensee. Unless otherwise agreed, The Qt Company +shall not be responsible for providing any service or support to Customers. + +9. CONFIDENTIALITY +Each Party acknowledges that during the Term of this Agreement each Party may +receive information about the other Party's business, business methods, business +plans, customers, business relations, technology, and other information, +including the terms of this Agreement, that is confidential and of great value +to the other Party, and the value of which would be significantly reduced if +disclosed to third parties (“Confidential Information”). Accordingly, when a +Party (the “Receiving Party”) receives Confidential Information from the other +Party (the “Disclosing Party”), the Receiving Party shall only disclose such +information to employees and Contractors on a need to know basis, and shall +cause its employees and employees of its Affiliates to: (i) maintain any and all +Confidential Information in confidence; (ii) not disclose the Confidential +Information to a third party without the Disclosing Party's prior written +approval; and (iii) not, directly or indirectly, use the Confidential +Information for any purpose other than for exercising its rights and fulfilling +its responsibilities pursuant to this Agreement. Each Party shall take +reasonable measures to protect the Confidential Information of the other Party, +which measures shall not be less than the measures taken by such Party to +protect its own confidential and proprietary information. Obligation of +confidentiality shall not apply to information that (i) is or becomes generally +known to the public through no act or omission of the Receiving Party; (ii) was +in the Receiving Party's lawful possession prior to the disclosure hereunder and +was not subject to limitations on disclosure or use; (iii) is developed +independently by employees or Contractors of the Receiving Party or other +persons working for the Receiving Party who have not had access to the +Confidential Information of the Disclosing Party, as proven by the written +records of the Receiving Party; (iv) is lawfully disclosed to the Receiving +Party without restrictions, by a third party not under an obligation of +confidentiality; or (v) the Receiving Party is legally compelled to disclose, in +which case the Receiving Party shall notify the Disclosing Party of such +compelled disclosure and assert the privileged and confidential nature of the +information and cooperate fully with the Disclosing Party to limit the scope of +disclosure and the dissemination of disclosed Confidential Information to the +minimum extent necessary. The obligations under this Section 9 shall continue to +remain in force for a period of five (5) years after the last disclosure, and, +with respect to trade secrets, for so long as such trade secrets are protected +under applicable trade secret laws. + +10. FEES, DELIVERY AND PAYMENT +10.1 License Fees +License Fees are described in The Qt Company’s standard price list, quote or +Purchase Order confirmation or in an appendix hereto, as the case may be. The +License Fees shall not be refunded or claimed as a credit in any event or for +any reason whatsoever. + +10.2 Ordering Licenses +Licensee may purchase Development Licenses and Distribution Licenses pursuant to +agreed pricing terms or, if no specific pricing terms have been agreed upon, at +The Qt Company's standard pricing terms applicable at the time of purchase. +Licensee shall submit all purchase orders for Development Licenses and +Distribution Licenses to The Qt Company by email or any other method acceptable +to The Qt Company (each such order is referred to herein as a “Purchase Order”) +for confirmation, whereupon the Purchase Order shall become binding between the +Parties. + +10.3 Distribution License Packs +Unless otherwise agreed, Distribution Licenses shall be purchased by way of +Distribution License Packs. Upon due payment of the ordered Distribution License +Pack(s), the Licensee will have an account of Distribution Licenses available +for installing, bundling or integrating (all jointly “installing”) the +Redistributables with the Devices or for otherwise distributing the +Redistributables in accordance with this Agreement. Each time Licensee +“installs” or distributes a copy of Redistributables, then one Distribution +License is used, and Licensee’s account of available Distribution Licenses is +decreased accordingly. Licensee may “install” copies of the Redistributables so +long as Licensee has Distribution Licenses remaining on its account. +Redistributables will be deemed to have been “installed” into a Device when one +of the following circumstances shall have occurred: a) the Redistributables have +been loaded onto the Device and used outside of the Licensee’s premises or b) +the Device has been fully tested and placed into Licensee's inventory (or sold) +for the first time (i.e., Licensee will not be required to use (or pay for) more +than one Distribution License for each individual Device, e.g. in a situation +where a Device is returned to Licensee's inventory after delivery to a +distributor or sale to a Customer). In addition, if Licensee includes a back-up +copy of the Redistributables on a CD-ROM or other storage medium along with the +product, that backup copy of the Redistributables will not be deemed to have +been “installed” and will not require an additional Distribution License. + +10.4 Payment Terms +License Fees and any other charges under this Agreement shall be paid by +Licensee no later than thirty (30) days from the date of the applicable invoice +from The Qt Company. The Qt Company will submit an invoice to Licensee after the +date of this Agreement and/or after The Qt Company receives a Purchase Order +from Licensee. A late payment charge of the lower of (a) one percent per month; +or (b) the interest rate stipulated by applicable law, shall be charged on any +unpaid balances that remain past due. The Qt Company shall have the right to +suspend, terminate or withhold grants of all rights to the Licensed Software +hereunder, including but not limited to the Developer License, Distribution +License, and Support, should Licensee fail to make payment in timely fashion. + +10.5 Taxes +All License Fees and other charges payable hereunder are gross amounts but +exclusive of any value added tax, use tax, sales tax and other taxes, duties or +tariffs (“Taxes”). Such applicable Taxes shall be paid by Licensee, or, where +applicable, in lieu of payment of such Taxes, Licensee shall provide an +exemption certificate to The Qt Company and any applicable authority. + +11 RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS +11.1 Licensee’s Record-keeping +Licensee shall at all times maintain accurate and up-to-date written records of +Licensee’s activities related to the use of Licensed Software and distribution +of Redistributables. The records shall be adequate to determine Licensee’s +compliance with the provisions of this Agreement and to demonstrate the number +of Designated Users and Redistributables distributed by Licensee. The records +shall conform to good accounting practices reasonably acceptable to The Qt +Company. Licensee shall, within thirty (30) days from receiving The Qt Company’s +request to that effect, deliver to The Qt Company a report on Licensee’s usage +of Licensed Software, such report to contain information, in sufficient detail, +on (i) amount of users working with Licensed Software, (ii) copies of +Redistributables distributed by Licensee during that calendar quarter, (iii) +number of undistributed copies of Redistributables and corresponding number of +unused Distribution Licenses remaining on Licensee’s account, and (iv) any other +information as The Qt Company may reasonably require from time to time. + +11.2. The Qt Company’s Audit Rights +The Qt Company or an independent auditor acting on behalf of The Qt Company’s, +may, upon at least five (5) business days’ prior written notice and at its +expense, audit Licensee with respect to the use of the Redistributables, but not +more frequently than once during each 6-month period. Such audit may be +conducted by mail, electronic means or through an in-person visit to Licensee’s +place of business. Any such in-person audit shall be conducted during regular +business hours at Licensee's facilities and shall not unreasonably interfere +with Licensee's business activities. The Qt Company or the independent auditor +acting on behalf of The Qt Company shall be entitled to inspect Licensee’s +Records. All such Licensee’s Records and use thereof shall be subject to an +obligation of confidentiality under this Agreement. If an audit reveals that +Licensee is using the Licensed Software beyond scope of the licenses Licensee +has paid for, Licensee agrees to immediately pay The Qt Company any amounts owed +for such unauthorized use. +In addition, in the event the audit reveals a material violation of the terms of +this Agreement (underpayment of more than 5% of License Fees shall always be +deemed a material violation for purposes of this section), then the Licensee +shall pay The Qt Company's reasonable cost of conducting such audit. + +12 TERM AND TERMINATION +12.1 Term +This Agreement shall enter into force upon due acceptance by both Parties and +remain in force for as long as there is any Development License(s) in force +(“Term”), unless and until terminated pursuant to the terms of this Section 12. + +12.2 Termination by The Qt Company +The Qt Company shall have the right to terminate this Agreement upon thirty (30) +days prior written notice if the Licensee is in material breach of any +obligation of this Agreement and fails to remedy such breach within such notice +period. + +12.3 Mutual Right to Terminate +Either Party shall have the right to terminate this Agreement immediately upon +written notice in the event that the other Party becomes insolvent, files for +any form of bankruptcy, makes any assignment for the benefit of creditors, has a +receiver, administrative receiver or officer appointed over the whole or a +substantial part of its assets, ceases to conduct business, or an act equivalent +to any of the above occurs under the laws of the jurisdiction of the other +Party. + +12.4 Parties´ Rights and Duties upon Termination +Upon expiry or termination of the Agreement Licensee shall cease and shall cause +all Designated Users (including those of its Affiliates’ and Contractors’) to +cease using the Licensed Software and distribution of the Redistributables under +this Agreement. +Notwithstanding the above, in the event the Agreement expires or is terminated: +(i) as a result of The Qt Company choosing not to renew the Development + License(s) as set forth in Section 3.1, then all valid licenses possessed + by the Licensee at such date shall be extended to be valid in perpetuity + under the terms of this Agreement and Licensee is entitled to purchase + additional licenses as set forth in Section 10.2; or +(ii) for reason other than by The Qt Company pursuant to item (i) above or + pursuant to Section 12.2, then the Licensee is entitled, for a period of + six (6) months after the effective date of termination, to continue + distribution of Devices under the Distribution Licenses paid but unused at + such effective date of termination. Upon any such termination the Licensee + shall destroy or return to The Qt Company all copies of the Licensed + Software and all related materials and will certify the same to The Qt + Company upon its request, provided however that Licensee may retain and + exploit such copies of the Licensed Software as it may reasonably require + in providing continued support to Customers. +Expiry or termination of this Agreement for any reason whatsoever shall not +relieve Licensee of its obligation to pay any License Fees accrued or payable to +The Qt Company prior to the effective date of termination, and Licensee shall +immediately pay to The Qt Company all such fees upon the effective date of +termination. Termination of this Agreement shall not affect any rights of +Customers to continue use of Applications and Devices (and therein incorporated +Redistributables). + +12.5 Extension in case of bankruptcy +In the event The Qt Company is declared bankrupt under a final, non-cancellable +decision by relevant court of law, and this Agreement is not, at the date of +expiry of the Development License(s) pursuant to Section 3.1, assigned to party, +who has assumed The Qt Company’s position as a legitimate licensor of Licensed +Software under this Agreement, then all valid licenses possessed by the Licensee +at such date of expiry, and which the Licensee has not notified for expiry, +shall be extended to be valid in perpetuity under the terms of this Agreement. + +13. GOVERNING LAW AND LEGAL VENUE +In the event this Agreement is in the name of The Qt Company Inc., a Delaware +Corporation, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of the State of California, USA, excluding its choice of law + provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any dispute, claim or controversy arising out of or relating to this + Agreement or the breach, termination, enforcement, interpretation or + validity thereof, including the determination of the scope or + applicability of this Agreement to arbitrate, shall be determined by + arbitration in San Francisco, USA, before one arbitrator. The arbitration + shall be administered by JAMS pursuant to JAMS' Streamlined Arbitration + Rules and Procedures. Judgment on the Award may be entered in any court + having jurisdiction. This Section shall not preclude parties from seeking + provisional remedies in aid of arbitration from a court of appropriate + jurisdiction. +In the event this Agreement is in the name of The Qt Company Ltd., a Finnish +Company, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of Finland, excluding its choice of law provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any disputes, controversy or claim arising out of or relating to this + Agreement, or the breach, termination or validity thereof shall be shall + be finally settled by arbitration in accordance with the Arbitration Rules + of Finland Chamber of Commerce. The arbitration tribunal shall consist of + one (1), or if either Party so requires, of three (3), arbitrators. The + award shall be final and binding and enforceable in any court of competent + jurisdiction. The arbitration shall be held in Helsinki, Finland and the + process shall be conducted in the English language. This Section shall not + preclude parties from seeking provisional remedies in aid of arbitration + from a court of appropriate jurisdiction. + +14. GENERAL PROVISIONS +14.1 No Assignment +Except in the case of a merger or sale of substantially all of its corporate +assets, Licensee shall not be entitled to assign or transfer all or any of its +rights, benefits and obligations under this Agreement without the prior written +consent of The Qt Company, which shall not be unreasonably withheld or delayed. +The Qt Company shall be entitled to freely assign or transfer any of its rights, +benefits or obligations under this Agreement. + +14.2 No Third Party Representations +Licensee shall make no representations or warranties concerning the Licensed +Software on behalf of The Qt Company. Any representation or warranty Licensee +makes or purports to make on The Qt Company’s behalf shall be void as to The Qt +Company. + +14.3 Surviving Sections +Any terms and conditions that by their nature or otherwise reasonably should +survive termination of this Agreement shall so be deemed to survive. + +14.4 Entire Agreement +This Agreement, the exhibits hereto, the License Certificate and any applicable +Purchase Order constitute the complete agreement between the Parties and +supersedes all prior or contemporaneous discussions, representations, and +proposals, written or oral, with respect to the subject matters discussed +herein. +In the event of any conflict or inconsistency between this Agreement and any +Purchase Order, the terms of this Agreement will prevail over the terms of the +Purchase Order with respect to such conflict or inconsistency. +Parties specifically acknowledge and agree that this Agreement prevails over any +click-to-accept or similar agreements the Designated Users may need to accept +online upon download of the Licensed Software, as may be required by The Qt +Company’s applicable processes relating to Licensed Software. + +14.5 Modifications +No modification of this Agreement shall be effective unless contained in a +writing executed by an authorized representative of each Party. No term or +condition contained in Licensee's Purchase Order shall apply unless expressly +accepted by The Qt Company in writing. + +14.6 Force Majeure +Except for the payment obligations hereunder, neither Party shall be liable to +the other for any delay or non-performance of its obligations hereunder in the +event and to the extent that such delay or non-performance is due to an event of +act of God, terrorist attack or other similar unforeseeable catastrophic event +that prevents either Party for fulfilling its obligations under this Agreement +and which such Party cannot avoid or circumvent (“Force Majeure Event”). If the +Force Majeure Event results in a delay or non-performance of a Party for a +period of three (3) months or longer, then either Party shall have the right to +terminate this Agreement with immediate effect without any liability (except for +the obligations of payment arising prior to the event of Force Majeure) towards +the other Party. + +14.7 Notices +Any notice given by one Party to the other shall be deemed properly given and +deemed received if specifically acknowledged by the receiving Party in writing +or when successfully delivered to the recipient by hand, fax, or special courier +during normal business hours on a business day to the addresses specified for +each Party on the signature page. Each communication and document made or +delivered by one Party to the other Party pursuant to this Agreement shall be in +the English language. + +14.8 Export Control +Licensee acknowledges that the Redistributables may be subject to export control +restrictions under the applicable laws of respective countries. Licensee shall +fully comply with all applicable export license restrictions and requirements as +well as with all laws and regulations relating to the Redistributables and +exercise of licenses hereunder and shall procure all necessary governmental +authorizations, including without limitation, all necessary licenses, approvals, +permissions or consents, where necessary for the re-exportation of the +Redistributables, Applications and/or Devices. + +14.9 No Implied License +There are no implied licenses or other implied rights granted under this +Agreement, and all rights, save for those expressly granted hereunder, shall +remain with The Qt Company and its licensors. In addition, no licenses or +immunities are granted to the combination of the Licensed Software with any +other software or hardware not delivered by The Qt Company under this Agreement. + +14.10 Attorney Fees +The prevailing Party in any action to enforce this Agreement shall be entitled +to recover its attorney’s fees and costs in connection with such action. + +14.11 Severability +If any provision of this Agreement shall be adjudged by any court of competent +jurisdiction to be unenforceable or invalid, that provision shall be limited or +eliminated to the minimum extent necessary so that this Agreement shall +otherwise remain in full force and effect and enforceable. + + + +APPENDICES +The Agreement includes Appendix 1 as shown below. In addition, the Agreement may +include one or more of the Appendices 3-5 listed below depending on the +product(s) purchased by the Licensee, what is stated in the quote or invoice, +and/or what is stated on the License Certificate. + +  +APPENDIX 1: LICENSED SOFTWARE +1a. Licensed Software - Qt Toolkit +Module Description +Qt Core Core non-graphical classes used by other modules. +Qt GUI Base classes for graphical user interface (GUI) + components. +Qt Multimedia Classes for audio, video and camera functionality. +Qt Multimedia Widgets Widget-based classes for implementing multimedia + functionality. +Qt Network Classes to make network programming easier and more + portable. +Qt QML Classes for QML and JavaScript languages. +Qt Quick A declarative framework for building highly dynamic + applications with custom user interfaces. +Qt Quick Controls 2 Provides lightweight QML types for creating + performant user interfaces for desktop, embedded, and + mobile devices. +Qt Quick Dialogs Types for creating and interacting with system + dialogs from a Qt Quick application. +Qt Quick Layouts Layouts are items that are used to arrange Qt Quick 2 + based items in the user interface. +Qt Quick Test A unit test framework for QML applications. +Qt SQL Classes for database integration using SQL. +Qt Test Classes for unit testing Qt applications and + libraries. +Qt Widgets Classes to extend Qt GUI with C++ widgets. +Active Qt Classes for applications which use ActiveX and COM +Qt 3D Functionality for near-realtime simulation systems + with support for 2D and 3D rendering. +Qt Android Extras Provides platform-specific APIs for Android. +Qt Bluetooth Provides access to Bluetooth hardware. +Qt Canvas 3D Enables OpenGL-like 3D drawing calls from Qt Quick + applications using JavaScript. +Qt Concurrent Classes for writing multi-threaded programs without + using low-level threading primitives. +Qt D-Bus Classes for inter-process communication over the + D-Bus protocol. +Qt Gamepad Enables Qt applications to support the use of gamepad + hardware. +Qt Graphical Effects Graphical effects for use with Qt Quick 2. +Qt Help Classes for integrating documentation into + applications, similar to Qt Assistant. +Qt Image Formats Plugins for additional image formats: TIFF, MNG, TGA, + WBMP. +Qt Location Displays map, navigation, and place content in a QML + application. +Qt Mac Extras Provides platform-specific APIs for macOS. +Qt Network Authorization Provides support for OAuth-based authorization to + online services. +Qt NFC Provides access to Near-Field communication (NFC) + hardware. +Qt Platform Headers Provides classes that encapsulate platform-specific + information. +Qt Positioning Provides access to position, satellite and area + monitoring classes. +Qt Print Support Classes to make printing easier and more portable. +Qt Purchasing Enables in-app purchase of products in Qt + applications. +Qt for Python Python bindings for Qt. +Qt Quick Controls Reusable Qt Quick based UI controls to create classic + desktop-style user interfaces. +Qt Quick Extras Provides a specialized set of controls that can be + used to build interfaces in Qt Quick. +Qt Quick Widgets Provides a C++ widget class for displaying a Qt + Quick user interface. +Qt SCXML Provides classes and tools for creating state + machines from SCXML files. +Qt Sensors Provides access to sensor hardware and motion gesture + recognition. +Qt Serial Bus Provides access to serial industrial bus interface. +Qt Serial Port Provides access to hardware and virtual serial ports. +Qt Speech Provides support for accessibility features such as + text-to-speech. +Qt SVG Classes for displaying the contents of SVG files. +Qt UI Tools Classes for loading QWidget based forms created in Qt + Designer dynamically, at runtime. +Qt WebChannel Provides access to QObject or QML objects from HTML + clients for seamless integration of Qt applications + with HTML/JavaScript clients. +Qt WebEngine Classes and functions for embedding web content in + applications using the Chromium browser project. +Qt WebSockets Provides WebSocket communication. +Qt WebView Displays web content in a QML application by using + APIs native to the platform. +Qt Windows Extras Provides platform-specific APIs for Windows. +Qt X11 Extras Provides platform-specific APIs for X11. +Qt XML C++ implementations of SAX and DOM. +Qt XML Patterns Support for XPath, XQuery, XSLT and XML schema + validation. +Qt Wayland Compositor Provides a framework to develop a Wayland compositor. +Qt Charts UI Components for displaying charts. +Qt Data Visualization UI Components for creating 3D data visualizations. +Qt Virtual Keyboard A framework for implementing different input methods + as well as a QML virtual keyboard. + +1b. Licensed software – Embedded software development libraries +Module Description +Boot 2 Qt stack Yocto based Embedded Linux stack for selected + target hardware +Qt OTA Client-side capability for device image + updates Over The Air. +Device Utilities Collection of API’s to manage the device; + E.g. display, WiFi and Bluetooth settings. +Qt Debugging Bridge (QDB) Daemon Enables host-target deployment, debugging, + profiling and other features over USB. Up to + developer to decide if this is left in the + final solution. + +1c. Licensed Software - Qt Tools/Applications +Tool Description +Qt Creator The integrated development environment for Qt. +Qt Designer Qt tool for designing and building graphical user interfaces. +Qt Linguist Tool used to add translations to Qt applications. +Qt Assistant Tool for viewing online documentation in Qt help-file format. +Qmake Utility tool used to automate the generation of make files. +uic User interface compiler for the Qt GUI toolkit. +rcc Resource compiler used for embedding resources into Qt + applications. +lupdate Tool that finds the translatable strings in the specified source, + header and Qt Designer interface files, and produces or updates + translation files. +lrelease Tool that produces translation files in the compact binary format + used by localized Qt applications. +qlalr Qt parser generator tool. +qdoc Configurable documentation generation tool. +qmlscene QML launcher tool +qmlviewer QML launcher tool + +1d. Licensed software –Qt Tools/Applications specific to embedded software +development +Tool Description +Target toolchains Cross compilation toolchains for + supported target devices and operating + systems +Qt Debugging Bridge (QDB) Host Tools Enables deployment, debugging, + profiling and other features over USB + from development host PC to target + device. +qtconfig-gui Qt Lite Configurator tool graphical + interface +Qt Emulator Qt emulator + +2. Parts of the Licensed Software that are permitted for distribution in +object-code form only (“Redistributables”) under this Agreement: + +2a. Qt for Application Development +(i) The Licensed Software's Qt Toolkit libraries defined in 1a +(ii) The Licensed Software's installer framework + +2b. Qt for Device Creation +(i) Qt for Application Development Redistributables defined in 2a +(ii) The Licensed Software’s Embedded software development libraries defined in + 1b + +2c. Qt 3D Studio +The Licensed Software’s Qt 3D Studio Runtime (“Qt53DStudioRuntime2”) + + +APPENDIX 3: ADDITIONS TO LICENSED SOFTWARE +In addition to what is provided under the definition of the Licensed Software, +Parties agree that Licensed Software shall also include the following additional +software products of The Qt Company if included in the quote / invoice: +Qt for Automation + - MQTT software protocol libraries + - KNX software protocol libraries + - OPCUA (open source backend) + - OPCUA (Unified Automation backend) +Qt Safe Renderer + - Qt Safe Renderer library +Qt Application Manager + - Qt Application Manager library with Qt Creator integration + +All the above is considered as Redistributables and subject to applicable +provisions and limitations including but not limited to what is defined in +Section 3. + + +APPENDIX 4: SMALL BUSINESS AND START-UP APPENDIX +The provisions of this Appendix 4 are applicable for Start-up Companies and for +the Evaluation Term. +For the purpose of this Appendix 4, the following additional definitions shall +be applicable: + “Trial Term” shall mean a period of twelve (12) months. + “Start-up Company” means a company with a maximum annual revenue, including + funding, equivalent to 100,000 USD (in applicable currency) during a respective + calendar year, as evidenced by duly audited records of the Licensee and + approved by The Qt Company. + +During the Trial Term, Section 3 shall apply with following modifications +(“Trial Term Modifications”): + - Licenses granted under Sections 3.1 and 3.2 shall be free of any charge. For + clarity, License for distribution of Devices pursuant to Section 3.3 is + subject to applicable License Fee for necessary Distribution Licenses; + - Development License under Section 3.1 is limited to a maximum of three (3) + Designated Users; and + - Support is available subject to availability, as judged by The Qt Company at + its free and absolute discretion. + +Upon expiry of the Trial Term: + a) This Appendix 4 is terminated, Trial Term Modifications cease to remain in + force, Licensee’s Development Licenses shall be automatically converted into + licenses subject to a License Fee (in the amount specified in the quote or + in Appendix 2 and payable with a 30-day payment term) and Licensee’s rights + and obligations under this Agreement shall continue to remain in force under + the standard provisions of the Agreement, unless the Licensee notifies The + Qt Company in writing no less than ninety (90) days before such expiry date + that Licensee does not agree to such continuance, in which event the + Agreement, and all rights of the Licensee thereunder, shall expire; provided + however that + b) in the event the Licensee still qualifies as a Start-up Company, the + Licensee has an option (“Option”), instead of what is stated in item a) + above, to extend the Trial Term renewal is limited to one time and total + duration of Trial Terms thus to 24 months after the effective date. Licensee + shall notify The Qt Company in writing no less than ninety (90) days before + the expiry date, if Licensee wish to exercise the Option. + + +APPENDIX 5: NON-COMMERCIAL USE APPENDIX +The provisions of this Appendix 5 are applicable for non-commercial use of the +Licensed Software by the Licensee. +For the purpose of this Appendix 5, the following additional definitions +(replacing the relevant definition of the Agreement, where applicable) shall be +applicable: + “Demo Units” shall mean (i) hardware development platform, which incorporates + the Licensed Software along with Licensee’s software and/or hardware, and + (ii) prototype versions of Applications or Devices. + “Designated User(s)” shall mean the employees and students of the Licensee. + “Licensee Products” shall mean Applications and/or Devices. + “Permitted Purpose” shall mean (i) Licensee’s internal evaluation and testing + of Licensed Software, (ii) building Demo Units as well as (iii) educational + use. + “Term” shall mean a period of twelve (12) months or any such other period as + may be agreed between the Parties. + +For the purpose of this Appendix 5, the following changes shall be agreed with +respect to relevant Sections of the Agreement: + I. Recital (A) shall be replaced in its entirety to read as follows: “(A) + Licensee wishes to use the Licensed Software for the Permitted Purpose.” + II. Section 3.1 shall be replaced in its entirety to read as follows: + “The Qt Company grants to Licensee a personal, non-exclusive, + non-transferable, revocable, royalty-free license, valid for the Term, to + use, modify and copy the Licensed Software solely for the Permitted + Purpose. Licensee may install copies of the Licensed Software on an + unlimited number of computers provided that only Designated Users may use + the Licensed Software. Licensee may demonstrate the Demo Units, provided + that such demonstrations must be conducted by Licensee, and the Demo Units + must remain in Licensee’s possession and under Licensee’s control at all + times. For clarity, this Agreement does not (i) entitle Licensee to use + Licensed Software to create Applications or Devices (other than prototypes + thereof) or (ii) carry any distribution rights to Licensee, but such + rights are subject to and conditional upon conclusion of a separate + license agreement with The Qt Company.” + III. Sections 3.2, 3.3, 8 and 10 shall be deleted. + IV. Section 3.4 shall be replaced in its entirety to read as follows: + “Licensee shall not: + - remove or alter any copyright, trademark or other proprietary rights + notice contained in any portion of the Licensed Software; + - transfer, publish, sublicense, disclose, display or otherwise make + the Licensed Software available to any third party (except that + Licensee may demonstrate the Demo Units pursuant to Section 3.1); + - in any way combine, incorporate or integrate Licensed Software with, or + use Licensed Software for creation of, any software created with or + incorporating Open Source Qt; + Licensee shall cause all Designated Users who make use of the licenses + granted under this Agreement, to be contractually bound to comply with + the relevant terms of this Agreement and not to use the Licensed + Software beyond the terms hereof. Licensee shall be responsible for any + and all actions and omissions of its Designated Users relating to the + Licensed Software and use thereof. Any use of Licensed Software beyond + the provisions of this Agreement is strictly prohibited and requires an + additional license from The Qt Company.” + V. Section 12 shall be replaced in its entirety to read as follows: + “This Agreement shall enter into force upon due acceptance by both Parties + and remain in force for the Term, unless and until terminated pursuant to + the terms of Section 12. Upon termination of the Agreement, Licensee shall + cease using the Licensed Software. All other copies of Licensed Software + in the possession or control of Licensee must be erased or destroyed. An + officer of Licensee must, upon request, promptly deliver to The Qt Company + a written confirmation that this has occurred.” + +Except for the modifications specified above, this Appendix carries no change to +the terms of the Agreement which shall remain in full force. diff --git a/.QT-FOR-AUTOMOTIVE-LICENSE-AGREEMENT b/.QT-FOR-AUTOMOTIVE-LICENSE-AGREEMENT new file mode 100644 index 0000000..a2b9606 --- /dev/null +++ b/.QT-FOR-AUTOMOTIVE-LICENSE-AGREEMENT @@ -0,0 +1,894 @@ +QT AUTOMOTIVE SUITE LICENSE AGREEMENT +Agreement version 3.0 + +This Qt Automotive Suite License Agreement (“Agreement”) is a legal agreement +between The Qt Company (as defined below) and the Licensee (as defined below) +for the license of Licensed Software (as defined below). Capitalized terms used +herein are defined in Section 1. + +WHEREAS: + +(A) Licensee wishes to use the Licensed Software for the purpose of developing +and distributing Applications and/or Devices; and + +(B) The Qt Company is willing to grant the Licensee a right to use Licensed +Software for such purpose pursuant to term and conditions of this Agreement. + +NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS: + +1. DEFINITIONS + +"Affiliate" of a Party shall mean an entity (i) which is directly or indirectly +controlling such Party; (ii) which is under the same direct or indirect +ownership or control as such Party; or (iii) which is directly or indirectly +owned or controlled by such Party. For these purposes, an entity shall be +treated as being controlled by another if that other entity has fifty percent +(50 %) or more of the votes in such entity, is able to direct its affairs and/or +to control the composition of its board of directors or equivalent body. + +"Applications" shall mean Licensee's software products created using the +Licensed Software in connection with the Program, which may include the +Redistributables, or part thereof. + +"Contractor(s)" shall mean third party consultants, distributors and contractors +performing services to a Party under applicable contractual arrangement. + +"Customer(s)" shall mean Licensee's end users to whom Licensee, directly or +indirectly, distributes copies of the Redistributables. + +"Deployment Platforms" shall mean operating systems specified in the License +Certificate, in which the Redistributables can be distributed pursuant to the +terms and conditions of this Agreement. + +"Designated User(s)" shall mean the employee(s) of Licensee or Licensee's +Affiliates acting within the scope of their employment or Licensee's +Contractors acting within the scope of their services for Licensee and on behalf +of Licensee. Designated Users shall be named in the License Certificate. + +"Development License" shall mean the license needed by the Licensee for each +Designated User to use the Licensed Software under the license grant described +in Section 3.1 of this Agreement. + +"Development Platforms" shall mean those operating systems specified in the +License Certificate, in which the Licensed Software can be used under the +Development License, but not distributed in any form or used for any other +purpose. + +"Devices" shall mean hardware devices or products that 1) are manufactured +and/or distributed by the Licensee or its Affiliates or Contractors in +connection with the Program, and (2)(i) incorporate or integrate the +Redistributables or parts thereof; or (ii) do not incorporate or integrate +the Redistributables at the time of distribution, but where, when used by a +Customer, the main user interface or substantial functionality of such +device is provided by Application(s) or otherwise depends on the Licensed +Software. + +"Distribution License(s)" shall mean the license required for distribution of +Redistributables in accordance with the license grant described in Section +3.2(ii)-(iii) of this Agreement. + +"Distribution License Packs" shall mean set of prepaid Distribution Licenses +for distribution of Redistributables, as defined in The Qt Company's standard +price list, quote, Purchase Order confirmation or in an appendix hereto, as the +case may be. + +"Initial Support Term" shall mean a time period of twelve (12) months, +calculated from the effective date of this Agreement. + +"Intellectual Property Rights" shall mean patents (including utility models), +design patents, and designs (whether or not capable of registration), chip +topography rights and other like protection, copyrights, trademarks, service +marks, trade names, logos or other words or symbols and any other form of +statutory protection of any kind and applications for any of the foregoing +as well as any trade secrets. +"Licensee" shall mean the individual or legal entity that is party to this +Agreement, as identified on the signature page hereof. + +"License Certificate" shall mean a certificate accompanying the Licensed +Software and generated for each Designated User respectively. License +Certificate will specify the Designated User, the Development Platforms, +Deployment Platforms, Program and the Term of this Agreement. The terms of the +License Certificate are considered part of this Agreement and shall be updated +from time to time to reflect any changes to the foregoing terms relating to +Licensee's rights to the Licensed Software. + +"Licensee's Records" shall mean books and records that are likely to contain +information bearing on Licensee's compliance with this Agreement or the payments +due to The Qt Company under this Agreement, including, but not limited to: +assembly logs, sales records and distribution records. + +"Licensee´s SDK Contractors" shall mean Contractors of Licensee, who have +purchased or received SDK from the Licensee relating to the Program. + +"License Fee" shall mean the fee charged to the Licensee for rights granted +under the terms of this Agreement. + +"Licensed Software" shall mean all versions of The Qt Company's computer +software products, online or electronic documentation, associated media and +printed materials, including the source code, example programs and the +documentation, licensed to the Licensee under this Agreement. Licensed Software +does not include Third Party Software (as defined in Section 4) or Open Source +Qt. + +"Modified Software" shall mean bug-fixes, error corrections, patches or +modifications made to the Licensed Software by Licensee, including documentation +related thereto. + +"Online Services" shall mean any services or access to systems made available +by The Qt Company to the Licensee over the Internet relating to the Licensed +Software or for the purpose of use by the Licensee of the Licensed Software or +Support. Use of any such Online Services is discretionary for the Licensee and +some of them may be subject to additional fees. + +"Open Source Qt" shall mean all versions of The Qt Company's Qt computer +software products, online or electronic documentation, associated media and +printed materials, including the source code, example programs and the +documentation available under the terms of the GNU Lesser General Public +License, version 2.1 or later ("LGPL") or the GNU General Public License, +version 2.0 or later ("GPL"). + +"Party" or "Parties" shall mean Licensee and/or The Qt Company. + +"Program" shall mean Licensee´s business program for which purpose the Licensee +is entitled to use the Licensed Software and grant the Licensee's SDK +Contractors a right to use the Licensed Software as part of a SDK. + +"Redistributables" shall mean the portions of the Licensed Software set forth +in Appendix 1, Section 1 that may be distributed pursuant to the terms of this +Agreement in object code form only, including any relevant documentation. Where +relevant, any reference to Licensed Software in this Agreement shall include and +refer also to Redistributables. + +"SDK" or "Software Development Kit" shall mean a combination of software modules +including Licensed Software intended to be utilized in connection with the +Program. + +"Submitted Modified Software" shall have the meaning as set forth in Section +2.3. + +"Support" shall mean standard developer support that is provided by +The Qt Company to assist Designated Users in using the Licensed Software in +accordance with The Qt Company's standard support terms. + +"Support Renewal Term" shall mean a time period of twelve (12) months, +calculated from the end of the Initial Support Term or previous Support Renewal +Term, as applicable. + +"Support Term" shall mean the Initial Support Term and any possible Support +Renewal Terms(s) during which time the Licensee is eligible to receive for +Support for the Licensed Software. + +"Taxes" shall have the meaning set forth in Section 10.5. + +"Term" shall mean the validity period of this Agreement, as set forth in the +License Certificate. + +“The Qt Company” shall mean: + +(i) in the event Licensee is an individual residing in the United States or a +legal entity incorporated in the United States or having its headquarters in the +United States, The Qt Company Inc., a Delaware corporation with its office at +2350 Mission College Blvd., Suite 1020, Santa Clara, CA 95054, USA.; or + +(ii) in the event the Licensee is an individual residing outside of the United +States or a legal entity incorporated outside of the United States or having its +registered office outside of the United States, The Qt Company Ltd., a Finnish +company with its registered office at Bertel Jungin aukio D3A, 02600 Espoo, +Finland. + +"Updates" shall mean a release or version of the Licensed Software containing +bug fixes, error corrections and other changes that are generally made available +to users of the Licensed Software that have contracted for Support. Updates are +generally depicted as a change to the digits following the decimal in the +Licensed Software version number. The Qt Company shall make Updates available to +the Licensee under the Support. Updates shall be considered as part of the +Licensed Software hereunder. + +"Upgrades" shall mean a release or version of the Licensed Software containing +enhancements and new features and are generally depicted as a change to the +first digit of the Licensed Software version number. In the event Upgrades are +provided to the Licensee under this Agreement, they shall be considered as part +of the Licensed Software hereunder. + +2. OWNERSHIP 2.1 + +Ownership of The Qt Company + +The Licensed Software is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. The Licensed +Software is licensed, not sold. + +All The Qt Company's Intellectual Property Rights are and shall remain the +exclusive property of The Qt Company or its licensors respectively. + +2.2 Ownership of Licensee + +All the Licensee's Intellectual Property Rights are and shall remain the +exclusive property of the Licensee or its licensors respectively. + +All Intellectual Property Rights to the Modified Software, Applications and +Devices shall remain with the Licensee and no rights thereto shall be granted by +the Licensee to The Qt Company under this Agreement (except as set forth in +Section 2.3 below). + +2.3 Modified Software + +Licensee may create Modified Software that breaks the source or binary +compatibility with the Licensed Software. This includes, but is not limited to, +changing the application programming interfaces ("API") by adding, changing or +deleting any variable, method, or class signature in the Licensed Software +and/or any inter-process protocols, services or standards in the Licensed +Software libraries. To the extent that Licensee breaks source or binary +compatibility with the Licensed Software, Licensee acknowledges that The Qt +Company's ability to provide Support may be prevented or limited and Licensee's +ability to make use of Updates may be restricted. + +To the extent Licensee submits Modified Software to The Qt Company ("Submitted +Modified Software"), Licensee hereby grants The Qt Company a sublicensable, +assignable, irrevocable, perpetual, worldwide, non-exclusive, royalty-free and +fully paid-up license, under all of Licensee's Intellectual Property Rights, to +reproduce, adapt, translate, modify, and prepare derivative works of, publicly +display, publicly perform, sublicense, make available and distribute such +Submitted Modified Software as The Qt Company sees fit at its free and absolute +discretion. For the sake of clarity, the Licensee shall have no obligation to +provide Modified Software to The Qt Company. + +3. LICENSES GRANTED + +3.1 Development with Licensed Software + +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non- exclusive, non-transferable license, valid for the +Term, to use, modify and copy the Licensed Software by Designated Users on the +Development Platforms for the sole purposes of designing, developing, +demonstrating and testing Application(s) and/or Devices, and to provide thereto +related support and other services to end-user Customers. + +Licensee may install copies of the Licensed Software on an unlimited number of +computers provided that (i) only the Designated Users may use the Licensed +Software, and (ii) all Designated Users must have a valid Development License to +use Licensed Software. + +Licensee may at any time designate another Designated User to replace a +then-current Designated User by notifying The Qt Company in writing, provided +that any Designated User may be replaced only once during any six-month period. + +3.2 Distribution of Redistributables + +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non- exclusive, non-transferable license, valid for the +Term, to (i) distribute, by itself or through its Contractors, Redistributables +as installed, incorporated or integrated into Applications for execution on the +Deployment Platforms, and (ii) distribute, by itself or through one or more +tiers of Contractors, Redistributables as installed, incorporated or integrated, +or intended to be installed, incorporated or integrated into Devices for +execution on the Deployment Platforms, and (iii) grant sublicenses to +Redistributables, as distributed hereunder, for Customers solely for Customer's +internal use and to the extent necessary in order for the Customers to use the +Applications and/or Devices for their respective intended purposes. + +Right to distribute the Redistributables as provided herein is conditional upon +the Licensee having purchased and paid the appropriate amount of Development and +Distribution Licenses from The Qt Company before distributing any +Redistributables to Customers. + +For the avoidance of any doubt it is specifically acknowledged and agreed that +distribution of Redistributables solely as installed, incorporated or integrated +into Applications for execution on the Deployment Platform(s), as specified in +(i) of the first paragraph of Section 3.2 above, i.e. with no connection to +Devices or intention to use in connection therewith, shall not require a +Distribution License. + +3.3 SDK License + +The Qt Company grants to Licensee a personal, worldwide, non-exclusive, +non-transferable license, valid for the Term, to (i) distribute Licensed +Software as a part of the SDK to Licensee´s SDK Contractors in connection with +the Program and (ii) in connection with the Program, by itself or by Licensee's +SDK Contractors, combine, incorporate or integrate Licensed Software with, or +use Licensed Software for creation of, any software created with or +incorporating Open Source Qt, provided, however, that: + +(i) the Licensee´s SDK Contractors are only entitled to use the Licensed +Software as part of SDK and for the sole purpose of developing software for +Devices that are distributed under the Program; and + +(ii) Licensee´s SDK Contractors shall not be entitled to distribute the SDK or +any part thereof to any third parties. + +For the avoidance of any doubt, the distribution of such software development +tools that do not contain Licensed Software shall not be covered by this +Agreement. + +3.4 Further Requirements + +The licenses granted above in this Section 3 by The Qt Company to Licensee are +conditional and subject to Licensee's compliance with the following terms: + +(i) Licensee shall not remove or alter any copyright, trademark or other +proprietary rights notice contained in any portion of the Licensed Software; + +(ii) Applications and SDKs must add primary and substantial functionality to the +Licensed Software; + +(iii) Applications may not pass on functionality which in any way makes it +possible for others to create software with the Licensed Software; provided +however that Licensee may use the Licensed Software's scripting and QML ("Qt +Quick") functionality solely in order to enable scripting, themes and styles +that augment the functionality and appearance of the Application(s) without +adding primary and substantial functionality to the Application(s); + +(iv) Applications and SDKs must not compete with the Licensed Software; + +(v) Licensee shall not use The Qt Company's or any of its suppliers' names, +logos, or trademarks to market Applications or SDKs, except that Licensee may +use "Built with Qt" logo to indicate that Application(s) was developed using +the Licensed Software; + +(vi) Except as expressly provided in Section 3.3, Licensee shall not +distribute, sublicense or disclose source code of Licensed Software to any third +party (provided however that Licensee may appoint employee(s) of Contractors as +Designated Users to use Licensed Software pursuant to this Agreement); + +(vii) Licensee shall not grant the Customers a right to (i) make copies of the +Redistributables except when and to the extent required to use the Applications +and/or Devices for their intended purpose, (ii) modify the Redistributables or +create derivative works thereof, (iii) decompile, disassemble or otherwise +reverse engineer Redistributables, or (iv) redistribute any copy or portion of +the Redistributables to any third party, except as part of the onward sale of +the Device on which the Redistributables are installed; + +(viii) Except as expressly provided in Section 3.3, Licensee shall not and +shall cause that its Affiliates, Contractors and Licensee's SDK Contractors +shall not a) in any way, combine, incorporate or integrate Licensed Software +with, or use Licensed Software for creation of, any software created with or +incorporating Open Source Qt or b) incorporate or integrate Applications into a +hardware device or product other than a Device, unless Licensee has received an +advance written permission from The Qt Company to do so. Unless specifically +otherwise agreed, any and all distribution by the Licensee during the Term of +a hardware device or product a) which incorporate or integrate any part of +Licensed Software or Open Source Qt; or b) where the main user interface or +substantial functionality is provided by software build with Licensed +Software or Open Source Qt or otherwise depends on the Licensed Software or Open +Open Source Qt, shall be considered as distribution under this Agreement and +dependent on compliance thereof (including but not limited to obligation to +pay applicable License Fees for such distribution); + +(ix) Licensee shall cause all of its Affiliates and Contractors entitled to make +use of the licenses granted under this Agreement, to be contractually bound to +comply with the relevant terms of this Agreement and not to use the Licensed +Software beyond the terms hereof and for any purposes other than operating +within the scope of their services for Licensee. Licensee shall be responsible +for any and all actions and omissions of its Affiliates and Contractors relating +to the Licensed Software and use thereof (including but not limited to payment +of all applicable License Fees); + +(x) Except when and to the extent explicitly provided in this Section 3, +Licensee shall not transfer, publish, disclose, display or otherwise make +available the Licensed Software; + +(xi) Licensee shall not take any action inconsistent with The Qt Company's +Intellectual Property Rights; and + +(xii) Attempt or enlist a third party to conduct or attempt to conduct any of +the above. + +Above terms shall not be applicable if and to the extent they conflict with any +mandatory provisions of any applicable laws. + +Any use of Licensed Software beyond the provisions of this Agreement is strictly +prohibited and requires an additional license from The Qt Company. + +4. THIRD PARTY SOFTWARE + +The Licensed Software may provide links to third party libraries or code +(collectively "Third Party Software") to implement various functions. Third +Party Software does not comprise part of the Licensed Software. In some cases, +access to Third Party Software may be included in the Licensed Software. Such +Third Party Software will be listed in the ".../src/3rdparty" source tree +delivered with the Licensed Software or documented in the Licensed Software, as +such may be amended from time to time. Licensee acknowledges that use or +distribution of Third Party Software is in all respects subject to applicable +license terms of applicable third party right holders. 5. PRE-RELEASE CODE + +The Licensed Software may contain pre-release code and functionality marked or +otherwise stated as "Technology Preview", "Alpha", "Beta" or similar +designation. Such pre-release code may be present in order to provide +experimental support for new platforms or preliminary versions of one or more +new functionalities. The pre-release code may not be at the level of performance +and compatibility of a final, generally available, product offering of the +Licensed Software. The pre-release parts of the Licensed Software may not +operate correctly, may contain errors and may be substantially modified by The +Qt Company prior to the first commercial product release, if any. The Qt Company +is under no obligation to make pre-release code commercially available, or +provide any Support or Updates relating thereto. The Qt Company assumes no +liability whatsoever regarding any pre-release code, but any use thereof is +exclusively at Licensee's own risk and expense. + +6. LIMITED WARRANTY AND WARRANTY DISCLAIMER + +The Qt Company hereby represents and warrants that it has the power and +authority to grant the rights and licenses granted to Licensee under this +Agreement. + +Except as set forth above, the Licensed Software is licensed to Licensee "as +is". + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF +ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL WARRANTIES, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT +WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT WARRANT THAT THE +LICENSED SOFTWARE WILL SATISFY LICENSEE'S REQUIREMENTS OR THAT IT WILL OPERATE +WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE UNINTERRUPTED. ALL +USE OF AND RELIANCE ON THE LICENSED SOFTWARE IS AT THE SOLE RISK OF AND +RESPONSIBILITY OF LICENSEE. + +Licensee's exclusive remedy and The Qt Company's entire liability for Licensed +Software shall be limited, at The Qt Company's option, to correction of the +error, replacement of the Licensed Software or return of the applicable fees +paid for the defective Licensed Software for the time period during which the +License is not able to utilize the Licensed Software under the terms of this +Agreement. + +7. INDEMNIFICATION AND LIMITATION OF LIABILITY + +7.1 Limitation of Liability + +EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, (II) +LICENSEE'S DUTY TO PAY ALL APPLICABLE LICENSE FEES AND COMPENSATIONS, AND (III) +BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO +EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT, +LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL, +CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND, +HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT. + +EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, (II) +LICENSEE'S DUTY TO PAY ALL APPLICABLE LICENSE FEES AND COMPENSATIONS, AND (III) +BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO +EVENT SHALL EITHER PARTY'S TOTAL AGGREGATE LIABILITY UNDER THIS AGREEMENT EXCEED +THE AGGREGATE LICENSE FEES RECEIVED BY THE QT COMPANY FROM LICENSEE DURING THE +PERIOD OF TWELVE (12) MONTHS IMMEDIATELY PRECEDING THE EVENT RESULTING IN SUCH +LIABILITY. + +THE PROVISIONS OF THIS SECTION 7 ALLOCATE THE RISKS UNDER THIS AGREEMENT BETWEEN +THE QT COMPANY AND LICENSEE AND THE PARTIES HAVE RELIED UPON THE LIMITATIONS SET +FORTH HEREIN IN DETERMINING WHETHER TO ENTER INTO THIS AGREEMENT. + +7.2 Licensee´s Indemnification + +Licensee shall indemnify and hold harmless The Qt Company from and against any +claim, injury, judgment, settlement, loss or expense, including attorneys' fees +related to: (a) Licensee's misrepresentation in connection with The Qt Company +or the Licensed Software or breach of this Agreement, (b) the Application or +Device (except where such cause of liability is solely attributable to the +Licensed Software). + +8. SUPPORT, UPDATES AND ONLINE SERVICES + +Licensee will be eligible to receive Support and Updates and to use the Online +Services during the Support Term. Unless otherwise decided by The Company at its +free and absolute discretion, Upgrades will not be included in the Support but +may be available subject to additional fees. + +Licenses granted under this Agreement shall include a prepaid Initial Support +Term. + +Initial Support Term shall be automatically extended to one or more Support +Renewal Term(s), unless and until either Party notifies the other Party in +writing that it does not wish to continue the Support, such notification to be +provided to the other Party no less than ninety (90) days before expiry of the +Initial Support Term or respective Support Renewal Term. During any such Support +Renewal Term Support shall be available subject to prices and terms agreed +between the Parties or, if no advance agreement exists, subject to The Qt +Company's standard pricing applicable at the commencement date of any such +Support Renewal Term. From time to time The Qt Company may change Support +provided within each Support plan; provided that during the respective Initial +Support Term or Support Renewal Term (as the case may be), the level of Support +provided by The Qt Company may not be reduced without the consent of the +Licensee. + +Unless otherwise agreed, The Qt Company shall not be responsible for providing +any service or support to the Customers. + +9. CONFIDENTIALITY + +Each Party acknowledges that during the Term of this Agreement each Party may +receive information about the other Party's business, business methods, business +plans, customers, business relations, technology, and other information, +including the terms of this Agreement, that is confidential and of great value +to the other Party, and the value of which would be significantly reduced if +disclosed to third parties ("Confidential Information"). Accordingly, when a +Party (the "Receiving Party") receives Confidential Information from the other +Party (the "Disclosing Party"), the Receiving Party shall only disclose such +information to employees and Contractors on a need to know basis, and shall +cause its employees and employees of its Affiliates to: (i) maintain any and all +Confidential Information in confidence; (ii) not disclose the Confidential +Information to a third party without the Disclosing Party's prior written +approval; and (iii) not, directly or indirectly, use the Confidential +Information for any purpose other than for exercising its rights and fulfilling +its responsibilities pursuant to this Agreement. Each Party shall take +reasonable measures to protect the Confidential Information of the other Party, +which measures shall not be less than the measures taken by such Party to +protect its own confidential and proprietary information. + +Obligation of confidentiality shall not apply to information that (i) is or +becomes generally known to the public through no act or omission of the +Receiving Party; (ii) was in the Receiving Party's lawful possession prior to +the disclosure hereunder and was not subject to limitations on disclosure or +use; (iii) is developed independently by employees or Contractors of the +Receiving Party or other persons working for the Receiving Party who have not +had access to the Confidential Information of the Disclosing Party, as proven by +the written records of the Receiving Party; (iv) is lawfully disclosed to the +Receiving Party without restrictions, by a third party not under an obligation +of confidentiality; or (v) the Receiving Party is legally compelled to disclose, +in which case the Receiving Party shall notify the Disclosing Party of such +compelled disclosure and assert the privileged and confidential nature of the +information and cooperate fully with the Disclosing Party to limit the scope of +disclosure and the dissemination of disclosed Confidential Information to the +minimum extent necessary. + +The obligations under this Section 9 shall continue to remain in force for a +period of five (5) years after the last disclosure, and, with respect to trade +secrets, for so long as such trade secrets are protected under applicable trade +secret laws. + +10. FEES, DELIVERY AND PAYMENT + +10.1 License Fees + +License Fees are described in The Qt Company's standard price list, quote or +Purchase Order confirmation or in an appendix hereto, as the case may be. The +License Fees shall not be refunded or claimed as a credit, even on the ground +that Distribution Licenses are not used, i.e. Redistributables are not actually +distributed corresponding to the Distribution Licenses purchased, or for any +other reason. + +10.2 Ordering Licenses + +Licensee may purchase Development Licenses and Distribution Licenses pursuant to +agreed pricing terms or, if no specific pricing terms have been agreed upon, at +The Qt Company's standard pricing terms applicable at the time of purchase. + +Licensee shall submit all purchase orders for Development Licenses and +Distribution Licenses to The Qt Company by email or any other method acceptable +to The Qt Company (each such order is referred to herein as a "Purchase Order") +for confirmation, whereupon the Purchase Order shall become binding between the +Parties. + +10.3 Distribution + +License Packs Unless otherwise agreed, the Distribution Licenses are bought by +way of Distribution License Packs. + +Upon due payment of the ordered Distribution License Pack(s), the Licensee will +have an account of Distribution Licenses available for installing, bundling or +integrating (all jointly "installing") the Redistributables with the Devices or +for otherwise distributing the Redistributables in accordance with this +Agreement. + +Each time Licensee "installs" or distributes a copy of Redistributables, then +one Distribution License is used, and Licensee's account of available +Distribution Licenses is decreased accordingly. + +Licensee may "install" copies of the Redistributables so long as Licensee has +Distribution Licenses remaining on its account. + +Redistributables will be deemed to have been "installed" into a Device when one +of the following circumstances shall have occurred: a) the Redistributables +have been loaded onto the Device and used outside of the Licensee's premises or +b) the Device has been fully tested and placed into Licensee's inventory (or +sold) for the first time (i.e., Licensee will not be required to use (or pay +for) more than one Distribution License for each individual Device, e.g. in a +situation where a Device is returned to Licensee's inventory after delivery to +a distributor or sale to a Customer). In addition, if Licensee includes a +back-up copy of the Redistributables on a CD-ROM or other storage medium +along with the product, that backup copy of the Redistributables will not +be deemed to have been "installed" and will not require an additional +Distribution License. + +10.4 Payment Terms +License Fees and any other charges under this Agreement shall be paid by +Licensee no later than thirty (30) days from the date of the applicable invoice +from The Qt Company. + +The Qt Company will submit an invoice to Licensee after the date of this +Agreement and/or after The Qt Company receives a Purchase Order from Licensee. +A late payment charge of the lower of (a) one percent per month; or (b) the +interest rate stipulated by applicable law, shall be charged on any unpaid +balances that remain past due. + +The Qt Company shall have the right to suspend, terminate or withhold grants of +all rights to the Licensed Software hereunder, including but not limited to the +Developer License, Distribution License, and Support, should Licensee fail to +make payment in a timely fashion. + +10.5 Taxes +All License Fees and other charges payable hereunder are gross amounts but +exclusive of any value added tax, use tax, sales tax and other taxes, duties or +tariffs ("Taxes"). Such applicable Taxes shall be paid by Licensee, or, where +applicable, in lieu of payment of such Taxes, Licensee shall provide an +exemption certificate to The Qt Company and any applicable authority. + +11 RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS + +11.1 Licensee's Record-keeping + +Licensee shall at all times maintain accurate and up-to-date written records of +Licensee's activities related to the use of Licensed Software and distribution +of Redistributables. The records shall be adequate to determine Licensee's +compliance with the provisions of this Agreement and to demonstrate the number +of Designated Users and Redistributables distributed by Licensee. The records +shall conform to good accounting practices reasonably acceptable to The Qt +Company. + +Licensee shall, within thirty (30) days from the end of each calendar +quarter, deliver to The Qt Company a report detailing the number of Designated +Users and copies of Redistributables distributed by Licensee during that +calendar quarter, and also detailing the number of undistributed copies of +Redistributables made by Licensee and remaining in its account (i.e., +undistributed copies for which Distribution Licenses have been or need to be +obtained from The Qt Company). Such report shall contain such other information +as The Qt Company shall reasonably require from time to time. + +11.2. The Qt Company's Audit Rights + +The Qt Company or an independent auditor acting on behalf of The Qt Company's, +may, upon at least five (5) business days' prior written notice and at its +expense, audit Licensee with respect to the use of the Redistributables, but +not more frequently than once during each 6-month period. Such audit may be +conducted by mail, electronic means or through an in-person visit to +Licensee's place of business. Any such in-person audit shall be conducted +during regular business hours at Licensee's facilities and shall not +unreasonably interfere with Licensee's business activities. The Qt Company or +the independent auditor acting on behalf of The Qt Company shall be entitled to +inspect Licensee's Records. All such Licensee's Records and use thereof shall be +subject to an obligation of confidentiality under this Agreement. + +If an audit reveals that Licensee is using the Licensed Software beyond scope of +the licenses Licensee has paid for, Licensee agrees to immediately pay The Qt +Company any amounts owed for such unauthorized use. + +In addition, in the event the audit reveals a material violation of the terms of +this Agreement (underpayment of more than 5% of License Fees shall always be +deemed a material violation for purposes of this section), then the Licensee +shall pay The Qt Company's reasonable cost of conducting such audit. + +12 TERM AND TERMINATION + +12.1 Term + +This Agreement shall enter into force upon due acceptance by both Parties and +remain in force for the Term, unless and until terminated pursuant to the terms +of this Section 12. + +12.2 Termination by The Qt Company + +The Qt Company shall have the right to terminate this Agreement upon thirty +(30) days prior written notice if (i) the Licensee is in material breach of any +obligation of this Agreement and fails to remedy such breach within such notice +period; (ii) or Licensee or any of its Affiliates bring a suit before any court +or administrative agency or otherwise assert a claim against The Qt Company's +or any of its Affiliates' Intellectual Property Rights or validity thereof. + +12.3 Mutual Right to Terminate + +Either Party shall have the right to terminate this Agreement immediately upon +written notice in the event that the other Party becomes insolvent, files for +any form of bankruptcy, makes any assignment for the benefit of creditors, has a +receiver, administrative receiver or officer appointed over the whole or a +substantial part of its assets, ceases to conduct business, or an act equivalent +to any of the above occurs under the laws of the jurisdiction of the other +Party. + +12.4 Parties´ Rights and Duties upon Termination + +Upon expiry or termination of the Agreement for any reason, Licensee shall, +within 30 days after such termination, cease and shall cause all Designated +Users (including those of its Affiliates' and Contractors') and Licensee's SDK +Contractors to cease using the Licensed Software and distribution of the +Redistributables under this Agreement. Notwithstanding the above, in the event +the Agreement expires or is terminated for reason other than by The Qt Company +pursuant to Section 12.2, the Licensee is entitled, for a period of six (6) +months after the effective date of termination, to continue distribution of +Devices under the Distribution Licenses paid but unused at such effective date +of termination. + +Upon any such termination the Licensee shall destroy or return to The Qt +Company all copies of the Licensed Software and all related materials and will +certify the same to The Qt Company upon its request, provided however that +Licensee may retain and exploit such copies of the Licensed Software as it may +reasonably require in providing continued support to Customers. + +Expiry or termination of this Agreement for any reason whatsoever shall not +relieve Licensee of its obligation to pay any License Fees accrued or payable +to The Qt Company prior to the effective date of termination, and Licensee shall +immediately pay to The Qt Company all such fees upon the effective date of +termination. Termination of this Agreement shall not affect any rights of +Customers to continue use of Applications and Devices (and therein incorporated +Redistributables). + +13.GOVERNING LAW AND LEGAL VENUE + +In the event this Agreement is in the name of The Qt Company Inc., a Delaware +Corporation, then: + +(i) this Agreement shall be construed and interpreted in accordance with the +laws of the State of California, USA, excluding its choice of law provisions; + +(ii) the United Nations Convention on Contracts for the International Sale of +Goods will not apply to this Agreement; and + +(iii) any dispute, claim or controversy arising out of or relating to this +Agreement or the breach, termination, enforcement, interpretation or validity +thereof, including the determination of the scope or applicability of this +Agreement to arbitrate, shall be determined by arbitration in San Francisco, +USA, before one arbitrator. The arbitration shall be administered by JAMS +pursuant to JAMS' Streamlined Arbitration Rules and Procedures. Judgment on the +Award may be entered in any court having jurisdiction. This Section shall not +preclude parties from seeking provisional remedies in aid of arbitration from a +court of appropriate jurisdiction. + +In the event this Agreement is in the name of The Qt Company Ltd., a Finnish +Company, then: + +(i) this Agreement shall be construed and interpreted in accordance with the +laws of Finland, excluding its choice of law provisions; + +(ii) the United Nations Convention on Contracts for the International Sale of +Goods will not apply to this Agreement; and + +(iii) any disputes, controversy or claim arising out of or relating to this +Agreement, or the breach, termination or validity thereof shall be shall be +finally settled by arbitration in accordance with the Arbitration Rules of +Finland Chamber of Commerce. The arbitration tribunal shall consist of one (1), +or if either Party so requires, of three (3), arbitrators. The award shall be +final and binding and enforceable in any court of competent jurisdiction. The +arbitration shall be held in Helsinki, Finland and the process shall be +conducted in the English language. This Section shall not preclude parties from +seeking provisional remedies in aid of arbitration from a court of appropriate +jurisdiction. + +14. GENERAL PROVISIONS + +14.1 No Assignment Licensee + +shall not be entitled to assign or transfer all or any of its rights, benefits +and obligations under this Agreement without the prior written consent of The Qt +Company, which shall not be unreasonably withheld or delayed. The Qt Company +shall be entitled to freely assign or transfer any of its rights, benefits or +obligations under this Agreement. + +14.2 No Third Party Representations + +Licensee shall make no representations or warranties concerning the Licensed +Software on behalf of The Qt Company. Any representation or warranty Licensee +makes or purports to make on The Qt Company's behalf shall be void as to The Qt +Company. + +14.3 Surviving Sections + +Any terms and conditions that by their nature or otherwise reasonably should +survive termination of this Agreement shall so be deemed to survive. + +14.4 Entire Agreement + +This Agreement, the exhibits hereto, the License Certificate and any applicable +Purchase Order constitute the complete agreement between the Parties and +supersedes all prior or contemporaneous discussions, representations, and +proposals, written or oral, with respect to the subject matters discussed +herein. In the event of any conflict or inconsistency between this Agreement and +any Purchase Order, the terms of this Agreement will prevail over the terms of +the Purchase Order with respect to such conflict or inconsistency. + +14.5 Modifications + +No modification of this Agreement shall be effective unless contained in a +writing executed by an authorized representative of each Party. No term or +condition contained in Licensee's Purchase Order shall apply unless expressly +accepted by The Qt Company in writing. + +14.6 Force Majeure + +Except for the payment obligations hereunder, neither Party shall be liable to +the other for any delay or non-performance of its obligations hereunder in the +event and to the extent that such delay or non- performance is due to an event +of act of God, terrorist attack or other similar unforeseeable catastrophic +event that prevents either Party for fulfilling its obligations under this +Agreement and which such Party cannot avoid or circumvent ("Force Majeure +Event"). If the Force Majeure Event results in a delay or non- performance of a +Party for a period of three (3) months or longer, then either Party shall have +the right to terminate this Agreement with immediate effect without any +liability (except for the obligations of payment arising prior to the event of +Force Majeure) towards the other Party. + +14.7 Notices + +Any notice given by one Party to the other shall be deemed properly given and +deemed received if specifically acknowledged by the receiving Party in writing +or when successfully delivered to the recipient by hand, fax, or special courier +during normal business hours on a business day to the addresses specified for +The Qt Company in the beginning of this Agreement, and for the Licensee in the +Licensee’s account profile. Each communication and document made or delivered by +one Party to the other Party pursuant to this Agreement shall be in the English +language. + +14.8 Export Control +Licensee acknowledges that the Redistributables may be subject to export +control restrictions under the applicable laws of respective countries. +Licensee shall fully comply with all applicable export license restrictions +and requirements as well as with all lawses hereunder and shall procure all +necessary governmental authorizations, including without limitation, all +necessary licenses, approvals, permissions or consents, where necessary for +the re- exportation of the Redistributables, Applications +and/or Devices. + +14.9 No Implied License +There are no implied licenses or other implied rights granted under this +Agreement, and all rights, save for those expressly granted hereunder, shall +remain with The Qt Company and its licensors. In addition, no licenses or +immunities are granted to the combination of the Licensed Software with any +other software or hardware not delivered by The Qt Company under this Agreement. + +14.10 Attorney Fees + +The prevailing Party in any action to enforce this Agreement shall be entitled +to recover its attorney's fees and costs in connection with such action. + +14.11 Severability + +If any provision of this Agreement shall be adjudged by any court of competent +jurisdiction to be unenforceable or invalid, that provision shall be limited or +eliminated to the minimum extent necessary so that this Agreement shall +otherwise remain in full force and effect and enforceable. + +IN WITNESS WHEREOF, the Parties hereto, intending to be legally bound hereby, +have caused this Agreement to be executed by Licensee’s authorized +representative installing the Licensed Software and accepting the terms hereof +in connection therewith. + +Appendix 1 + +1. Parts of the Licensed Software that are permitted for distribution in object +code form only ("Redistributables") under this Agreement: + +- The Licensed Software's essential and add-on libraries + +- The Licensed Software's configuration tool ("qtconfig") + +- The Licensed Software's help tool ("Qt Assistant") + +- The Licensed Software's internationalization tools ("Qt Linguist", "lupdate", +"lrelease") + +- The Licensed Software's QML ("Qt Quick") launcher tool ("qmlscene" and +"qmlviewer") + +- The Licensed Software's installer framework + +2. Parts of the Licensed Software that are not permitted for distribution +include, but are not limited to: + +- The Licensed Software's source code and header files + +- The Licensed Software's documentation + +- The Licensed Software's documentation generation tool ("qdoc") + +- The Licensed Software's tool for writing makefiles ("qmake") + +- The Licensed Software's Meta Object Compiler ("moc") + +- The Licensed Software's User Interface Compiler ("uic" or in the case of Qt Jambi: "juic") + +- The Licensed Software's Resource Compiler ("rcc") + +- The Licensed Software's generator (only in the case of Qt Jambi if applicable) + +- The Licensed Software's parts of the IDE tool ("Qt Creator") + +- The Licensed Software's Emulator + +- Build scripts, recipes and other material for creating the +configuration of Licensed Software and/or 3rd party components, including the +reference operating system configuration delivered in conjunction with the diff --git a/.QT-FOR-DEVICE-CREATION-LICENSE-AGREEMENT b/.QT-FOR-DEVICE-CREATION-LICENSE-AGREEMENT new file mode 100644 index 0000000..3cbd6af --- /dev/null +++ b/.QT-FOR-DEVICE-CREATION-LICENSE-AGREEMENT @@ -0,0 +1,1089 @@ +QT LICENSE AGREEMENT +Agreement version 4.1 +This License Agreement (“Agreement”) is a legal agreement between The Qt Company +(as defined below) and the Licensee (as defined below) for the license of +Licensed Software (as defined below). Capitalized terms used herein are defined +in Section 1. +WHEREAS: + +(A) Licensee wishes to use the Licensed Software for the purpose of developing + and distributing Applications and/or Devices; and +(B) The Qt Company is willing to grant the Licensee a right to use Licensed + Software for such purpose pursuant to term and conditions of this Agreement. + +NOW, THEREFORE, THE PARTIES HEREBY AGREE AS FOLLOWS: + +1. DEFINITIONS +“Affiliate” of a Party shall mean an entity (i) which is directly or indirectly +controlling such Party; (ii) which is under the same direct or indirect +ownership or control as such Party; or (iii) which is directly or indirectly +owned or controlled by such Party. For these purposes, an entity shall be +treated as being controlled by another if that other entity has fifty percent +(50 %) or more of the votes in such entity, is able to direct its affairs and/or +to control the composition of its board of directors or equivalent body. + +“Add-on Products” shall mean The Qt Company’s specific add-on software products +(for example Qt Safe Renderer, Qt for Automation, Qt Application Manager), which +are not licensed as part of The Qt Company’s standard offering, but shall be +included into the scope of Licensed Software only if so specifically agreed +between the Parties. + +“Applications” shall mean Licensee's software products created using the +Licensed Software, which may include the Redistributables, or part thereof. + +“Contractor(s)” shall mean third party consultants, distributors and contractors +performing services to a Party under applicable contractual arrangement. + +“Customer(s)” shall mean Licensee’s end users to whom Licensee, directly or +indirectly, distributes copies of the Redistributables. + +“Deployment Platforms” shall mean operating systems specified in the License +Certificate, in which the Redistributables can be distributed pursuant to the +terms and conditions of this Agreement. + +“Designated User(s)” shall mean the employee(s) of Licensee or Licensee’s +Affiliates acting within the scope of their employment or Licensee's Contractors +acting within the scope of their services for Licensee and on behalf of +Licensee. Designated Users shall be named in the License Certificate. + +“Development License” shall mean the license needed by the Licensee for each +Designated User to use the Licensed Software under the license grant described +in Section 3.1 of this Agreement. Development Licenses are available separately +for Qt for Application Development (desktop) and Qt for Device Creation +(embedded) products, each product having its designated scope and purpose of +use. Distribution Licenses are always connected to Qt for Device Creation +product only. + +“Development Platforms” shall mean those operating systems specified in the +License Certificate, in which the Licensed Software can be used under the +Development License, but not distributed in any form or used for any other +purpose. + +“Devices” shall mean hardware devices or products that 1) are manufactured +and/or distributed by the Licensee or its Affiliates or Contractors, and 2) +(i) incorporate or integrate the Redistributables or parts thereof; or (ii) do +not incorporate or integrate the Redistributables at the time of distribution, +but where, when used by a Customer, the main user interface or substantial +functionality of such device is provided by Application(s) or otherwise depends +on the Licensed Software. Devices shall be specified in Appendix 2 or in a +quote. + +“Distribution License(s)” shall mean the license required for distribution of +Redistributables in connection with Devices pursuant to license grant described +in Section 3.3 of this Agreement. + +“Distribution License Packs” shall mean set of prepaid Distribution Licenses for +distribution of Redistributables, as defined in The Qt Company’s standard price +list, quote, Purchase Order confirmation or in an appendix hereto, as the case +may be. + +“Intellectual Property Rights” shall mean patents (including utility models), +design patents, and designs (whether or not capable of registration), chip +topography rights and other like protection, copyrights, trademarks, service +marks, trade names, logos or other words or symbols and any other form of +statutory protection of any kind and applications for any of the foregoing as +well as any trade secrets. + +“License Certificate” shall mean a certificate generated by The Qt Company for +each Designated User respectively upon them downloading the Licensed Software. +License Certificate will be available under respective Designated User’s Qt +Account at account.qt.io and it will specify the Designated User, the +Development Platforms, Deployment Platforms and the License Term. The terms of +the License Certificate are considered part of this Agreement and shall be +updated from time to time to reflect any agreed changes to the foregoing terms +relating to Designated User’s rights to the Licensed Software. + +“License Fee” shall mean the fee charged to the Licensee for rights granted +under the terms of this Agreement. + +“License Term” shall mean the agreed validity period of the Development License +of the respective Designated User, during which time the Designated User is +entitled to use the Licensed Software, as set forth in the respective License +Certificate. + +“Licensed Software” shall mean either +(i) Qt for Application Development or +(ii) Qt for Device Creation, and/or +(iii) Qt 3D Studio, and/or +(iv) Qt Design Studio, and/or +(v) selected Add-on Products, if any, depending on which product(s) the + Licensee has purchased under this Agreement, + +as well as corresponding online or electronic documentation, associated media +and printed materials, including the source code, example programs and the +documentation, licensed to the Licensee under this Agreement. Licensed Software +does not include Third Party Software (as defined in Section 4) or Open Source +Qt. The Qt Company may, in the course of its development activities, at its free +and absolute discretion and without any obligation to send or publish any +notifications to the Licensee or in general, make changes, additions or +deletions in the components and functionalities of the Licensed Software, +provided that no such changes, additions or deletions will affect the already +released version of the Licensed Software, but only upcoming version(s). + +“Licensee” shall mean the individual or legal entity that is party to this +Agreement, as identified on the signature page hereof. + +“Licensee’s Records” shall mean books and records that are likely to contain +information bearing on Licensee’s compliance with this Agreement or the payments +due to The Qt Company under this Agreement, including, but not limited to: +assembly logs, sales records and distribution records. + +“Modified Software” shall have the meaning as set forth in Section 2.3. + +“Online Services” shall mean any services or access to systems made available by +The Qt Company to the Licensee over the Internet relating to the Licensed +Software or for the purpose of use by the Licensee of the Licensed Software or +Support. Use of any such Online Services is discretionary for the Licensee and +some of them may be subject to additional fees. + +“Open Source Qt” shall mean the non-commercial Qt computer software products, +licensed under the terms of the GNU Lesser General Public License, version 2.1 +or later (“LGPL”) or the GNU General Public License, version 2.0 or later +(“GPL”). For clarity, Open Source Qt shall not be provided nor governed under +this Agreement. + +”Party” or “Parties” shall mean Licensee and/or The Qt Company. + +“Qt 3D Studio” shall mean all versions of The Qt Company’s Qt 3D Studio, a 3D +user interface design and development environment for rapid designing and +prototyping of animated user interfaces. + +“Qt Design Studio” shall mean all versions of The Qt Company’s Qt Design Studio +tool, a 2D user interface design and development environment for rapid designing +and prototyping of animated user interfaces. + +“Qt for Application Development” shall mean The Qt Company’s productized +offering, which consist of all versions of +(i) Qt Toolkit, and +(ii) Qt Tools/Applications. + +“Qt for Device Creation” shall mean The Qt Company’s productized offering, +which consist of all versions of +(i) Qt for Application Development, and +(ii) Software components specific to embedded software development as set forth + in Appendix 1, Sections 1b and 1d. + +“Qt Toolkit” shall mean the modules defined in Appendix 1, Section 1a. + +“Qt Tools/Applications” shall mean the tools defined in Appendix 1, Section 1c. + +"Redistributables" shall mean the portions of the Licensed Software set forth in +Appendix 1, Section 2 that may be distributed pursuant to the terms of this +Agreement in object code form only, including any relevant documentation. Where +relevant, any reference to Licensed Software in this Agreement shall include and +refer also to Redistributables. + +“Renewal Term” shall mean an extension of previous License Term as agreed +between the Parties. + +“Submitted Modified Software” shall have the meaning as set forth in +Section 2.3. + +“Support” shall mean standard developer support that is provided by The Qt +Company to assist Designated Users in using the Licensed Software in accordance +with The Qt Company’s standard support terms and as further defined in +Section 8 hereunder. + +“Taxes” shall have the meaning set forth in Section 10.5. + +“Term” shall have the meaning set forth in Section 12. + +“The Qt Company” shall mean: +(i) in the event Licensee is an individual residing in the United States or a + legal entity incorporated in the United States or having its headquarters + in the United States, The Qt Company Inc., a Delaware corporation with its + office at 2350 Mission College Blvd., Suite 1020, Santa Clara, CA 95054, + USA.; or +(ii) in the event the Licensee is an individual residing outside of the United + States or a legal entity incorporated outside of the United States or + having its registered office outside of the United States, The Qt Company + Ltd., a Finnish company with its registered office at Bertel Jungin aukio + D3A, 02600 Espoo, Finland. + +"Third Party Software " shall have the meaning set forth in Section 4. + +“Updates” shall mean a release or version of the Licensed Software containing +bug fixes, error corrections and other changes that are generally made available +to users of the Licensed Software that have contracted for Support. Updates are +generally depicted as a change to the digits following the decimal in the +Licensed Software version number. The Qt Company shall make Updates available to +the Licensee under the Support. Updates shall be considered as part of the +Licensed Software hereunder. + +“Upgrades” shall mean a release or version of the Licensed Software containing +enhancements and new features and are generally depicted as a change to the +first digit of the Licensed Software version number. In the event Upgrades are +provided to the Licensee under this Agreement, they shall be considered as part +of the Licensed Software hereunder. + +2. OWNERSHIP +2.1 Ownership of The Qt Company +The Licensed Software is protected by copyright laws and international copyright +treaties, as well as other intellectual property laws and treaties. The Licensed +Software is licensed, not sold. All The Qt Company's Intellectual Property +Rights are and shall remain the exclusive property of The Qt Company or its +licensors respectively. + +2.2 Ownership of Licensee +All the Licensee's Intellectual Property Rights are and shall remain the +exclusive property of the Licensee or its licensors respectively. All +Intellectual Property Rights to the Modified Software, Applications and Devices +shall remain with the Licensee and no rights thereto shall be granted by the +Licensee to The Qt Company under this Agreement (except as set forth in Section +2.3 below). + +2.3 Modified Software +Licensee may create bug-fixes, error corrections, patches or modifications to +the Licensed Software (“Modified Software”). Such Modified Software may break +the source or binary compatibility with the Licensed Software (including without +limitation through changing the application programming interfaces ("API") or by +adding, changing or deleting any variable, method, or class signature in the +Licensed Software and/or any inter-process protocols, services or standards in +the Licensed Software libraries). To the extent that Licensee’s Modified +Software so breaks source or binary compatibility with the Licensed Software, +Licensee acknowledges that The Qt Company's ability to provide Support may be +prevented or limited and Licensee's ability to make use of Updates may be +restricted. Licensee may, at its sole and absolute discretion, choose to submit +Modified Software to The Qt Company (“Submitted Modified Software”) in +connection with Licensee’s Support request, service request or otherwise. In the +event Licensee does so, then, Licensee hereby grants The Qt Company a +sublicensable, assignable, irrevocable, perpetual, worldwide, non-exclusive, +royalty-free and fully paid-up license, under all of Licensee’s Intellectual +Property Rights, to reproduce, adapt, translate, modify, and prepare derivative +works of, publicly display, publicly perform, sublicense, make available and +distribute such Submitted Modified Software as The Qt Company sees fit at its +free and absolute discretion. + +3. LICENSES GRANTED +3.1 Development with Licensed Software +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable license, valid for the +License Term, to use, modify and copy the Licensed Software by Designated Users +on the Development Platforms for the sole purposes of designing, developing, +demonstrating and testing Application(s) and/or Devices, and to provide thereto +related support and other related services to end-user Customers. Licensee may +install copies of the Licensed Software on an unlimited number of computers +provided that (i) only the Designated Users may use the Licensed Software, and +(ii) all Designated Users must have a valid Development License to use Licensed +Software. Licensee may at any time designate another Designated User to replace +a then-current Designated User by notifying The Qt Company in writing, provided +that any Designated User may be replaced only once during any six-month period. +Upon expiry of the initially agreed License Term, the respective License Terms +shall be automatically extended to one or more Renewal Term(s), unless and until +either Party notifies the other Party in writing that it does not wish to +continue the License Term, such notification to be provided to the other Party +no less than ninety (90) days before expiry of the respective License Term. +Unless otherwise agreed between the Parties, Renewal Term shall be of equal +length with the initial Term. Any such Renewal Term shall be subject to License +Fees agreed between the Parties or, if no advance agreement exists, subject to +The Qt Company’s standard pricing applicable at the commencement date of any +such Renewal Term. + +3.2 Distribution of Applications +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through its Contractors, Redistributables as installed, +incorporated or integrated into Applications for execution on the Deployment +Platforms, and (ii) grant sublicenses to Redistributables, as distributed +hereunder, for Customers solely for Customer’s internal use and to the extent +necessary in order for the Customers to use the Applications for their +respective intended purposes. +Right to distribute the Redistributables as part of an Application as provided +herein is not royalty-bearing but is conditional upon the Licensee having paid +the agreed Development Licenses from The Qt Company before distributing any +Redistributables to Customers. + +3.3 Distribution of Devices +Subject to the terms of this Agreement, The Qt Company grants to Licensee a +personal, worldwide, non-exclusive, non-transferable, revocable (for cause +pursuant to this Agreement) right and license, valid for the Term, to (i) +distribute, by itself or through one or more tiers of Contractors, +Redistributables as installed, incorporated or integrated, or intended to be +installed, incorporated or integrated into Devices for execution on the +Deployment Platforms, and (ii) grant sublicenses to Redistributables, as +distributed hereunder, for Customers solely for Customer’s internal use and to +the extent necessary in order for the Customers to use the Devices for their +respective intended purposes. +Right to distribute the Redistributables with Devices as provided herein is +conditional upon the Licensee having purchased and paid the appropriate amount +of Development Licenses for Qt for Device Creation product and Distribution +Licenses from The Qt Company before distributing any Redistributables to +Customers. + +3.4 Further Requirements +The licenses granted above in this Section 3 by The Qt Company to Licensee are +conditional and subject to Licensee's compliance with the following terms: +(i) Licensee shall not remove or alter any copyright, trademark or other + proprietary rights notice contained in any portion of the Licensed + Software; +(ii) Applications must add primary and substantial functionality to the + Licensed Software; +(iii) Applications may not pass on functionality which in any way makes it + possible for others to create software with the Licensed Software; + provided however that Licensee may use the Licensed Software's scripting + and QML ("Qt Quick") functionality solely in order to enable scripting, + themes and styles that augment the functionality and appearance of the + Application(s) without adding primary and substantial functionality to + the Application(s); +(iv) Applications must not compete with the Licensed Software; +(v) Licensee shall not use The Qt Company's or any of its suppliers' names, + logos, or trademarks to market Applications, except that Licensee may use + “Built with Qt” logo to indicate that Application(s) was developed using + the Licensed Software; +(vi) Licensee shall not distribute, sublicense or disclose source code of + Licensed Software to any third party (provided however that Licensee may + appoint employee(s) of Contractors as Designated Users to use Licensed + Software pursuant to this Agreement). Such right may be available for the + Licensee subject to a separate software development kit (“SDK”) license + agreement to be concluded with The Qt Company; +(vii) Licensee shall not grant the Customers a right to (i) make copies of the + Redistributables except when and to the extent required to use the + Applications and/or Devices for their intended purpose, (ii) modify the + Redistributables or create derivative works thereof, (iii) decompile, + disassemble or otherwise reverse engineer Redistributables, or (iv) + redistribute any copy or portion of the Redistributables to any third + party, except as part of the onward sale of the Device on which the + Redistributables are installed; +(viii) Licensee shall not and shall cause that its Affiliates or Contractors + shall not a) in any way combine, incorporate or integrate Licensed + Software with, or use Licensed Software for creation of, any software + created with or incorporating Open Source Qt, or b) incorporate or + integrate Applications into a hardware device or product other than a + Device, unless Licensee has received an advance written permission from + The Qt Company to do so. Absent such written permission, any and all + distribution by the Licensee during the Term of a hardware device or + product a) which incorporate or integrate any part of Licensed Software + or Open Source Qt; or b) where the main user interface or substantial + functionality is provided by software built with Licensed Software or + Open Source Qt or otherwise depends on the Licensed Software or Open + Source Qt, shall be considered as a Device distribution under this + Agreement and dependent on compliance thereof (including but not limited + to obligation to pay applicable License Fees for such distribution). + Notwithstanding what is provided above in this sub-section (viii), + Licensee is entitled to use and combine Qt 3D Studio and/or Qt Design + Studio with Open Source Qt (“Combination”) for its internal evaluation + purposes, provided that Licensee shall in no way transfer, publish, + disclose, display or otherwise make available any software or work + resulting from such Combination; +(ix) Licensee shall cause all of its Affiliates and Contractors entitled to + make use of the licenses granted under this Agreement, to be + contractually bound to comply with the relevant terms of this Agreement + and not to use the Licensed Software beyond the terms hereof and for any + purposes other than operating within the scope of their services for + Licensee. Licensee shall be responsible for any and all actions and + omissions of its Affiliates and Contractors relating to the Licensed + Software and use thereof (including but not limited to payment of all + applicable License Fees); +(x) Except when and to the extent explicitly provided in this Section 3, + Licensee shall not transfer, publish, disclose, display or otherwise + make available the Licensed Software; +; and +(xi) Licensee shall not attempt or enlist a third party to conduct or attempt + to conduct any of the above. + +Above terms shall not be applicable if and to the extent they conflict with any +mandatory provisions of any applicable laws. Any use of Licensed Software beyond +the provisions of this Agreement is strictly prohibited and requires an +additional license from The Qt Company. + +4. THIRD PARTY SOFTWARE +The Licensed Software may provide links to third party libraries or code +(collectively "Third Party Software") to implement various functions. Third +Party Software does not comprise part of the Licensed Software. In some cases, +access to Third Party Software may be included in the Licensed Software. Such +Third Party Software will be listed in the ".../src/3rdparty" source tree +delivered with the Licensed Software or documented in the Licensed Software, as +such may be amended from time to time. Licensee acknowledges that use or +distribution of Third Party Software is in all respects subject to applicable +license terms of applicable third party right holders. + +5. PRE-RELEASE CODE +The Licensed Software may contain pre-release code and functionality marked or +otherwise stated as “Technology Preview”, “Alpha”, “Beta” or similar +designation. Such pre-release code may be present in order to provide +experimental support for new platforms or preliminary versions of one or more +new functionalities. The pre-release code may not be at the level of performance +and compatibility of a final, generally available, product offering of the +Licensed Software. The pre-release parts of the Licensed Software may not +operate correctly, may contain errors and may be substantially modified by The +Qt Company prior to the first commercial product release, if any. The Qt Company +is under no obligation to make pre-release code commercially available, or +provide any Support or Updates relating thereto. The Qt Company assumes no +liability whatsoever regarding any pre-release code, but any use thereof is +exclusively at Licensee’s own risk and expense. For clarity, Licensee is +entitled to use such pre-release code pursuant to Section 3, just like other +Licensed Software, provided however that in the event Add-on Products are +included and available as such pre-release code, Licensee’s right to use such +Add-on Products is nevertheless subject to and conditional upon conclusion of +separate agreement with The Qt Company. + +6. LIMITED WARRANTY AND WARRANTY DISCLAIMER +The Qt Company hereby represents and warrants that it has the power and +authority to grant the rights and licenses granted to Licensee under this +Agreement. Except as set forth above, the Licensed Software is licensed to +Licensee "as is" and Licensee’s exclusive remedy and The Qt Company’s entire +liability for errors in the Licensed Software shall be limited, at The Qt +Company’s option, to correction of the error, replacement of the Licensed +Software or return of the applicable fees paid for the defective Licensed +Software for the time period during which the License is not able to utilize the +Licensed Software under the terms of this Agreement. + +TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE QT COMPANY ON BEHALF OF +ITSELF AND ITS LICENSORS, SUPPLIERS AND AFFILIATES, DISCLAIMS ALL OTHER +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, ANY IMPLIED +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND +NON-INFRINGEMENT WITH REGARD TO THE LICENSED SOFTWARE. THE QT COMPANY DOES NOT +WARRANT THAT THE LICENSED SOFTWARE WILL SATISFY LICENSEE’S REQUIREMENTS OR THAT +IT WILL OPERATE WITHOUT DEFECT OR ERROR OR THAT THE OPERATION THEREOF WILL BE +UNINTERRUPTED. ALL USE OF AND RELIANCE ON THE LICENSED SOFTWARE IS AT THE SOLE +RISK OF AND RESPONSIBILITY OF LICENSEE. + +7. INDEMNIFICATION AND LIMITATION OF LIABILITY +7.1 Limitation of Liability +EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL MISCONDUCT, AND (II) +BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, IN NO +EVENT SHALL EITHER PARTY BE LIABLE TO THE OTHER PARTY FOR ANY LOSS OF PROFIT, +LOSS OF DATA, LOSS OF BUSINESS OR GOODWILL OR ANY OTHER INDIRECT, SPECIAL, +CONSEQUENTIAL, INCIDENTAL OR PUNITIVE COST, DAMAGES OR EXPENSE OF ANY KIND, +HOWSOEVER ARISING UNDER OR IN CONNECTION WITH THIS AGREEMENT. PARTIES +SPECIFICALLY AGREE THAT LICENSEE’S OBLIGATION TO PAY LICENSE AND OTHER FEES +CORRESPONDING TO ACTUAL USAGE OF LICENSED SOFTWARE HEREUNDER SHALL BE CONSIDERED +AS A DIRECT DAMAGE. EXCEPT FOR (I) CASES OF GROSS NEGLIGENCE OR INTENTIONAL +MISCONDUCT, AND (II) BREACH OF CONFIDENTIALITY, AND TO THE EXTENT PERMITTED BY +APPLICABLE LAW, IN NO EVENT SHALL EITHER PARTY’S TOTAL AGGREGATE LIABILITY UNDER +THIS AGREEMENT EXCEED THE AGGREGATE LICENSE FEES PAID OR PAYABLE TO THE QT +COMPANY FROM LICENSEE DURING THE PERIOD OF TWELVE (12) MONTHS IMMEDIATELY +PRECEDING THE EVENT RESULTING IN SUCH LIABILITY. THE PROVISIONS OF THIS SECTION +7 ALLOCATE THE RISKS UNDER THIS AGREEMENT BETWEEN THE QT COMPANY AND LICENSEE +AND THE PARTIES HAVE RELIED UPON THE LIMITATIONS SET FORTH HEREIN IN DETERMINING +WHETHER TO ENTER INTO THIS AGREEMENT. + +7.2 Licensee´s Indemnification +Licensee shall indemnify and hold harmless The Qt Company from and against any +claim, injury, judgment, settlement, loss or expense, including attorneys' fees +related to: (a) Licensee’s misrepresentation in connection with The Qt Company +or the Licensed Software or breach of this Agreement, (b) the Application or +Device (except where such cause of liability is solely attributable to the +Licensed Software). + +8. SUPPORT, UPDATES AND ONLINE SERVICES +Upon due payment of the agreed License Fees the Licensee will be eligible to +receive Support and Updates and to use the Online Services during the License +Term, provided, however, that in the event the License Term is longer than 36 +months, Support is provided only for the first 12 months, unless the Parties +specifically otherwise agree. Unless otherwise decided by The Company at its +free and absolute discretion, Upgrades will not be included in the Support but +may be available subject to additional fees. From time to time The Qt Company +may change the Support terms, provided that during the respective ongoing +License Term the level of Support provided by The Qt Company may not be reduced +without the consent of the Licensee. Unless otherwise agreed, The Qt Company +shall not be responsible for providing any service or support to Customers. + +9. CONFIDENTIALITY +Each Party acknowledges that during the Term of this Agreement each Party may +receive information about the other Party's business, business methods, business +plans, customers, business relations, technology, and other information, +including the terms of this Agreement, that is confidential and of great value +to the other Party, and the value of which would be significantly reduced if +disclosed to third parties (“Confidential Information”). Accordingly, when a +Party (the “Receiving Party”) receives Confidential Information from the other +Party (the “Disclosing Party”), the Receiving Party shall only disclose such +information to employees and Contractors on a need to know basis, and shall +cause its employees and employees of its Affiliates to: (i) maintain any and all +Confidential Information in confidence; (ii) not disclose the Confidential +Information to a third party without the Disclosing Party's prior written +approval; and (iii) not, directly or indirectly, use the Confidential +Information for any purpose other than for exercising its rights and fulfilling +its responsibilities pursuant to this Agreement. Each Party shall take +reasonable measures to protect the Confidential Information of the other Party, +which measures shall not be less than the measures taken by such Party to +protect its own confidential and proprietary information. Obligation of +confidentiality shall not apply to information that (i) is or becomes generally +known to the public through no act or omission of the Receiving Party; (ii) was +in the Receiving Party's lawful possession prior to the disclosure hereunder and +was not subject to limitations on disclosure or use; (iii) is developed +independently by employees or Contractors of the Receiving Party or other +persons working for the Receiving Party who have not had access to the +Confidential Information of the Disclosing Party, as proven by the written +records of the Receiving Party; (iv) is lawfully disclosed to the Receiving +Party without restrictions, by a third party not under an obligation of +confidentiality; or (v) the Receiving Party is legally compelled to disclose, in +which case the Receiving Party shall notify the Disclosing Party of such +compelled disclosure and assert the privileged and confidential nature of the +information and cooperate fully with the Disclosing Party to limit the scope of +disclosure and the dissemination of disclosed Confidential Information to the +minimum extent necessary. The obligations under this Section 9 shall continue to +remain in force for a period of five (5) years after the last disclosure, and, +with respect to trade secrets, for so long as such trade secrets are protected +under applicable trade secret laws. + +10. FEES, DELIVERY AND PAYMENT +10.1 License Fees +License Fees are described in The Qt Company’s standard price list, quote or +Purchase Order confirmation or in an appendix hereto, as the case may be. The +License Fees shall not be refunded or claimed as a credit in any event or for +any reason whatsoever. + +10.2 Ordering Licenses +Licensee may purchase Development Licenses and Distribution Licenses pursuant to +agreed pricing terms or, if no specific pricing terms have been agreed upon, at +The Qt Company's standard pricing terms applicable at the time of purchase. +Licensee shall submit all purchase orders for Development Licenses and +Distribution Licenses to The Qt Company by email or any other method acceptable +to The Qt Company (each such order is referred to herein as a “Purchase Order”) +for confirmation, whereupon the Purchase Order shall become binding between the +Parties. + +10.3 Distribution License Packs +Unless otherwise agreed, Distribution Licenses shall be purchased by way of +Distribution License Packs. Upon due payment of the ordered Distribution License +Pack(s), the Licensee will have an account of Distribution Licenses available +for installing, bundling or integrating (all jointly “installing”) the +Redistributables with the Devices or for otherwise distributing the +Redistributables in accordance with this Agreement. Each time Licensee +“installs” or distributes a copy of Redistributables, then one Distribution +License is used, and Licensee’s account of available Distribution Licenses is +decreased accordingly. Licensee may “install” copies of the Redistributables so +long as Licensee has Distribution Licenses remaining on its account. +Redistributables will be deemed to have been “installed” into a Device when one +of the following circumstances shall have occurred: a) the Redistributables have +been loaded onto the Device and used outside of the Licensee’s premises or b) +the Device has been fully tested and placed into Licensee's inventory (or sold) +for the first time (i.e., Licensee will not be required to use (or pay for) more +than one Distribution License for each individual Device, e.g. in a situation +where a Device is returned to Licensee's inventory after delivery to a +distributor or sale to a Customer). In addition, if Licensee includes a back-up +copy of the Redistributables on a CD-ROM or other storage medium along with the +product, that backup copy of the Redistributables will not be deemed to have +been “installed” and will not require an additional Distribution License. + +10.4 Payment Terms +License Fees and any other charges under this Agreement shall be paid by +Licensee no later than thirty (30) days from the date of the applicable invoice +from The Qt Company. The Qt Company will submit an invoice to Licensee after the +date of this Agreement and/or after The Qt Company receives a Purchase Order +from Licensee. A late payment charge of the lower of (a) one percent per month; +or (b) the interest rate stipulated by applicable law, shall be charged on any +unpaid balances that remain past due. The Qt Company shall have the right to +suspend, terminate or withhold grants of all rights to the Licensed Software +hereunder, including but not limited to the Developer License, Distribution +License, and Support, should Licensee fail to make payment in timely fashion. + +10.5 Taxes +All License Fees and other charges payable hereunder are gross amounts but +exclusive of any value added tax, use tax, sales tax and other taxes, duties or +tariffs (“Taxes”). Such applicable Taxes shall be paid by Licensee, or, where +applicable, in lieu of payment of such Taxes, Licensee shall provide an +exemption certificate to The Qt Company and any applicable authority. + +11 RECORD-KEEPING AND REPORTING OBLIGATIONS; AUDIT RIGHTS +11.1 Licensee’s Record-keeping +Licensee shall at all times maintain accurate and up-to-date written records of +Licensee’s activities related to the use of Licensed Software and distribution +of Redistributables. The records shall be adequate to determine Licensee’s +compliance with the provisions of this Agreement and to demonstrate the number +of Designated Users and Redistributables distributed by Licensee. The records +shall conform to good accounting practices reasonably acceptable to The Qt +Company. Licensee shall, within thirty (30) days from receiving The Qt Company’s +request to that effect, deliver to The Qt Company a report on Licensee’s usage +of Licensed Software, such report to contain information, in sufficient detail, +on (i) amount of users working with Licensed Software, (ii) copies of +Redistributables distributed by Licensee during that calendar quarter, (iii) +number of undistributed copies of Redistributables and corresponding number of +unused Distribution Licenses remaining on Licensee’s account, and (iv) any other +information as The Qt Company may reasonably require from time to time. + +11.2. The Qt Company’s Audit Rights +The Qt Company or an independent auditor acting on behalf of The Qt Company’s, +may, upon at least five (5) business days’ prior written notice and at its +expense, audit Licensee with respect to the use of the Redistributables, but not +more frequently than once during each 6-month period. Such audit may be +conducted by mail, electronic means or through an in-person visit to Licensee’s +place of business. Any such in-person audit shall be conducted during regular +business hours at Licensee's facilities and shall not unreasonably interfere +with Licensee's business activities. The Qt Company or the independent auditor +acting on behalf of The Qt Company shall be entitled to inspect Licensee’s +Records. All such Licensee’s Records and use thereof shall be subject to an +obligation of confidentiality under this Agreement. If an audit reveals that +Licensee is using the Licensed Software beyond scope of the licenses Licensee +has paid for, Licensee agrees to immediately pay The Qt Company any amounts owed +for such unauthorized use. +In addition, in the event the audit reveals a material violation of the terms of +this Agreement (underpayment of more than 5% of License Fees shall always be +deemed a material violation for purposes of this section), then the Licensee +shall pay The Qt Company's reasonable cost of conducting such audit. + +12 TERM AND TERMINATION +12.1 Term +This Agreement shall enter into force upon due acceptance by both Parties and +remain in force for as long as there is any Development License(s) in force +(“Term”), unless and until terminated pursuant to the terms of this Section 12. + +12.2 Termination by The Qt Company +The Qt Company shall have the right to terminate this Agreement upon thirty (30) +days prior written notice if the Licensee is in material breach of any +obligation of this Agreement and fails to remedy such breach within such notice +period. + +12.3 Mutual Right to Terminate +Either Party shall have the right to terminate this Agreement immediately upon +written notice in the event that the other Party becomes insolvent, files for +any form of bankruptcy, makes any assignment for the benefit of creditors, has a +receiver, administrative receiver or officer appointed over the whole or a +substantial part of its assets, ceases to conduct business, or an act equivalent +to any of the above occurs under the laws of the jurisdiction of the other +Party. + +12.4 Parties´ Rights and Duties upon Termination +Upon expiry or termination of the Agreement Licensee shall cease and shall cause +all Designated Users (including those of its Affiliates’ and Contractors’) to +cease using the Licensed Software and distribution of the Redistributables under +this Agreement. +Notwithstanding the above, in the event the Agreement expires or is terminated: +(i) as a result of The Qt Company choosing not to renew the Development + License(s) as set forth in Section 3.1, then all valid licenses possessed + by the Licensee at such date shall be extended to be valid in perpetuity + under the terms of this Agreement and Licensee is entitled to purchase + additional licenses as set forth in Section 10.2; or +(ii) for reason other than by The Qt Company pursuant to item (i) above or + pursuant to Section 12.2, then the Licensee is entitled, for a period of + six (6) months after the effective date of termination, to continue + distribution of Devices under the Distribution Licenses paid but unused at + such effective date of termination. Upon any such termination the Licensee + shall destroy or return to The Qt Company all copies of the Licensed + Software and all related materials and will certify the same to The Qt + Company upon its request, provided however that Licensee may retain and + exploit such copies of the Licensed Software as it may reasonably require + in providing continued support to Customers. +Expiry or termination of this Agreement for any reason whatsoever shall not +relieve Licensee of its obligation to pay any License Fees accrued or payable to +The Qt Company prior to the effective date of termination, and Licensee shall +immediately pay to The Qt Company all such fees upon the effective date of +termination. Termination of this Agreement shall not affect any rights of +Customers to continue use of Applications and Devices (and therein incorporated +Redistributables). + +12.5 Extension in case of bankruptcy +In the event The Qt Company is declared bankrupt under a final, non-cancellable +decision by relevant court of law, and this Agreement is not, at the date of +expiry of the Development License(s) pursuant to Section 3.1, assigned to party, +who has assumed The Qt Company’s position as a legitimate licensor of Licensed +Software under this Agreement, then all valid licenses possessed by the Licensee +at such date of expiry, and which the Licensee has not notified for expiry, +shall be extended to be valid in perpetuity under the terms of this Agreement. + +13. GOVERNING LAW AND LEGAL VENUE +In the event this Agreement is in the name of The Qt Company Inc., a Delaware +Corporation, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of the State of California, USA, excluding its choice of law + provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any dispute, claim or controversy arising out of or relating to this + Agreement or the breach, termination, enforcement, interpretation or + validity thereof, including the determination of the scope or + applicability of this Agreement to arbitrate, shall be determined by + arbitration in San Francisco, USA, before one arbitrator. The arbitration + shall be administered by JAMS pursuant to JAMS' Streamlined Arbitration + Rules and Procedures. Judgment on the Award may be entered in any court + having jurisdiction. This Section shall not preclude parties from seeking + provisional remedies in aid of arbitration from a court of appropriate + jurisdiction. +In the event this Agreement is in the name of The Qt Company Ltd., a Finnish +Company, then: +(i) this Agreement shall be construed and interpreted in accordance with the + laws of Finland, excluding its choice of law provisions; +(ii) the United Nations Convention on Contracts for the International Sale of + Goods will not apply to this Agreement; and +(iii) any disputes, controversy or claim arising out of or relating to this + Agreement, or the breach, termination or validity thereof shall be shall + be finally settled by arbitration in accordance with the Arbitration Rules + of Finland Chamber of Commerce. The arbitration tribunal shall consist of + one (1), or if either Party so requires, of three (3), arbitrators. The + award shall be final and binding and enforceable in any court of competent + jurisdiction. The arbitration shall be held in Helsinki, Finland and the + process shall be conducted in the English language. This Section shall not + preclude parties from seeking provisional remedies in aid of arbitration + from a court of appropriate jurisdiction. + +14. GENERAL PROVISIONS +14.1 No Assignment +Except in the case of a merger or sale of substantially all of its corporate +assets, Licensee shall not be entitled to assign or transfer all or any of its +rights, benefits and obligations under this Agreement without the prior written +consent of The Qt Company, which shall not be unreasonably withheld or delayed. +The Qt Company shall be entitled to freely assign or transfer any of its rights, +benefits or obligations under this Agreement. + +14.2 No Third Party Representations +Licensee shall make no representations or warranties concerning the Licensed +Software on behalf of The Qt Company. Any representation or warranty Licensee +makes or purports to make on The Qt Company’s behalf shall be void as to The Qt +Company. + +14.3 Surviving Sections +Any terms and conditions that by their nature or otherwise reasonably should +survive termination of this Agreement shall so be deemed to survive. + +14.4 Entire Agreement +This Agreement, the exhibits hereto, the License Certificate and any applicable +Purchase Order constitute the complete agreement between the Parties and +supersedes all prior or contemporaneous discussions, representations, and +proposals, written or oral, with respect to the subject matters discussed +herein. +In the event of any conflict or inconsistency between this Agreement and any +Purchase Order, the terms of this Agreement will prevail over the terms of the +Purchase Order with respect to such conflict or inconsistency. +Parties specifically acknowledge and agree that this Agreement prevails over any +click-to-accept or similar agreements the Designated Users may need to accept +online upon download of the Licensed Software, as may be required by The Qt +Company’s applicable processes relating to Licensed Software. + +14.5 Modifications +No modification of this Agreement shall be effective unless contained in a +writing executed by an authorized representative of each Party. No term or +condition contained in Licensee's Purchase Order shall apply unless expressly +accepted by The Qt Company in writing. + +14.6 Force Majeure +Except for the payment obligations hereunder, neither Party shall be liable to +the other for any delay or non-performance of its obligations hereunder in the +event and to the extent that such delay or non-performance is due to an event of +act of God, terrorist attack or other similar unforeseeable catastrophic event +that prevents either Party for fulfilling its obligations under this Agreement +and which such Party cannot avoid or circumvent (“Force Majeure Event”). If the +Force Majeure Event results in a delay or non-performance of a Party for a +period of three (3) months or longer, then either Party shall have the right to +terminate this Agreement with immediate effect without any liability (except for +the obligations of payment arising prior to the event of Force Majeure) towards +the other Party. + +14.7 Notices +Any notice given by one Party to the other shall be deemed properly given and +deemed received if specifically acknowledged by the receiving Party in writing +or when successfully delivered to the recipient by hand, fax, or special courier +during normal business hours on a business day to the addresses specified for +each Party on the signature page. Each communication and document made or +delivered by one Party to the other Party pursuant to this Agreement shall be in +the English language. + +14.8 Export Control +Licensee acknowledges that the Redistributables may be subject to export control +restrictions under the applicable laws of respective countries. Licensee shall +fully comply with all applicable export license restrictions and requirements as +well as with all laws and regulations relating to the Redistributables and +exercise of licenses hereunder and shall procure all necessary governmental +authorizations, including without limitation, all necessary licenses, approvals, +permissions or consents, where necessary for the re-exportation of the +Redistributables, Applications and/or Devices. + +14.9 No Implied License +There are no implied licenses or other implied rights granted under this +Agreement, and all rights, save for those expressly granted hereunder, shall +remain with The Qt Company and its licensors. In addition, no licenses or +immunities are granted to the combination of the Licensed Software with any +other software or hardware not delivered by The Qt Company under this Agreement. + +14.10 Attorney Fees +The prevailing Party in any action to enforce this Agreement shall be entitled +to recover its attorney’s fees and costs in connection with such action. + +14.11 Severability +If any provision of this Agreement shall be adjudged by any court of competent +jurisdiction to be unenforceable or invalid, that provision shall be limited or +eliminated to the minimum extent necessary so that this Agreement shall +otherwise remain in full force and effect and enforceable. + + + +APPENDICES +The Agreement includes Appendix 1 as shown below. In addition, the Agreement may +include one or more of the Appendices 3-5 listed below depending on the +product(s) purchased by the Licensee, what is stated in the quote or invoice, +and/or what is stated on the License Certificate. + +  +APPENDIX 1: LICENSED SOFTWARE +1a. Licensed Software - Qt Toolkit +Module Description +Qt Core Core non-graphical classes used by other modules. +Qt GUI Base classes for graphical user interface (GUI) + components. +Qt Multimedia Classes for audio, video and camera functionality. +Qt Multimedia Widgets Widget-based classes for implementing multimedia + functionality. +Qt Network Classes to make network programming easier and more + portable. +Qt QML Classes for QML and JavaScript languages. +Qt Quick A declarative framework for building highly dynamic + applications with custom user interfaces. +Qt Quick Controls 2 Provides lightweight QML types for creating + performant user interfaces for desktop, embedded, and + mobile devices. +Qt Quick Dialogs Types for creating and interacting with system + dialogs from a Qt Quick application. +Qt Quick Layouts Layouts are items that are used to arrange Qt Quick 2 + based items in the user interface. +Qt Quick Test A unit test framework for QML applications. +Qt SQL Classes for database integration using SQL. +Qt Test Classes for unit testing Qt applications and + libraries. +Qt Widgets Classes to extend Qt GUI with C++ widgets. +Active Qt Classes for applications which use ActiveX and COM +Qt 3D Functionality for near-realtime simulation systems + with support for 2D and 3D rendering. +Qt Android Extras Provides platform-specific APIs for Android. +Qt Bluetooth Provides access to Bluetooth hardware. +Qt Canvas 3D Enables OpenGL-like 3D drawing calls from Qt Quick + applications using JavaScript. +Qt Concurrent Classes for writing multi-threaded programs without + using low-level threading primitives. +Qt D-Bus Classes for inter-process communication over the + D-Bus protocol. +Qt Gamepad Enables Qt applications to support the use of gamepad + hardware. +Qt Graphical Effects Graphical effects for use with Qt Quick 2. +Qt Help Classes for integrating documentation into + applications, similar to Qt Assistant. +Qt Image Formats Plugins for additional image formats: TIFF, MNG, TGA, + WBMP. +Qt Location Displays map, navigation, and place content in a QML + application. +Qt Mac Extras Provides platform-specific APIs for macOS. +Qt Network Authorization Provides support for OAuth-based authorization to + online services. +Qt NFC Provides access to Near-Field communication (NFC) + hardware. +Qt Platform Headers Provides classes that encapsulate platform-specific + information. +Qt Positioning Provides access to position, satellite and area + monitoring classes. +Qt Print Support Classes to make printing easier and more portable. +Qt Purchasing Enables in-app purchase of products in Qt + applications. +Qt for Python Python bindings for Qt. +Qt Quick Controls Reusable Qt Quick based UI controls to create classic + desktop-style user interfaces. +Qt Quick Extras Provides a specialized set of controls that can be + used to build interfaces in Qt Quick. +Qt Quick Widgets Provides a C++ widget class for displaying a Qt + Quick user interface. +Qt SCXML Provides classes and tools for creating state + machines from SCXML files. +Qt Sensors Provides access to sensor hardware and motion gesture + recognition. +Qt Serial Bus Provides access to serial industrial bus interface. +Qt Serial Port Provides access to hardware and virtual serial ports. +Qt Speech Provides support for accessibility features such as + text-to-speech. +Qt SVG Classes for displaying the contents of SVG files. +Qt UI Tools Classes for loading QWidget based forms created in Qt + Designer dynamically, at runtime. +Qt WebChannel Provides access to QObject or QML objects from HTML + clients for seamless integration of Qt applications + with HTML/JavaScript clients. +Qt WebEngine Classes and functions for embedding web content in + applications using the Chromium browser project. +Qt WebSockets Provides WebSocket communication. +Qt WebView Displays web content in a QML application by using + APIs native to the platform. +Qt Windows Extras Provides platform-specific APIs for Windows. +Qt X11 Extras Provides platform-specific APIs for X11. +Qt XML C++ implementations of SAX and DOM. +Qt XML Patterns Support for XPath, XQuery, XSLT and XML schema + validation. +Qt Wayland Compositor Provides a framework to develop a Wayland compositor. +Qt Charts UI Components for displaying charts. +Qt Data Visualization UI Components for creating 3D data visualizations. +Qt Virtual Keyboard A framework for implementing different input methods + as well as a QML virtual keyboard. + +1b. Licensed software – Embedded software development libraries +Module Description +Boot 2 Qt stack Yocto based Embedded Linux stack for selected + target hardware +Qt OTA Client-side capability for device image + updates Over The Air. +Device Utilities Collection of API’s to manage the device; + E.g. display, WiFi and Bluetooth settings. +Qt Debugging Bridge (QDB) Daemon Enables host-target deployment, debugging, + profiling and other features over USB. Up to + developer to decide if this is left in the + final solution. + +1c. Licensed Software - Qt Tools/Applications +Tool Description +Qt Creator The integrated development environment for Qt. +Qt Designer Qt tool for designing and building graphical user interfaces. +Qt Linguist Tool used to add translations to Qt applications. +Qt Assistant Tool for viewing online documentation in Qt help-file format. +Qmake Utility tool used to automate the generation of make files. +uic User interface compiler for the Qt GUI toolkit. +rcc Resource compiler used for embedding resources into Qt + applications. +lupdate Tool that finds the translatable strings in the specified source, + header and Qt Designer interface files, and produces or updates + translation files. +lrelease Tool that produces translation files in the compact binary format + used by localized Qt applications. +qlalr Qt parser generator tool. +qdoc Configurable documentation generation tool. +qmlscene QML launcher tool +qmlviewer QML launcher tool + +1d. Licensed software –Qt Tools/Applications specific to embedded software +development +Tool Description +Target toolchains Cross compilation toolchains for + supported target devices and operating + systems +Qt Debugging Bridge (QDB) Host Tools Enables deployment, debugging, + profiling and other features over USB + from development host PC to target + device. +qtconfig-gui Qt Lite Configurator tool graphical + interface +Qt Emulator Qt emulator + +2. Parts of the Licensed Software that are permitted for distribution in +object-code form only (“Redistributables”) under this Agreement: + +2a. Qt for Application Development +(i) The Licensed Software's Qt Toolkit libraries defined in 1a +(ii) The Licensed Software's installer framework + +2b. Qt for Device Creation +(i) Qt for Application Development Redistributables defined in 2a +(ii) The Licensed Software’s Embedded software development libraries defined in + 1b + +2c. Qt 3D Studio +The Licensed Software’s Qt 3D Studio Runtime (“Qt53DStudioRuntime2”) + + +APPENDIX 3: ADDITIONS TO LICENSED SOFTWARE +In addition to what is provided under the definition of the Licensed Software, +Parties agree that Licensed Software shall also include the following additional +software products of The Qt Company if included in the quote / invoice: +Qt for Automation + - MQTT software protocol libraries + - KNX software protocol libraries + - OPCUA (open source backend) + - OPCUA (Unified Automation backend) +Qt Safe Renderer + - Qt Safe Renderer library +Qt Application Manager + - Qt Application Manager library with Qt Creator integration + +All the above is considered as Redistributables and subject to applicable +provisions and limitations including but not limited to what is defined in +Section 3. + + +APPENDIX 4: SMALL BUSINESS AND START-UP APPENDIX +The provisions of this Appendix 4 are applicable for Start-up Companies and for +the Evaluation Term. +For the purpose of this Appendix 4, the following additional definitions shall +be applicable: + “Trial Term” shall mean a period of twelve (12) months. + “Start-up Company” means a company with a maximum annual revenue, including + funding, equivalent to 100,000 USD (in applicable currency) during a respective + calendar year, as evidenced by duly audited records of the Licensee and + approved by The Qt Company. + +During the Trial Term, Section 3 shall apply with following modifications +(“Trial Term Modifications”): + - Licenses granted under Sections 3.1 and 3.2 shall be free of any charge. For + clarity, License for distribution of Devices pursuant to Section 3.3 is + subject to applicable License Fee for necessary Distribution Licenses; + - Development License under Section 3.1 is limited to a maximum of three (3) + Designated Users; and + - Support is available subject to availability, as judged by The Qt Company at + its free and absolute discretion. + +Upon expiry of the Trial Term: + a) This Appendix 4 is terminated, Trial Term Modifications cease to remain in + force, Licensee’s Development Licenses shall be automatically converted into + licenses subject to a License Fee (in the amount specified in the quote or + in Appendix 2 and payable with a 30-day payment term) and Licensee’s rights + and obligations under this Agreement shall continue to remain in force under + the standard provisions of the Agreement, unless the Licensee notifies The + Qt Company in writing no less than ninety (90) days before such expiry date + that Licensee does not agree to such continuance, in which event the + Agreement, and all rights of the Licensee thereunder, shall expire; provided + however that + b) in the event the Licensee still qualifies as a Start-up Company, the + Licensee has an option (“Option”), instead of what is stated in item a) + above, to extend the Trial Term renewal is limited to one time and total + duration of Trial Terms thus to 24 months after the effective date. Licensee + shall notify The Qt Company in writing no less than ninety (90) days before + the expiry date, if Licensee wish to exercise the Option. + + +APPENDIX 5: NON-COMMERCIAL USE APPENDIX +The provisions of this Appendix 5 are applicable for non-commercial use of the +Licensed Software by the Licensee. +For the purpose of this Appendix 5, the following additional definitions +(replacing the relevant definition of the Agreement, where applicable) shall be +applicable: + “Demo Units” shall mean (i) hardware development platform, which incorporates + the Licensed Software along with Licensee’s software and/or hardware, and + (ii) prototype versions of Applications or Devices. + “Designated User(s)” shall mean the employees and students of the Licensee. + “Licensee Products” shall mean Applications and/or Devices. + “Permitted Purpose” shall mean (i) Licensee’s internal evaluation and testing + of Licensed Software, (ii) building Demo Units as well as (iii) educational + use. + “Term” shall mean a period of twelve (12) months or any such other period as + may be agreed between the Parties. + +For the purpose of this Appendix 5, the following changes shall be agreed with +respect to relevant Sections of the Agreement: + I. Recital (A) shall be replaced in its entirety to read as follows: “(A) + Licensee wishes to use the Licensed Software for the Permitted Purpose.” + II. Section 3.1 shall be replaced in its entirety to read as follows: + “The Qt Company grants to Licensee a personal, non-exclusive, + non-transferable, revocable, royalty-free license, valid for the Term, to + use, modify and copy the Licensed Software solely for the Permitted + Purpose. Licensee may install copies of the Licensed Software on an + unlimited number of computers provided that only Designated Users may use + the Licensed Software. Licensee may demonstrate the Demo Units, provided + that such demonstrations must be conducted by Licensee, and the Demo Units + must remain in Licensee’s possession and under Licensee’s control at all + times. For clarity, this Agreement does not (i) entitle Licensee to use + Licensed Software to create Applications or Devices (other than prototypes + thereof) or (ii) carry any distribution rights to Licensee, but such + rights are subject to and conditional upon conclusion of a separate + license agreement with The Qt Company.” + III. Sections 3.2, 3.3, 8 and 10 shall be deleted. + IV. Section 3.4 shall be replaced in its entirety to read as follows: + “Licensee shall not: + - remove or alter any copyright, trademark or other proprietary rights + notice contained in any portion of the Licensed Software; + - transfer, publish, sublicense, disclose, display or otherwise make + the Licensed Software available to any third party (except that + Licensee may demonstrate the Demo Units pursuant to Section 3.1); + - in any way combine, incorporate or integrate Licensed Software with, or + use Licensed Software for creation of, any software created with or + incorporating Open Source Qt; + Licensee shall cause all Designated Users who make use of the licenses + granted under this Agreement, to be contractually bound to comply with + the relevant terms of this Agreement and not to use the Licensed + Software beyond the terms hereof. Licensee shall be responsible for any + and all actions and omissions of its Designated Users relating to the + Licensed Software and use thereof. Any use of Licensed Software beyond + the provisions of this Agreement is strictly prohibited and requires an + additional license from The Qt Company.” + V. Section 12 shall be replaced in its entirety to read as follows: + “This Agreement shall enter into force upon due acceptance by both Parties + and remain in force for the Term, unless and until terminated pursuant to + the terms of Section 12. Upon termination of the Agreement, Licensee shall + cease using the Licensed Software. All other copies of Licensed Software + in the possession or control of Licensee must be erased or destroyed. An + officer of Licensee must, upon request, promptly deliver to The Qt Company + a written confirmation that this has occurred.” + +Except for the modifications specified above, this Appendix carries no change to +the terms of the Agreement which shall remain in full force. diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a5343bf --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "src/3rdparty/mapbox-gl-native"] + path = src/3rdparty/mapbox-gl-native + url = ../qtlocation-mapboxgl.git + branch = upstream/qt-staging diff --git a/.qmake.conf b/.qmake.conf new file mode 100644 index 0000000..634dfd5 --- /dev/null +++ b/.qmake.conf @@ -0,0 +1,4 @@ +load(qt_build_config) + +CONFIG += warning_clean +MODULE_VERSION = 5.11.3 diff --git a/.tag b/.tag new file mode 100644 index 0000000..48db7dd --- /dev/null +++ b/.tag @@ -0,0 +1 @@ +ffac0dc5a60c93663f6a4ea5483422c58cdb52b6 diff --git a/LICENSE.FDL b/LICENSE.FDL new file mode 100644 index 0000000..938bb8d --- /dev/null +++ b/LICENSE.FDL @@ -0,0 +1,450 @@ + GNU Free Documentation License + Version 1.3, 3 November 2008 + + + Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The "publisher" means any person or entity that distributes copies of +the Document to the public. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no +other conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to +give them a chance to provide you with an updated version of the +Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other +documents released under this License, and replace the individual +copies of this License in the various documents with a single copy +that is included in the collection, provided that you follow the rules +of this License for verbatim copying of each of the documents in all +other respects. + +You may extract a single document from such a collection, and +distribute it individually under this License, provided you insert a +copy of this License into the extracted document, and follow this +License in all other respects regarding verbatim copying of that +document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions of the +GNU Free Documentation License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in +detail to address new problems or concerns. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +11. RELICENSING + +"Massive Multiauthor Collaboration Site" (or "MMC Site") means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +"Massive Multiauthor Collaboration" (or "MMC") contained in the site +means any set of copyrightable works thus published on the MMC site. + +"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +"Incorporate" means to publish or republish a Document, in whole or in +part, as part of another Document. + +An MMC is "eligible for relicensing" if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole or +in part into the MMC, (1) had no cover texts or invariant sections, and +(2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/LICENSE.GPL2 b/LICENSE.GPL2 new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE.GPL2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/LICENSE.GPL3 b/LICENSE.GPL3 new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE.GPL3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.GPL3-EXCEPT b/LICENSE.GPL3-EXCEPT new file mode 100644 index 0000000..b1cb1be --- /dev/null +++ b/LICENSE.GPL3-EXCEPT @@ -0,0 +1,704 @@ +This is the GNU General Public License version 3, annotated with The +Qt Company GPL Exception 1.0: + +------------------------------------------------------------------------- + +The Qt Company GPL Exception 1.0 + +Exception 1: + +As a special exception you may create a larger work which contains the +output of this application and distribute that work under terms of your +choice, so long as the work is not otherwise derived from or based on +this application and so long as the work does not in itself generate +output that contains the output from this application in its original +or modified form. + +Exception 2: + +As a special exception, you have permission to combine this application +with Plugins licensed under the terms of your choice, to produce an +executable, and to copy and distribute the resulting executable under +the terms of your choice. However, the executable must be accompanied +by a prominent notice offering all users of the executable the entire +source code to this application, excluding the source code of the +independent modules, but including any changes you have made to this +application, under the terms of this license. + + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.GPLv3 b/LICENSE.GPLv3 new file mode 100644 index 0000000..71c4ad4 --- /dev/null +++ b/LICENSE.GPLv3 @@ -0,0 +1,686 @@ + GNU GENERAL PUBLIC LICENSE + + The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. + Contact: http://www.qt.io/licensing/ + + You may use, distribute and copy the Qt Toolkit under the terms of + GNU Lesser General Public License version 3. That license references + the General Public License version 3, that is displayed below. Other + portions of the Qt Toolkit may be licensed directly under this license. + +------------------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/LICENSE.LGPL3 b/LICENSE.LGPL3 new file mode 100644 index 0000000..65c5ca8 --- /dev/null +++ b/LICENSE.LGPL3 @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/LICENSE.LGPLv3 b/LICENSE.LGPLv3 new file mode 100644 index 0000000..6bf924c --- /dev/null +++ b/LICENSE.LGPLv3 @@ -0,0 +1,175 @@ + GNU LESSER GENERAL PUBLIC LICENSE + + The Qt Toolkit is Copyright (C) 2015 The Qt Company Ltd. + Contact: http://www.qt.io/licensing/ + + You may use, distribute and copy the Qt Toolkit under the terms of + GNU Lesser General Public License version 3, which is displayed below. + This license makes reference to the version 3 of the GNU General + Public License, which you can find in the LICENSE.GPLv3 file. + +------------------------------------------------------------------------- + + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright © 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies of this +licensedocument, but changing it is not allowed. + +This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + +0. Additional Definitions. + + As used herein, “this License” refers to version 3 of the GNU Lesser +General Public License, and the “GNU GPL” refers to version 3 of the +GNU General Public License. + + “The Library” refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An “Application” is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A “Combined Work” is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the “Linked +Version”. + + The “Minimal Corresponding Source” for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The “Corresponding Application Code” for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + +1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + +2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort + to ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + +3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this + license document. + +4. Combined Works. + + You may convey a Combined Work under terms of your choice that, taken +together, effectively do not restrict modification of the portions of +the Library contained in the Combined Work and reverse engineering for +debugging such modifications, if you also do each of the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this + license document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of + this License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with + the Library. A suitable mechanism is one that (a) uses at run + time a copy of the Library already present on the user's + computer system, and (b) will operate properly with a modified + version of the Library that is interface-compatible with the + Linked Version. + + e) Provide Installation Information, but only if you would + otherwise be required to provide such information under section 6 + of the GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the Application + with a modified version of the Linked Version. (If you use option + 4d0, the Installation Information must accompany the Minimal + Corresponding Source and Corresponding Application Code. If you + use option 4d1, you must provide the Installation Information in + the manner specified by section 6 of the GNU GPL for conveying + Corresponding Source.) + +5. Combined Libraries. + + You may place library facilities that are a work based on the Library +side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities, conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of + it is a work based on the Library, and explaining where to find + the accompanying uncombined form of the same work. + +6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +as you received it specifies that a certain numbered version of the +GNU Lesser General Public License “or any later version” applies to +it, you have the option of following the terms and conditions either +of that published version or of any later version published by the +Free Software Foundation. If the Library as you received it does not +specify a version number of the GNU Lesser General Public License, +you may choose any version of the GNU Lesser General Public License +ever published by the Free Software Foundation. + +If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the Library. + diff --git a/config.tests/gypsy/gypsy.pro b/config.tests/gypsy/gypsy.pro new file mode 100644 index 0000000..93d56eb --- /dev/null +++ b/config.tests/gypsy/gypsy.pro @@ -0,0 +1,2 @@ +TEMPLATE = app +SOURCES += main.cpp diff --git a/config.tests/gypsy/main.cpp b/config.tests/gypsy/main.cpp new file mode 100644 index 0000000..2b8d3ca --- /dev/null +++ b/config.tests/gypsy/main.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +int main() +{ + GypsyControl *control = gypsy_control_get_default(); + GypsyDevice *device = gypsy_device_new("test"); + GypsySatellite *satellite = gypsy_satellite_new("test"); + + GConfClient *client = gconf_client_get_default(); + g_object_unref(client); + + return 0; +} diff --git a/config.tests/winrt/main.cpp b/config.tests/winrt/main.cpp new file mode 100644 index 0000000..ff1eb81 --- /dev/null +++ b/config.tests/winrt/main.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Devices::Geolocation; +using namespace ABI::Windows::Foundation; + +typedef IAsyncOperationCompletedHandler AccessHandler; + +int main(int, char**) +{ + IGeolocator *locator; + RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(), + reinterpret_cast(&locator)); + return 0; +} diff --git a/config.tests/winrt/winrt.pro b/config.tests/winrt/winrt.pro new file mode 100644 index 0000000..1476abc --- /dev/null +++ b/config.tests/winrt/winrt.pro @@ -0,0 +1,5 @@ +SOURCES += main.cpp +win32-msvc201* { + LIBS += runtimeobject.lib + CONFIG += console +} diff --git a/configure.json b/configure.json new file mode 100644 index 0000000..379d8b4 --- /dev/null +++ b/configure.json @@ -0,0 +1,6 @@ +{ + "subconfigs": [ + "src/positioning", + "src/location" + ] +} diff --git a/dist/changes-5.10.0 b/dist/changes-5.10.0 new file mode 100644 index 0000000..1703cf6 --- /dev/null +++ b/dist/changes-5.10.0 @@ -0,0 +1,62 @@ +Qt 5.10 introduces many new features and improvements as well as bugfixes +over the 5.9.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.10 series is binary compatible with the 5.9.x series. +Applications compiled for 5.9 will continue to run with 5.10. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.10.0 Changes * +**************************************************************************** + + - This release contains only minor code improvements. + +QtLocation +---------- + + - Map: + * [QTBUG-55782] Added methods to add or remove MapItemViews at runtime. + * Visible region now returns a QGeoPolygon. + + - MapGestureArea: + * Added rotation and tilt scroll wheel actions. + + - MapPolyline: + * Added setPath overload taking a QGeoPath. + + - MapboxGL: + * Updated Mapbox GL Native to v1.1.0. + * [QTBUG-62002] Updated map styles. + * [QTBUG-61442] Fix MapParameter dynamic usage. + + - QDeclarativeGeoMap: + * Added setBearing overload to rotate the map around a given + QGeoCoordinate. + * Added alignCoordinateToPoint method to QDeclarativeGeoMap. + + - QDeclarativeGeoMapType: + * [QTBUG-58931] Added CameraCapabilities and metadata properties to + QDeclarativeGeoMapType. + + - QGeoRouteRequest/RouteQuery: + * Add TrafficFeature to the query features. + +QtPositioning +------------- + + - QGeoPath: + * Add ::size() to QGeoPath to retrieve the number of coordinates in the + path. + + - QGeoPolygon: + * Added QGeoPolygon shape. diff --git a/dist/changes-5.10.1 b/dist/changes-5.10.1 new file mode 100644 index 0000000..695c4e3 --- /dev/null +++ b/dist/changes-5.10.1 @@ -0,0 +1,26 @@ +Qt 5.10.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.10.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.10 series is binary compatible with the 5.9.x series. +Applications compiled for 5.9 will continue to run with 5.10. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +This release contains all fixes included in the Qt 5.9.4 release. + +**************************************************************************** +* Qt 5.10.1 Changes * +**************************************************************************** + + - This release contains only minor code improvements. diff --git a/dist/changes-5.11.0 b/dist/changes-5.11.0 new file mode 100644 index 0000000..aae6398 --- /dev/null +++ b/dist/changes-5.11.0 @@ -0,0 +1,116 @@ +Qt 5.11 introduces many new features and improvements as well as bugfixes +over the 5.10.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.11 series is binary compatible with the 5.10.x series. +Applications compiled for 5.10 will continue to run with 5.11. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.11.0 Changes * +**************************************************************************** + +QtLocation +---------- + + - Qt.labs.location + * A new experimental QML plugin introduced, Qt.labs.location, which + offers two new categories of QML elements: Map Objects and the + Navigator type. Both are experimental, not supposed to work in every + setup (check documentation). This plugin can be disabled at configure + time, through the location-labs-plugin feature. + * Map objects are supposed to be QQuickItem-less equivalents of + Map Items. They are supposed to be easy to implement in plugins, and + to introduce minimal overhead. They come with a reference + implementation based on QSGNodes, that is supposed to work in all of the + currently offered raster based plugins. + At the present, the MapboxGL plugin does not offer map objects support. + * The Navigator type is a new QML api to expose turn-by-turn navigation + functions present in plugins. It requires a plugin supporting + one of the Navigation capabilities to actually work. + At the present, none of the open source plugins have these capabilities. + + - MapParameter + * MapParameter type now deprecated and re-registered under the DynamicParameter + name. + + - QGeoServiceProvider/QGeoServiceProviderFactory + * Added QGeoServiceProviderFactoryV2, capable of producing + QNavigationManagerEngine objects. + Note: QNavigationManagerEngine is private API. + + - QGeoPath + * The path property changed type, from QList to + QVariantList. This is binary-compatible with the previous release, + and source-compatible in QML. + It may, however, introduce source incompatibilities when using + the QObject property API in C++ (::property and ::setProperty), for + example in the generation of language bindings. + For the regular user-code, the use case was deemed as rare enough + (since QGeoShape has a public setter/getter for the path) to not cause + any problem. + As the beta phase passed without issues, this change was therefore left in place. + + - QGeoRoute + * QGeoRoute, QGeoRouteSegment and QGeoManeuver private implementation are + now (privately) exported and can be subclassed in plugins to provide + custom private implementations. + * QGeoRouteRequest now allows to specify extra parameters in QVariantMap form. + * RouteQuery (QDeclarativeGeoRouteQuery) now allows to specify extra parameters + through Map/DynamicParameters. + * Added extended attributes to QGeoManeuver/QDeclarativeGeoManeuver. + * [QTBUG-64066] Introduced new Waypoint QML type, intended to replace + QGeoCoordinate as mean to specify waypoints in a RouteQuery. + + - QPlace + * QPlace private implementation is now (privately) exported and can be + subclassed in plugins to provide custom private implementations. + * QPlaceContent::Type has a new value, CustomType. + + - QDeclarativeGeoMap + * [QTBUG-66315] Fixed a crash calling Map.clearMapItems before Map is + initialized. + * Fixed MapGestureArea not emitting when controlled with scroll wheel. + * [QTBUG-66880] Fixed clearData not triggering scene update. + * [QTBUG-67580] Fixed crash in QQuickGeoMapGestureArea. + * [QTBUG-67759] Fixed crash when calling clearData at startup. + + - Map Items + * [QTBUG-66758] Fixed re-set map items rendering stale geometry. + * [QTBUG-38459] Fixed the geometry of map items so that, if layers.enabled + is set to true, the scene graph renderer will not cut the items borders + anymore. + * [QTBUG-66692, QTBUG-66830] Fixed polyline geometry generation. + * [QTBUG-62086, QTBUG-65833] Fixed MapItemView not setting context data + upon item deletion. + * [QTBUG-67765] Fixed interaction with Map Items borders. + + - Plugins + * Mapbox: Added OnlinePlacesFeature, PlaceRecommendationsFeature, SearchSuggestionsFeature + and LocalizedPlacesFeature. + * Mapbox: Added OnlineGeocodingFeature, ReverseGeocodingFeature and LocalizedGeocodingFeature. + * Mapbox/OSM: OSRM backend now returns extended attributes for the maneuvers, such as + bearing_before, bearing_after, instruction, type and modifier. + * Mapbox/OSM: OSRM backend now uses Waypoint's bearing when sending a route request. + * Mapbox/OSM: Added OSRM's {leg,step}_index extra attribute to QGeoManeuver. + * Mapbox: Supported Mapbox Directions API voice & banner instructions. + * MapboxGL: Added map margins support via 'margins' map parameter. + * HERE: the plugin now uses Waypoint's bearing when sending a route request. + * OSM: supported the query limit parameter in the nominatim backend. + + +QtPositioning +---------- + + * [QTBUG-65937] Metatype for QGeoPositionInfo is now declared and registered. + This change introduces a potential source incompatibility, as existing user code may + contain the type registration already. diff --git a/dist/changes-5.11.1 b/dist/changes-5.11.1 new file mode 100644 index 0000000..4ce6df8 --- /dev/null +++ b/dist/changes-5.11.1 @@ -0,0 +1,40 @@ +Qt 5.11.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.11.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.11 series is binary compatible with the 5.10.x series. +Applications compiled for 5.10 will continue to run with 5.11. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.11.1 Changes * +**************************************************************************** + +QtLocation +---------- + +* [QTBUG-63223] Fixed MapPolyline documentation. +* Fixed a crash in Map{Polyline,Polygon,Route}ObjectQsg. +* [QTBUG-68261] Changed nominatim default endpoint base URL to HTTPS. +* Bumped mapbox-gl-native to the latest version, fixing offline tiles support. +* [QTBUG-68121] Supported raster-dem source types in the MapboxGL plugin. +* [QTBUG-68358] Added missing route query parameters to the query in the mapbox plugin. +* [QTBUG-68366] Fixed MapItemView deleting wrong item after changing to use the delegate model internally. +* [QTBUG-68598] Supported dynamic QObject properties in QMapboxGLStyleChange. + + +QtPositioning +------------- + +* [QTBUG-64699] Fixed QGeoPositionInfo timestamp comparison in nmea source. diff --git a/dist/changes-5.11.2 b/dist/changes-5.11.2 new file mode 100644 index 0000000..ffc0b7d --- /dev/null +++ b/dist/changes-5.11.2 @@ -0,0 +1,38 @@ +Qt 5.11.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.11.0 through 5.11.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.11 series is binary compatible with the 5.10.x series. +Applications compiled for 5.10 will continue to run with 5.11. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.11.2 Changes * +**************************************************************************** + +QtLocation +---------- + +* [QTBUG-68086] Documented potential SSL dependency in OSM provider. +* [QTBUG-69031] Added Qt.labs.location::Navigator::trackPositionSource property. +* [QTBUG-69262] Improved the Clipper dependency attribution file. +* [QTBUG-59665] Removed indexing of offline data from OSM plugin. + + +QtPositioning +------------- + +* Reduced minimum NMEA update interval. +* [QTBUG-64699] NMEA sentences now combined by timestamp also in live mode. + diff --git a/dist/changes-5.11.3 b/dist/changes-5.11.3 new file mode 100644 index 0000000..9b09a0c --- /dev/null +++ b/dist/changes-5.11.3 @@ -0,0 +1,32 @@ +Qt 5.11.3 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.11.0 through 5.11.2. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.11 series is binary compatible with the 5.10.x series. +Applications compiled for 5.10 will continue to run with 5.11. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.11.3 Changes * +**************************************************************************** + + QtLocation + ---------- + + - [QTBUG-69617] Fixed Qt.labs.location not linking statically on iOS. + - [QTBUG-71355] Fixed crash when calling QGeoPath::length() on empty + QGeoPath instance. + - Fixed place search matching based on category in mapbox plugin. Previously, + the search results contained places which did not have any category set. + diff --git a/dist/changes-5.2.1 b/dist/changes-5.2.1 new file mode 100644 index 0000000..37a1f55 --- /dev/null +++ b/dist/changes-5.2.1 @@ -0,0 +1,53 @@ +Qt 5.2.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.2.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.2 + +The Qt version 5.2 series is binary compatible with the 5.1.x series. +Applications compiled for 5.1 will continue to run with 5.2. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* General * +**************************************************************************** + +General Improvements +-------------------- + + - [QTBUG-34910] Fixed too long weather status string in weatherinfo + example (UI change). + - [QTBUG-36187] Byte order marker removed from positionpoll plugin + which caused compile errors on some older compilers. + - Fixed make install rules for all examples. + - declarative_core unit test was fixed and re-enabled. + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - [QTBUG-33220] Removed QtQml dependency from QtPositioning. + +**************************************************************************** +* Plugin Specific Changes * +**************************************************************************** + +GeoClue +------- + + - Delay satellite provider until requested. + - Sets the default preferred positioning method to AllPositioningMethods. + - Fix for incorrect accuracy values reported by the plug-in. + diff --git a/dist/changes-5.3.0 b/dist/changes-5.3.0 new file mode 100644 index 0000000..1bf445a --- /dev/null +++ b/dist/changes-5.3.0 @@ -0,0 +1,74 @@ +Qt 5.3 introduces many new features and improvements as well as bugfixes +over the 5.2.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.3 + +The Qt version 5.3 series is binary compatible with the 5.2.x series. +Applications compiled for 5.2 will continue to run with 5.3. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* General * +**************************************************************************** + +General Improvements +-------------------- + + - New SatelliteInfo example added. The example displays the signal + strength of surrounding satellites. The example employs a demo mode on + those platforms which don't provide satellite information + + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - Position (QML): + * Added direction and vertical speed properties. + + - QGeoPositionInfoSource: + * iOS position backend added. + * [QTBUG-34102] Android backend added. Android devices can retrieve + their current position. Network- and Satellite-based providers are + supported. + + - QGeoSatelliteInfoSource: + * [QTBUG-34102] Android backend added. Android devices can retrieve + information about the currently accessible GPS and GLONASS satellites. + + - QGeoRectangle: + * Added QGeoRectangle(QList) constructor added. + * Improved class documentation. + + - QGeoShape: + * Added extendShape(QGeoCoordinate) function. + + - QNmeaPositionInfoSource: + * Added support for reporting position accuracy. + + +**************************************************************************** +* Plugin Specific Changes * +**************************************************************************** + +GeoClue +------- + - [QTBUG-36298] Reports direction of travel and + vertical speed attributes, if supported by the active Geoclue + provider. + - Fixed the emission of the updateTimeout() signal when + position updates do not arrive in a timely manner. + - Position info source provider no longer internally filters + position updates to the requested update interval. + diff --git a/dist/changes-5.3.1 b/dist/changes-5.3.1 new file mode 100644 index 0000000..8913770 --- /dev/null +++ b/dist/changes-5.3.1 @@ -0,0 +1,57 @@ +Qt 5.3.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.3.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.3 + +The Qt version 5.3 series is binary compatible with the 5.2.x series. +Applications compiled for 5.2 will continue to run with 5.3. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* General * +**************************************************************************** + +General Improvements +-------------------- + + - Added PLUGIN_CLASS_NAME variable to all plug-in based projects. This + enables the automatic static linking and deployment of this plug-ins. + + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - QGeoPositionInfoSource: + * WinRT backend added + + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Android +------- + + - [QTBUG-39082] Fixed crash on Galaxy S4 when running satellite and position + updates at the same time. + +iOS +--- + + - [QTBUG-38770] Added "classname" entry to all qmldir files enabling QML + plug-ins when doing static builds on iOS. + diff --git a/dist/changes-5.3.2 b/dist/changes-5.3.2 new file mode 100644 index 0000000..b1981f6 --- /dev/null +++ b/dist/changes-5.3.2 @@ -0,0 +1,50 @@ +Qt 5.3.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.3.0 and Qt 5.3.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.3 + +The Qt version 5.3 series is binary compatible with the 5.2.x series. +Applications compiled for 5.2 will continue to run with 5.3. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - [QTBUG-39843] [iOS] Fixed link error of positioncl and positionpoll + plug-in. + + - [QTBUG-40198] Fixed symbol clash between sensor and position plug-in. + This happened when using static builds of Qt (e.g. on iOS) and the + application used QtSensors and QtPositioning at the same time. + + - Improved weatherinfo example to not hang on the "Loading weather data" + screen if the current QGeoPositionInfoSource instance returned an error + during its startup. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +[iOS] + + - [QTBUG-38300] Fixed wrong value of QGeoPositionInfo attributes if those + attributes were not provided by the platforms CoreLocation framework. + +[Linux] + + - [QTBUG-40425] Fixed missing error report when failing to connect to GeoClue. + diff --git a/dist/changes-5.4.0 b/dist/changes-5.4.0 new file mode 100644 index 0000000..3a14766 --- /dev/null +++ b/dist/changes-5.4.0 @@ -0,0 +1,54 @@ +Qt 5.4 introduces many new features and improvements as well as bugfixes +over the 5.3.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.4 + +The Qt version 5.4 series is binary compatible with the 5.3.x series. +Applications compiled for 5.3 will continue to run with 5.4. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - Position: + * [QTBUG-39547] Added magneticVariation and magneticVariationValid + property to the QML Position type. + + - General: + * Weatherinfo end Flickr example improved. + + - QGeoCircle: + * [QTBUG-41447] Fixed contains() when rounding errors occur + + - QGeoCoordinate: + * [QTBUG-41739] Fixed toString() output in cases when rounding + of longitude/latitude is necessary. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Android +------- + + - [QTBUG-41873] Fixed wrong initialization of QGeoPositionInfoSource::error() + after class instantiation. + +iOS +--- + + - [QTBUG-41827] Fixed position retrieval on iOS 8. This was required due + to the new authorization scheme in CLLocationManager. + diff --git a/dist/changes-5.4.1 b/dist/changes-5.4.1 new file mode 100644 index 0000000..4876ab3 --- /dev/null +++ b/dist/changes-5.4.1 @@ -0,0 +1,29 @@ +Qt 5.4.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.4.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://doc.qt.io/qt-5.4 + +The Qt version 5.4 series is binary compatible with the 5.3.x series. +Applications compiled for 5.3 will continue to run with 5.4. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - PositionSource: + * Fixed a case where an NMEA source file, which is integrated + using qrc, was not loaded. diff --git a/dist/changes-5.4.2 b/dist/changes-5.4.2 new file mode 100644 index 0000000..4f256ef --- /dev/null +++ b/dist/changes-5.4.2 @@ -0,0 +1,28 @@ +Qt 5.4.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.4.0 and Qt 5.4.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://doc.qt.io/qt-5.4 + +The Qt version 5.4 series is binary compatible with the 5.3.x series. +Applications compiled for 5.3 will continue to run with 5.4. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - [QTBUG-44572] Fixed issue where the default timeout for position update + requests was not set on WinRT. diff --git a/dist/changes-5.5.0 b/dist/changes-5.5.0 new file mode 100644 index 0000000..737ce13 --- /dev/null +++ b/dist/changes-5.5.0 @@ -0,0 +1,40 @@ +Qt 5.5 introduces many new features and improvements as well as bugfixes +over the 5.4.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.5 series is binary compatible with the 5.4.x series. +Applications compiled for 5.4 will continue to run with 5.5. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - Added QGeoShape::center() function + - Converted QGeoShape, QGeoCircle, QGeoRectangle & QGeoCoordinate to Q_GADGET. + It simplifies the integration of the above classes into QML as the extensive + QML value type wrapper conversion is implicitly done by Qt QML. The existing + custom wrappers were removed. + - Fixed Debug stream operators for QGeoAreaMonitorInfo, QGeoCoordinate, + QGeoPositionInfo, QGeoSatelliteInfo & QGeoShape following a QDebug related + change in Qt Base. + - [QTBUG-44663] Added updateTimeout() signal to QML PositionSource type + - [QTBUG-44983] Fixed broken build of qtlocation repo when using qmake -r + on OS X + +QtLocation +---------- + + - First Technology Preview release of this module diff --git a/dist/changes-5.5.1 b/dist/changes-5.5.1 new file mode 100644 index 0000000..8b2ce35 --- /dev/null +++ b/dist/changes-5.5.1 @@ -0,0 +1,54 @@ +Qt 5.5.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.5.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://doc.qt.io/qt-5.5/ + +The Qt version 5.5 series is binary compatible with the 5.4.x series. +Applications compiled for 5.4 will continue to run with 5.5. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtLocation +----------- + + - Fixed change signal emission for Map.activeMapType property. + - Improved places-list and places-map examples. + - [QTBUG-47188] Fixed incorrect naming of OSM plugin's parameter. + The documentation mandates the osm prefix which was not used in + the case of the geocoding.host, useragent and routing.host parameters. + The required osm.places.host parameter was not handled at all. + - [QTBUG-47322] Added support for zoom level 19 to OSM plugin. + - Fixed an issue where the incorrect prefetch algorithm was used for map + tile fetching. + +QtPositioning +------------- + + - Improved minor documentation issues. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Windows +------- + + - Added missing QtQuick.Controls import to mapviewer example. + +Linux/gcc +--------- + + - [QTBUG-47427] Fixed compile issue in geoclue plugin when using gcc 4.5. diff --git a/dist/changes-5.6.0 b/dist/changes-5.6.0 new file mode 100644 index 0000000..1402343 --- /dev/null +++ b/dist/changes-5.6.0 @@ -0,0 +1,69 @@ +Qt 5.6 introduces many new features and improvements as well as bugfixes +over the 5.5.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.6 series is binary compatible with the 5.5.x series. +Applications compiled for 5.5 will continue to run with 5.6. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - Removed Blackberry 10 support. + - [QTBUG-40702] Removed dependency towards libgeoclue. The plugin uses the + GeoClue DBus interface. + - Enabled Position plugin for OS X. Previously only the plugin was only working + for iOS. + - [QTBUG-48082] Added QtSerialPort based NMEA position plugin for Windows. + - Improved and fixed the WinRT position backend. Various changes on the WinRT + platform caused the necessity for such and update. + - Fixed endless requests towards Android's LocationManager when stopUpdates() was not + called in pair with startUpdates(). + +QtLocation +---------- + + - First stable release of the Qt Location API. The QML API changed significantly + compared to the Qt 5.5 release. The changes are documented in the official + module documentation under "API changes". + - Improved the Flick and Pinch implementation for QML Map. + - [QTBUG-44809] Fixed tile version handling + - Fixed crash during Map.setVisibleRegion while no plugin has been set. + - Improved error reporting in map, geocoding and routing model APIs. + - QTBUG[36919] Added means to prevent stealing of mouse/touch grabs on map. + - Improved coordinate related animation handling. Previously an animation along + the longitude always used a shortest path direction. Now it is possible to set + the direction for the animation. + - [QTBUG-46147] Fixed crash in QML Map element. + - [QTBUG-44311] Fixed bouncing Map flick on zoom level 2. + - [QTBUG-47020] & [QTBUG-47019] Improved Mouse and keyboard integration of QML Map + - [QTBUG-46388] Fixed Pinch when no MouseArea overlaps QML Map element. + - Fixed various unit test. + - Fixed a variety of minor documentation issues. + - Fixed dysfunctional tile cache for OSM plugin due to the server delivering JPEG + file when PNG files were expected. + - [QTBUG-41187] Unified the default tile cache location on disk. + - Fixed crash in gesture area while receiving wheel events. + - Fixed route requests with excluded areas for HERE plugin. + - Added support for custom map background color. + - Improved Map rendering performance throughout the QML API. + - [QTBUG-49772] Fixed weatherinfo example due to missing appid handling for + openweatermap.org. + - [QTBUG-47292] Added ability to clear the map cache. + - [QTBUG-50060] Improved performance of QML MapPolyline. + - [QTBUG-50240] Fixed OSM routing to handle changed server status code for a + successful route retrieval. + - [QTBUG-50519] Fixed map in mapviewer example when displaying the minimap. diff --git a/dist/changes-5.6.1 b/dist/changes-5.6.1 new file mode 100644 index 0000000..cd579b6 --- /dev/null +++ b/dist/changes-5.6.1 @@ -0,0 +1,38 @@ +Qt 5.6.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.6.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://doc.qt.io/qt-5.6/ + +The Qt version 5.6 series is binary compatible with the 5.5.x series. +Applications compiled for 5.5 will continue to run with 5.6. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtLocation +---------- + + - Improved the example and class documentation + - Consolidated various qmake project files across the module + - [QTBUG-51541] Fixed compile issue on linux/gcc-4.8 when using C++98 + - Improved compression rate of various screenshots throughout the documentation + +QtPositioning +------------- + + - [QTBUG-38802] Added default capability (location) for WinRT + - [QTBUG-53059] Fixed crash on iOS/OS X due to incorrectly initialized member + variable in the positioning backend + diff --git a/dist/changes-5.6.2 b/dist/changes-5.6.2 new file mode 100644 index 0000000..9f2a6f9 --- /dev/null +++ b/dist/changes-5.6.2 @@ -0,0 +1,55 @@ +Qt 5.6.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.6.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.6 series is binary compatible with the 5.5.x series. +Applications compiled for 5.5 will continue to run with 5.6. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtLocation +---------- + + - Fixed autotests containing bugs not showing in the tests + - [QTBUG-31797][QTBUG-53455] Fixed the QtLocation autotest framework by + replacing waitForRendering() with waitForPolished() + - Disabled explicit example installation + - [QTBUG-52514] Fixed QML Map not working with repeaters + - [QTBUG-53128] Fixed QML Map items not updating on window resize + - [QTBUG-52075] Fixed QML Map items losing focus during fast dragging + - [QTBUG-52301] Prevented flickering upon MapItemView's model reset + - [QTBUG-54141] Fixed QML Map setVisibleRegion failing in some cases + - Fixed a potential memory leak in QGeoTiledMappingManagerEngine + - [QTBUG-19929] Prevented generating MapCircles with invalid radius + - [QTBUG-54599] Added support for fetching OSM provider information from + a remote repository + - [QTBUG-54337] Fixed bounding calculation for QGeoCircle + - Added a minimal QML Map usage example + - Added support for dynamically remove unavailable providers from the OSM + plugin + - [QTBUG-55081] Fixed accessing MapPolyline before it is added to a Map + - [QTBUG-54964] Fixed invisible copyright notice on Android using Holo theme + - Improved macOS documentation + - Fixed the geocoding in the MapViewer example + - [QTBUG-55371] Fixed OSM plugin geocoding feature + +QtPositioning +------------- + + - [QTBUG-54026] Reduced Android minimum update interval + - Improved Android 5.0+ compatibility + diff --git a/dist/changes-5.6.3 b/dist/changes-5.6.3 new file mode 100644 index 0000000..4709ff2 --- /dev/null +++ b/dist/changes-5.6.3 @@ -0,0 +1,38 @@ +Qt 5.6.3 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.6.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.6 series is binary compatible with the 5.5.x series. +Applications compiled for 5.5 will continue to run with 5.6. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtLocation +---------- + + - [QTBUG-56099] Updated HERE plugin base address + - [QTBUG-56119] Added OSRMv5 support to the osm plugin + - [QTBUG-57027] Fixed fitViewportToGeoShape on rectangles crossing the date + line. + - Updated HERE plugin geocoding endpoints + + +QtPositioning +------------- + + - [QTBUG-54844] Fixed error status for QGeoPositionInfoSourceAndroid + - [QTBUG-56623] WinRT backend now uses backend-provided timestamps diff --git a/dist/changes-5.7.0 b/dist/changes-5.7.0 new file mode 100644 index 0000000..d1fdb43 --- /dev/null +++ b/dist/changes-5.7.0 @@ -0,0 +1,53 @@ +Qt 5.7 introduces many new features and improvements as well as bugfixes +over the 5.6.x series. Also, there is a change in the licensing terms. +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.7 series is binary compatible with the 5.6.x series. +Applications compiled for 5.6 will continue to run with 5.7. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Important License Changes * +**************************************************************************** + + This module is no longer available under LGPLv2.1. The libraries are + now available under the following licenses: + * Commercial License + * GNU General Public License v2.0 (LICENSE.GPL2) and later + * GNU Lesser General Public License v3.0 (LICENSE.LGPL3) + + The tools are now available under the following licenses: + * Commercial License + * GNU General Public License 3.0 (LICENSE.GPL3) with exceptions + described in The Qt Company GPL Exception 1.0 (LICENSE.GPL3-EXCEPT) + +**************************************************************************** +* Library * +**************************************************************************** + +QtPositioning +------------- + + - Added support for tvOs to iOS/OS X positioning engine + - Added qHash() function for QGeoCoordinate + - Fixed negative QGeoCoordinate.azimuthTo results + - Added experimental support for GeoPosition API on Windows desktop + running Windows 8 or later + +QtLocation +---------- + + - [QTBUG-47025] Added option to hide map data copyright information + - Adjusted minimum map zoom level to prevent gray bounds. It prevents the map + from being smaller than the canvas size. + - Fixed several internal performance issues diff --git a/dist/changes-5.7.1 b/dist/changes-5.7.1 new file mode 100644 index 0000000..2d111d5 --- /dev/null +++ b/dist/changes-5.7.1 @@ -0,0 +1,45 @@ +Qt 5.7.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.7.0. + +Qt 5.7.1 contains a merge from Qt 5.6.2 and all changes in Qt 5.6.2 are +also in Qt 5.7.1. For more see changes-5.6.2. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.7 series is binary compatible with the 5.6.x series. +Applications compiled for 5.6 will continue to run with 5.7. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtLocation +---------- + +- [QTBUG-55535] Fixed map polygon not working correctly when zooming. +- [QTBUG-55964] Fixed rendering of polylines and polygons at the edge + of the map. +- [QTBUG-52610] Fixed incorrect map polyline drawing. +- [QTBUG-52076] Fixed crashes on tessellation of self-intersecting + GeoPolygons. +- [QTBUG-52030] Fixed that map's center and zoom were not initialized when + used in a layout. +- [QTBUG-52514] Fixed not working MapPolygon inside Repeater. +- [QTBUG-53128] Fixed map objects moving incorrectly on map resize. +- [QTBUG-52075] Fixed losing map item focus while dragging. + +QtPositioning +------------- + +- [QTBUG-54506] Fixed unneeded dependency on Activity for Android Platform. diff --git a/dist/changes-5.8.0 b/dist/changes-5.8.0 new file mode 100644 index 0000000..1c2a5b4 --- /dev/null +++ b/dist/changes-5.8.0 @@ -0,0 +1,70 @@ +Qt 5.8 introduces many new features and improvements as well as bugfixes +over the 5.7.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + + http://doc.qt.io/qt-5/index.html + +The Qt version 5.8 series is binary compatible with the 5.7.x series. +Applications compiled for 5.7 will continue to run with 5.8. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtLocation +----------- + + - Added geoservices plugin to support ESRI mapping, geocoding and routing + services. + - Improved HERE geoservice plugin code base (improved warning) and converted + the HERE endpoints to newer versions (away from Nokia endpoints. At the same time + China specific URLs were removed. + - Improved Mapbox plugin to cater for better cache customization and + to support the standard box map modes. + - Renamed various internal C++ functions to improve code readability + - Added better high DPI support in the various geoservice provider plugins. + For more details see QTBUG-53318, QTBUG-48868 and QTBUG-36949. + - [QTBUG-45284] Added offline data support and improved cache handling in OSM + geoservice plugin + - Added routing support to Mapbox plugin + - Added server side provider support for QtLocation. This enables the quicker + selection of alternative providers by already deployed OSM applications in case + an OSM based default provider changes T&Cs. + - Removed QtSystemInfo dependency from QtLocation + - [QTBUG-56213] Adapted map related mouse/touch behavior following + changes to general mouse/touch handling in Qt QML + - Adapted QtLocation and QtPositioning to the Qt Lite related build system changes + - Marked QGeoMapPrivate as private export + - Added support for unitary tile caching. This enables use cases whereby the cache + size can be defined in number of tiles rather then bytes. + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Android +------- + + - [QTBUG-55988] Added capability to ask for Location permissions at runtime. This is + required since Android 7.0. + +WinRT +----- + + - Fixed a name space related compile problem in the positioning plugin for WinRT + - Migrated the QtLocation rendering code to the new scenegraph changes enabling + Direct3D 12 + - [QTBUG-54474] Added GeolocationAccessStatus existence check + - [QTBUG-56340] Added direction information to position updates + - [QTBUG-56623] Changed positioning plugin to use the platforms positioning time + stamp rather than a programmatically acquired time stamp at the time of the callback. + Effectively, this forwards time stamps from the GPS satellite to the user application. + - [QTBUG-53925] Enabled WinRT positioning backend for MSVC2013/2015 diff --git a/dist/changes-5.9.0 b/dist/changes-5.9.0 new file mode 100644 index 0000000..46faf68 --- /dev/null +++ b/dist/changes-5.9.0 @@ -0,0 +1,131 @@ +Qt 5.9 introduces many new features and improvements as well as bugfixes +over the 5.8.x series. For more details, refer to the online documentation +included in this distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* General * +**************************************************************************** + +Third-Party Code +---------------- + + - Added Mapbox GL Native as a third-party component to + src/3rdparty/mapbox-gl-native. + +**************************************************************************** +* Library * +**************************************************************************** + +QtLocation +----------- + + - Added boundingGeoRectangle to QGeoShape to return a geographical bounding + box in form of a QGeoRectangle. + - Added QGeoPath as a new geo shape. + - Added a new QML type, MapParameter, to control plugin-specific map features + at runtime. + - Deprecated QGeoShape::extendShape. + - Renamed the QGeoProjection class into QWebMercator. + - Added new private API, QGeoProjection, for the coordinate <-> screen projection + conversion, removing it from QGeoTiledMapScene. + - Moved CacheAreas enum from QGeoFileTileCache into QAbstractGeoTileCache. + - Map.zoomLevel now always refers to a normalized tile size of 256x256 pixels. + Conversions to/from different tile sizes are performed internally by QGeoMap. + - Added clipping support to Clip2Tri 3rd party library through clipper, to protect Qt from + clipper invocations that throw. + - Updated clipper lib to version 6.4. + - QGeoProjectionWebMercator now uses a projection matrix to project to item position. + - [QTBUG-58124] Fixed a bug on destruction of the OSM plugin causing segfault. + - Added rotation, tilt and field of view properties to QDeclarativeGeoMap. + - Changed map items to be always positioned/wrapped based on their geo left bound. + - Changed the Map items opacity ramp values from 2.0 -> 3.0 to 1.5 -> 2.5. + - QDeclarativeGeoMap::setVisibleRegion now handles QGeoPath as well. + - Map Items geo data is now managed by a contained QGeoShape, which also handles geographical + transformations such as translations. + - Moved the declarative implementation from imports/ into location/declarativemaps and + location/declarativeplaces, privately exporting the classes. + - QQuickGeoMapGestureArea now correctly works with tilted/rotated maps. + - Added two new gestures to QQuickGeoMapGestureArea, two fingers rotation and two fingers tilting. + - Added the infrastructure to allow a QGeoMap to be responsible for drawing map items. + - Added a new QML type, MapCopyrightNotice, backed by the now exposed QDeclarativeGeoMapCopyrightNotice. + - MapCopyrightNotice can now be styled using CSS. + - Added a new plugin, MapboxGL, based on the mapbox-gl-native 3rd party library. + - The MapboxGL plugin now ships with a built-in development token. + - Added a new QML type, MapItemGroup, to group multiple map items in a new meta map item. + - Added the new mapItemOpacity method to QGeoMapItemBase, to account for the opacity of a MapItemGroup too. + - Added the new fitViewportToVisibleMapItems method to QDeclarativeGeoMap. + - Adapted mapviewer example to enable changing tilting/rotation/FoV. + - QGeoTiledMap now uses anisotropic filtering when available. + - Unblacklisted some previously blacklisted autotests now fixed in qtdeclarative. + - Blacklisted flick autotests on windows due to platform-induced flakiness. + - Added support to the MapboxGL plugin to natively renders map polylines, polygons and rectangles. + - Added support to the MapboxGL plugin to render both via FBO and via QSGRenderNode. + - QGeoCameraCapabilities are now specific to a QGeoMapType, and not any longer fixed to a plugin. + - DevicePixelRatio is now considered before enabling mipmapping so that mipmapping remains off if + tiles do not need to be minificated. + - Changed QGeoTiledMap default FoV from 90 to 45 degrees. + - [QTBUG-59417] Fixed QGeoTiledMapScene viewing frustum calculation. + - Added a new mapReady signal to QDeclarativeGeoMap to notify when the map becomes ready. + - Added ICU support to MapboxGL plugin, depending on its availability. + - [QTBUG-58821] Removed dependency on QtWidgets. + - Added a new plugin, itemsoverlay, to provide a transparent canvas to add only map items. + - MapQuickItem now correctly tilts and rotates when the zoomLevel property is set. + - [QTBUG-59259] Increased QGeoCoordinate debug operator precision. + - Improved the mapviewer example, also using QtQuick.Controls2 sliders. + - QtPositioning now also compiler-optimized when building in release. + - [QTBUG-59503] Flick autotests skipped on windows. + - [QTBUG-59479] Mercator-projected qgeocoordinates in map items are now cached. + - Removed pathPropertyChanged() from Polyline,Polygon. + - [QTBUG-23659] Enabled overzooming tiles in qgeotiledmapscene. + - [QTBUG-59416] keepTouchGrab now considered when handing mouse events in QDeclarativeGeoMap. + - Tile formats now proactively converted to ARGB32_Premultiplied. + - Added possibility to disable prefetching. + - Added parameter to control prefetching in tile-based geoservice plugins. + - Disabled warnings in third-party code. + - Improved flicking behavior of QQuickGeoMapGestureArea. + - [QTBUG-60021] Map update now triggered after initialization. + - QGeoMap pointers now protected with QPointer. + - QtLocation now marked as warning-free. + - [QTBUG-58801] Fixed copyright notice not showing before map type is changed. + - Added overzoomEnabled to QGeoCameraCapabilities. + - Various fixes for building with various QtLite configurations. + - QGeoTileFetcher::handleReply now virtual. + - [QTBUG-60266] declarative_ui autotests disabled for boot2qt. + + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +iOS +--- + + - [QTBUG-52014][QTBUG-59275] Allow background updates if such capability + is present in infoDict. + + +WinRT +----- + + - [QTBUG-57288] Remove support for WinRT 8.1 and Windows Phone 8.1. + - [QTBUG-60299] Added error handling for GeoPositionInfoSource creation. + + +Android +------- + + - [QTBUG-59010] Added guard against unprotected javaVM pointer usage. + - [QTBUG-59158] UTC flag for Android position timestamps now set. diff --git a/dist/changes-5.9.1 b/dist/changes-5.9.1 new file mode 100644 index 0000000..58b4ac7 --- /dev/null +++ b/dist/changes-5.9.1 @@ -0,0 +1,43 @@ +Qt 5.9.1 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.9.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + + - [QTBUG-60007] Fixed plugin resources not loading on static builds + - [QTBUG-60881] Fixed building on configurations without OpenGL + - Fixed an incorrect condition in QDeclarativeGeoMap::geometryChanged() + causing a crash in some scenarios + - MapItems can now be set to render under specific style layers with the + MapboxGL plugin + - [QTBUG-60821] Added detailed 3rd party attributions for the 3rd party + mapbox-gl-native library + - [QTBUG-61070] MapQuickItem's setCoordinate now checks for coordinate's + validity. + - [QTBUG-61087] OpenGL is now a requirement to build the MapboxGL plugin + - LocationSingleton now allows to create a qgeopath from a list of coordinates, + and not only empty qgeopaths + - Updated QtLocation qmltypes + - [QTBUG-61266] Updated QtPositioning qmltypes, adding 5.9 version + - [QTBUG-57690] Fixed Map.visibleRegion in the case that all the map is visible + - Various documentation fixes + - The MapboxGL plugin now uses the MapItem's objectName (if present) when creating + a mapbox layer name for it + diff --git a/dist/changes-5.9.2 b/dist/changes-5.9.2 new file mode 100644 index 0000000..ac6321d --- /dev/null +++ b/dist/changes-5.9.2 @@ -0,0 +1,56 @@ +Qt 5.9.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.9.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.9.2 Changes * +**************************************************************************** + +QtLocation +---------- + - [QTBUG-61070] mapQuickItems aren't drawn any longer if coordinate is invalid + - [QTBUG-61813] Fixed Map.toCoordinate always clamping y values to 0 + - [QTBUG-61727] Fixed dragging items out of map bounds + - [QTBUG-62098] Fixed plugins build dependency + - [QTBUG-62122] Fixed potential memleak on QDeclarativeGeoMap destruction + - [QTBUG-62154] Fixed MapCircle artifacts with small radiuses + - Restored usage of setProperty on Map.fitViewportToGeoShape + - [QTBUG-57690] Fixed visible region computation in QGeoProjectionWebMercator + - Fixed QDeclarativeGeoMap::populateMap duplicating items + - Fixed GeocodeModel not autoUpdating if plugin attaches late + - [QTBUG-62075] Fixed PluginParameter not working with script as property values + - Added a configure feature for each geoservice plugin + - Fixed file tile cache not honoring disk cache size of 0 + - [QTBUG-63124] Fixed wrong clipper dependency due to incorrect qmake syntax + - [QTBUG-63251] Fixed Map.fromCoordinate returning wrong values during tilting + + - OSM + * [QTBUG-61637] Fixed OSM plugin not working with providersrepository.disabled = true + * [QTBUG-63033] Fixed disabling redirection for providers that have no local fallback + + - MapboxGL: + * [QTBUG-61442] Fixed MapParameter dynamic usage + * [QTBUG-62454] Bumped Mapbox GL to v1.1.0 to prevent a crash + * [QTBUG-62861] Fixed broken native text rendering with Mapbox GL plugin + * [QTBUG-58869] Fixed Mapbox GL not rendering circles natively + * Fixed Mapbox GL plugin not respecting MapItem visibility and opacity + + +QtPositioning +------------- + - [QTBUG-62778] Fixed PositionSource never turning active on Android in some cases diff --git a/dist/changes-5.9.3 b/dist/changes-5.9.3 new file mode 100644 index 0000000..98a9b30 --- /dev/null +++ b/dist/changes-5.9.3 @@ -0,0 +1,41 @@ +Qt 5.9.3 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.9.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.9.3 Changes * +**************************************************************************** + +QtLocation +---------- + + - [QTBUG-63374] Fixed MapBoxGL crashing when no OpenGLContext can be created + - Updated all FDL and BSD license header throughout the module + - [QTBUG-62417] Forced MapBoxGL to only build on Windows when Qt is build + with Angle + - [QTBUG-63926] Added check to MapBoxGL handling corner cases of Polygon + placement + - [QTBUG-63928] Fixed missing consideration of alpha channel when + setting DeclarativeRectangleMapItem opacity. + +QtPositioning +------------- + + - Updated all FDL and BSD license header throughout the module + - Added missing null check to Android positioning plug-in + diff --git a/dist/changes-5.9.4 b/dist/changes-5.9.4 new file mode 100644 index 0000000..6393129 --- /dev/null +++ b/dist/changes-5.9.4 @@ -0,0 +1,31 @@ +Qt 5.9.4 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.9.0. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +http://doc.qt.io/qt-5/index.html + +The Qt version 5.9 series is binary compatible with the 5.8.x series. +Applications compiled for 5.8 will continue to run with 5.9. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Qt 5.9.4 Changes * +**************************************************************************** + +QtLocation +---------- + + - [QTBUG-63013] Upgraded Mapbox GL to fix SQLite query binding. + - [QTBUG-58589] Added plugin dependencies to the mapviewer example. + - [QTBUG-64632] Upgraded Mapbox GL to fix remove GLES limitations on Windows. + This upgrade also fixed building Mapbox GL with MinGW. + - [QTBUG-57147] Fixed license headers. diff --git a/examples/examples.pro b/examples/examples.pro new file mode 100644 index 0000000..da770da --- /dev/null +++ b/examples/examples.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +qtHaveModule(positioning) { + SUBDIRS += positioning + + qtHaveModule(location): SUBDIRS += location +} diff --git a/examples/location/location.pro b/examples/location/location.pro new file mode 100644 index 0000000..0b4b233 --- /dev/null +++ b/examples/location/location.pro @@ -0,0 +1,10 @@ +TEMPLATE = subdirs + +qtHaveModule(quick) { + SUBDIRS += places \ + places_list \ + places_map \ + mapviewer \ + minimal_map \ + planespotter +} diff --git a/examples/location/mapviewer/doc/images/mapviewer.png b/examples/location/mapviewer/doc/images/mapviewer.png new file mode 100644 index 0000000000000000000000000000000000000000..b55d7ff3574a714e4dc5da5a94442292f7ddcaed GIT binary patch literal 176944 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 1 )) { + showMessage(qsTr("Ambiguous geocode"), + count + " " + qsTr("results found for the") + + " " + st + " " +qsTr("point, please specify location")) + goButton.enabled = true; + } + } + } + } +} diff --git a/examples/location/mapviewer/forms/RouteAddressForm.ui.qml b/examples/location/mapviewer/forms/RouteAddressForm.ui.qml new file mode 100644 index 0000000..2254382 --- /dev/null +++ b/examples/location/mapviewer/forms/RouteAddressForm.ui.qml @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + property alias fromStreet: fromStreet + property alias fromCountry: fromCountry + property alias toStreet: toStreet + property alias toCity: toCity + property alias toCountry: toCountry + property alias fromCity: fromCity + property alias goButton: goButton + property alias clearButton: clearButton + property alias cancelButton: cancelButton + + Rectangle { + id: tabRectangle + y: 20 + height: tabTitle.height * 2 + color: "#46a2da" + anchors.rightMargin: 0 + anchors.leftMargin: 0 + anchors.left: parent.left + anchors.right: parent.right + + Label { + id: tabTitle + color: "#ffffff" + text: qsTr("Route Address") + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Item { + id: item2 + anchors.rightMargin: 20 + anchors.leftMargin: 20 + anchors.bottomMargin: 20 + anchors.topMargin: 20 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabRectangle.bottom + + GridLayout { + id: gridLayout3 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + id: label1 + text: qsTr("From") + font.bold: true + anchors.horizontalCenter: parent.horizontalCenter + Layout.columnSpan : 2 + } + + Label { + id: label2 + text: qsTr("Street") + } + + TextField { + id: fromStreet + Layout.fillWidth: true + } + + Label { + id: label3 + text: qsTr("City") + } + + TextField { + id: fromCity + Layout.fillWidth: true + } + + Label { + id: label7 + text: qsTr("Country") + } + + TextField { + id: fromCountry + Layout.fillWidth: true + } + + Label { + id: label6 + text: qsTr("To") + font.bold: true + anchors.horizontalCenter: parent.horizontalCenter + Layout.columnSpan: 2 + } + + Label { + id: label4 + text: qsTr("Street") + } + + TextField { + id: toStreet + Layout.fillWidth: true + } + + Label { + id: label5 + text: qsTr("City") + } + + TextField { + id: toCity + Layout.fillWidth: true + } + + Label { + id: label8 + text: qsTr("Country") + } + + TextField { + id: toCountry + Layout.fillWidth: true + } + + RowLayout { + id: rowLayout1 + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignRight + + Button { + id: goButton + text: qsTr("Proceed") + } + + Button { + id: clearButton + text: qsTr("Clear") + } + + Button { + id: cancelButton + text: qsTr("Cancel") + } + } + + Item { + Layout.fillHeight: true + Layout.columnSpan: 2 + } + } + } +} diff --git a/examples/location/mapviewer/forms/RouteCoordinate.qml b/examples/location/mapviewer/forms/RouteCoordinate.qml new file mode 100644 index 0000000..6d4bd65 --- /dev/null +++ b/examples/location/mapviewer/forms/RouteCoordinate.qml @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtPositioning 5.5 + +RouteCoordinateForm { + property variant toCoordinate + property variant fromCoordinate + signal showRoute(variant startCoordinate,variant endCoordinate) + signal closeForm() + + goButton.onClicked: { + var startCoordinate = QtPositioning.coordinate(parseFloat(fromLatitude.text), + parseFloat(fromLongitude.text)); + var endCoordinate = QtPositioning.coordinate(parseFloat(toLatitude.text), + parseFloat(toLongitude.text)); + if (startCoordinate.isValid && endCoordinate.isValid) { + goButton.enabled = false; + showRoute(startCoordinate,endCoordinate) + } + } + + clearButton.onClicked: { + fromLatitude.text = "" + fromLongitude.text = "" + toLatitude.text = "" + toLongitude.text = "" + } + + cancelButton.onClicked: { + closeForm() + } + + Component.onCompleted: { + fromLatitude.text = "" + fromCoordinate.latitude + fromLongitude.text = "" + fromCoordinate.longitude + toLatitude.text = "" + toCoordinate.latitude + toLongitude.text = "" + toCoordinate.longitude + } +} + diff --git a/examples/location/mapviewer/forms/RouteCoordinateForm.ui.qml b/examples/location/mapviewer/forms/RouteCoordinateForm.ui.qml new file mode 100644 index 0000000..f71d251 --- /dev/null +++ b/examples/location/mapviewer/forms/RouteCoordinateForm.ui.qml @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + property alias fromLatitude: fromLatitude + property alias fromLongitude: fromLongitude + property alias toLatitude: toLatitude + property alias toLongitude: toLongitude + property alias clearButton: clearButton + property alias goButton: goButton + property alias cancelButton: cancelButton + + Rectangle { + id: tabRectangle + y: 20 + height: tabTitle.height * 2 + color: "#46a2da" + anchors.rightMargin: 0 + anchors.leftMargin: 0 + anchors.left: parent.left + anchors.right: parent.right + + Label { + id: tabTitle + color: "#ffffff" + text: qsTr("Route Coordinates") + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Item { + id: item2 + anchors.rightMargin: 20 + anchors.leftMargin: 20 + anchors.bottomMargin: 20 + anchors.topMargin: 20 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabRectangle.bottom + + GridLayout { + id: gridLayout3 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + id: label1 + text: qsTr("From") + anchors.horizontalCenter: parent.horizontalCenter + font.bold: true + Layout.columnSpan : 2 + } + + Label { + id: label2 + text: qsTr("Latitude") + } + + TextField { + id: fromLatitude + Layout.fillWidth: true + } + + Label { + id: label3 + text: qsTr("Longitude") + } + + TextField { + id: fromLongitude + Layout.fillWidth: true + } + + Label { + id: label6 + text: qsTr("To") + anchors.horizontalCenter: parent.horizontalCenter + font.bold: true + Layout.columnSpan: 2 + } + + Label { + id: label4 + text: qsTr("Latitude") + } + + TextField { + id: toLatitude + Layout.fillWidth: true + } + + Label { + id: label5 + text: qsTr("Longitude") + } + + TextField { + id: toLongitude + Layout.fillWidth: true + } + + RowLayout { + id: rowLayout1 + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignRight + Button { + id: goButton + text: qsTr("Proceed") + } + + Button { + id: clearButton + text: qsTr("Clear") + } + + Button { + id: cancelButton + text: qsTr("Cancel") + } + } + Item { + Layout.fillHeight: true + Layout.columnSpan: 2 + } + } + } +} diff --git a/examples/location/mapviewer/forms/RouteList.qml b/examples/location/mapviewer/forms/RouteList.qml new file mode 100644 index 0000000..9cf889d --- /dev/null +++ b/examples/location/mapviewer/forms/RouteList.qml @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import "../helper.js" as Helper + +//! [routeinfomodel0] +ListView { +//! [routeinfomodel0] + property variant routeModel + property string totalTravelTime + property string totalDistance + signal closeForm() +//! [routeinfomodel1] + interactive: true + model: ListModel { id: routeInfoModel } + header: RouteListHeader {} + delegate: RouteListDelegate{ + routeIndex.text: index + 1 + routeInstruction.text: instruction + routeDistance.text: distance + } +//! [routeinfomodel1] + footer: Button { + anchors.horizontalCenter: parent.horizontalCenter + text: qsTr("Close") + onClicked: { + closeForm() + } + } + + Component.onCompleted: { + //! [routeinfomodel2] + routeInfoModel.clear() + if (routeModel.count > 0) { + for (var i = 0; i < routeModel.get(0).segments.length; i++) { + routeInfoModel.append({ + "instruction": routeModel.get(0).segments[i].maneuver.instructionText, + "distance": Helper.formatDistance(routeModel.get(0).segments[i].maneuver.distanceToNextInstruction) + }); + } + } + //! [routeinfomodel2] + totalTravelTime = routeModel.count == 0 ? "" : Helper.formatTime(routeModel.get(0).travelTime) + totalDistance = routeModel.count == 0 ? "" : Helper.formatDistance(routeModel.get(0).distance) + } +//! [routeinfomodel3] +} +//! [routeinfomodel3] diff --git a/examples/location/mapviewer/forms/RouteListDelegate.qml b/examples/location/mapviewer/forms/RouteListDelegate.qml new file mode 100644 index 0000000..81fe2e0 --- /dev/null +++ b/examples/location/mapviewer/forms/RouteListDelegate.qml @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + id: root + property bool checked: false + property alias routeInstruction: instructionLabel + property alias routeDistance: distanceLabel + property alias routeIndex: indexLabel + + width: parent.width + height: indexLabel.height * 2 + + RowLayout { + spacing: 10 + anchors.left: parent.left + anchors.leftMargin: 30 + anchors.verticalCenter: parent.verticalCenter + Label { + id: indexLabel + } + Label { + id: instructionLabel + wrapMode: Text.Wrap + } + Label { + id: distanceLabel + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 15 + height: 1 + color: "#46a2da" + } +} + + + diff --git a/examples/location/mapviewer/forms/RouteListHeader.qml b/examples/location/mapviewer/forms/RouteListHeader.qml new file mode 100644 index 0000000..968395b --- /dev/null +++ b/examples/location/mapviewer/forms/RouteListHeader.qml @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Item { + property alias travelTime: travelTimeLabel + property alias distance: distanceLabel + width: parent.width + height: tabTitle.height * 3.0 + + Rectangle { + id: tabRectangle + y: tabTitle.height + height: tabTitle.height * 2 - 1 + color: "#46a2da" + anchors.left: parent.left + anchors.right: parent.right + + Label { + id: tabTitle + color: "#ffffff" + text: qsTr("Route Information") + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + + Label { + id: travelTimeLabel + text: totalTravelTime + color: "#ffffff" + font.bold: true + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + } + + Label { + id: distanceLabel + text: totalDistance + color: "#ffffff" + font.bold: true + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + } + } +} diff --git a/examples/location/mapviewer/helper.js b/examples/location/mapviewer/helper.js new file mode 100644 index 0000000..faef9d7 --- /dev/null +++ b/examples/location/mapviewer/helper.js @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +function roundNumber(number, digits) +{ + var multiple = Math.pow(10, digits); + return Math.round(number * multiple) / multiple; +} + +function formatTime(sec) +{ + var value = sec + var seconds = value % 60 + value /= 60 + value = (value > 1) ? Math.round(value) : 0 + var minutes = value % 60 + value /= 60 + value = (value > 1) ? Math.round(value) : 0 + var hours = value + if (hours > 0) value = hours + "h:"+ minutes + "m" + else value = minutes + "min" + return value +} + +function formatDistance(meters) +{ + var dist = Math.round(meters) + if (dist > 1000 ){ + if (dist > 100000){ + dist = Math.round(dist / 1000) + } + else{ + dist = Math.round(dist / 100) + dist = dist / 10 + } + dist = dist + " km" + } + else{ + dist = dist + " m" + } + return dist +} diff --git a/examples/location/mapviewer/main.cpp b/examples/location/mapviewer/main.cpp new file mode 100644 index 0000000..73bc937 --- /dev/null +++ b/examples/location/mapviewer/main.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +static bool parseArgs(QStringList& args, QVariantMap& parameters) +{ + + while (!args.isEmpty()) { + + QString param = args.takeFirst(); + + if (param.startsWith("--help")) { + QTextStream out(stdout); + out << "Usage: " << endl; + out << "--plugin. - Sets parameter = value for plugin" << endl; + out.flush(); + return true; + } + + if (param.startsWith("--plugin.")) { + + param.remove(0, 9); + + if (args.isEmpty() || args.first().startsWith("--")) { + parameters[param] = true; + } else { + + QString value = args.takeFirst(); + + if (value == "true" || value == "on" || value == "enabled") { + parameters[param] = true; + } else if (value == "false" || value == "off" + || value == "disable") { + parameters[param] = false; + } else { + parameters[param] = value; + } + } + } + } + return false; +} + +int main(int argc, char *argv[]) +{ +#if QT_CONFIG(library) + const QByteArray additionalLibraryPaths = qgetenv("QTLOCATION_EXTRA_LIBRARY_PATH"); + for (const QByteArray &p : additionalLibraryPaths.split(':')) + QCoreApplication::addLibraryPath(QString(p)); +#endif + QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication application(argc, argv); + + QVariantMap parameters; + QStringList args(QCoreApplication::arguments()); + + // Fetch tokens from the environment, if present + const QByteArray mapboxMapID = qgetenv("MAPBOX_MAP_ID"); + const QByteArray mapboxAccessToken = qgetenv("MAPBOX_ACCESS_TOKEN"); + const QByteArray hereAppID = qgetenv("HERE_APP_ID"); + const QByteArray hereToken = qgetenv("HERE_TOKEN"); + const QByteArray esriToken = qgetenv("ESRI_TOKEN"); + + if (!mapboxMapID.isEmpty()) + parameters["mapbox.map_id"] = QString::fromLocal8Bit(mapboxMapID); + if (!mapboxAccessToken.isEmpty()) { + parameters["mapbox.access_token"] = QString::fromLocal8Bit(mapboxAccessToken); + parameters["mapboxgl.access_token"] = QString::fromLocal8Bit(mapboxAccessToken); + } + if (!hereAppID.isEmpty()) + parameters["here.app_id"] = QString::fromLocal8Bit(hereAppID); + if (!hereToken.isEmpty()) + parameters["here.token"] = QString::fromLocal8Bit(hereToken); + if (!esriToken.isEmpty()) + parameters["esri.token"] = QString::fromLocal8Bit(esriToken); + + if (parseArgs(args, parameters)) + return 0; + if (!args.contains(QStringLiteral("osm.useragent"))) + parameters[QStringLiteral("osm.useragent")] = QStringLiteral("QtLocation Mapviewer example"); + + QQmlApplicationEngine engine; + engine.addImportPath(QStringLiteral(":/imports")); + engine.load(QUrl(QStringLiteral("qrc:///mapviewer.qml"))); + QObject::connect(&engine, SIGNAL(quit()), qApp, SLOT(quit())); + + QObject *item = engine.rootObjects().first(); + Q_ASSERT(item); + + QMetaObject::invokeMethod(item, "initializeProviders", + Q_ARG(QVariant, QVariant::fromValue(parameters))); + + return application.exec(); +} diff --git a/examples/location/mapviewer/map/CircleItem.qml b/examples/location/mapviewer/map/CircleItem.qml new file mode 100644 index 0000000..d06aeb7 --- /dev/null +++ b/examples/location/mapviewer/map/CircleItem.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.5 +import QtLocation 5.6 + +//TODO: remove/refactor me when items are integrated + +MapCircle { + + color: "#46a2da" + border.color: "#190a33" + border.width: 2 + smooth: true + opacity: 0.25 + + function setGeometry(markers, index){ + center.latitude = markers[index].coordinate.latitude + center.longitude = markers[index].coordinate.longitude + radius= center.distanceTo(markers[index + 1].coordinate) + } +} diff --git a/examples/location/mapviewer/map/ImageItem.qml b/examples/location/mapviewer/map/ImageItem.qml new file mode 100644 index 0000000..cc5507c --- /dev/null +++ b/examples/location/mapviewer/map/ImageItem.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.5; +import QtLocation 5.6 + +MapQuickItem { //to be used inside MapComponent only + id: imageItem + + MouseArea { + anchors.fill: parent + drag.target: parent + } + + function setGeometry(markers, index) { + coordinate.latitude = markers[index].coordinate.latitude + coordinate.longitude = markers[index].coordinate.longitude + } + + sourceItem: Image { + id: testImage + source: "../resources/icon.png" + opacity: 0.7 + } +} diff --git a/examples/location/mapviewer/map/MapComponent.qml b/examples/location/mapviewer/map/MapComponent.qml new file mode 100644 index 0000000..c7ac36d --- /dev/null +++ b/examples/location/mapviewer/map/MapComponent.qml @@ -0,0 +1,641 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtLocation 5.9 +import QtPositioning 5.5 +import "../helper.js" as Helper + +//! [top] +Map { + id: map +//! [top] + property variant markers + property variant mapItems + property int markerCounter: 0 // counter for total amount of markers. Resets to 0 when number of markers = 0 + property int currentMarker + property int lastX : -1 + property int lastY : -1 + property int pressX : -1 + property int pressY : -1 + property int jitterThreshold : 30 + property bool followme: false + property variant scaleLengths: [5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000] + property alias routeQuery: routeQuery + property alias routeModel: routeModel + property alias geocodeModel: geocodeModel + property alias slidersExpanded: sliders.expanded + + signal showGeocodeInfo() + signal geocodeFinished() + signal routeError() + signal coordinatesCaptured(double latitude, double longitude) + signal showMainMenu(variant coordinate) + signal showMarkerMenu(variant coordinate) + signal showRouteMenu(variant coordinate) + signal showPointMenu(variant coordinate) + signal showRouteList() + + function geocodeMessage() + { + var street, district, city, county, state, countryCode, country, postalCode, latitude, longitude, text + latitude = Math.round(geocodeModel.get(0).coordinate.latitude * 10000) / 10000 + longitude =Math.round(geocodeModel.get(0).coordinate.longitude * 10000) / 10000 + street = geocodeModel.get(0).address.street + district = geocodeModel.get(0).address.district + city = geocodeModel.get(0).address.city + county = geocodeModel.get(0).address.county + state = geocodeModel.get(0).address.state + countryCode = geocodeModel.get(0).address.countryCode + country = geocodeModel.get(0).address.country + postalCode = geocodeModel.get(0).address.postalCode + + text = "Latitude: " + latitude + "
" + text +="Longitude: " + longitude + "
" + "
" + if (street) text +="Street: "+ street + "
" + if (district) text +="District: "+ district +"
" + if (city) text +="City: "+ city + "
" + if (county) text +="County: "+ county + "
" + if (state) text +="State: "+ state + "
" + if (countryCode) text +="Country code: "+ countryCode + "
" + if (country) text +="Country: "+ country + "
" + if (postalCode) text +="PostalCode: "+ postalCode + "
" + return text + } + + function calculateScale() + { + var coord1, coord2, dist, text, f + f = 0 + coord1 = map.toCoordinate(Qt.point(0,scale.y)) + coord2 = map.toCoordinate(Qt.point(0+scaleImage.sourceSize.width,scale.y)) + dist = Math.round(coord1.distanceTo(coord2)) + + if (dist === 0) { + // not visible + } else { + for (var i = 0; i < scaleLengths.length-1; i++) { + if (dist < (scaleLengths[i] + scaleLengths[i+1]) / 2 ) { + f = scaleLengths[i] / dist + dist = scaleLengths[i] + break; + } + } + if (f === 0) { + f = dist / scaleLengths[i] + dist = scaleLengths[i] + } + } + + text = Helper.formatDistance(dist) + scaleImage.width = (scaleImage.sourceSize.width * f) - 2 * scaleImageLeft.sourceSize.width + scaleText.text = text + } + + function deleteMarkers() + { + var count = map.markers.length + for (var i = 0; i map.jitterThreshold || + Math.abs(map.pressY - parent.y -mouse.y ) > map.jitterThreshold) { + if (pressed) parent.radius = parent.center.distanceTo( + map.toCoordinate(Qt.point(mouse.x, mouse.y))) + } + if (mouse.button == Qt.LeftButton) { + map.lastX = mouse.x + parent.x + map.lastY = mouse.y + parent.y + } + } + + onPressAndHold:{ + if (Math.abs(map.pressX - parent.x- mouse.x ) < map.jitterThreshold + && Math.abs(map.pressY - parent.y - mouse.y ) < map.jitterThreshold) { + showPointMenu(lastCoordinate); + } + } + } + //! [pointdel1] + } + } + //! [pointdel1] + + //! [routeview0] + MapItemView { + model: routeModel + delegate: routeDelegate + //! [routeview0] + autoFitViewport: true + //! [routeview1] + } + //! [routeview1] + + //! [geocodeview] + MapItemView { + model: geocodeModel + delegate: pointDelegate + } + //! [geocodeview] + + Timer { + id: scaleTimer + interval: 100 + running: false + repeat: false + onTriggered: { + map.calculateScale() + } + } + + MouseArea { + id: mouseArea + property variant lastCoordinate + anchors.fill: parent + acceptedButtons: Qt.LeftButton | Qt.RightButton + + onPressed : { + map.lastX = mouse.x + map.lastY = mouse.y + map.pressX = mouse.x + map.pressY = mouse.y + lastCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y)) + } + + onPositionChanged: { + if (mouse.button == Qt.LeftButton) { + map.lastX = mouse.x + map.lastY = mouse.y + } + } + + onDoubleClicked: { + var mouseGeoPos = map.toCoordinate(Qt.point(mouse.x, mouse.y)); + var preZoomPoint = map.fromCoordinate(mouseGeoPos, false); + if (mouse.button === Qt.LeftButton) { + map.zoomLevel = Math.floor(map.zoomLevel + 1) + } else if (mouse.button === Qt.RightButton) { + map.zoomLevel = Math.floor(map.zoomLevel - 1) + } + var postZoomPoint = map.fromCoordinate(mouseGeoPos, false); + var dx = postZoomPoint.x - preZoomPoint.x; + var dy = postZoomPoint.y - preZoomPoint.y; + + var mapCenterPoint = Qt.point(map.width / 2.0 + dx, map.height / 2.0 + dy); + map.center = map.toCoordinate(mapCenterPoint); + + lastX = -1; + lastY = -1; + } + + onPressAndHold:{ + if (Math.abs(map.pressX - mouse.x ) < map.jitterThreshold + && Math.abs(map.pressY - mouse.y ) < map.jitterThreshold) { + showMainMenu(lastCoordinate); + } + } + } +//! [end] +} +//! [end] diff --git a/examples/location/mapviewer/map/MapSliders.qml b/examples/location/mapviewer/map/MapSliders.qml new file mode 100644 index 0000000..ed475d0 --- /dev/null +++ b/examples/location/mapviewer/map/MapSliders.qml @@ -0,0 +1,333 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Controls 2.0 as C2 + +Row { + id: containerRow + + property var mapSource + property real fontSize : 14 + property color labelBackground : "transparent" + property int edge: Qt.RightEdge + property alias expanded: sliderToggler.checked + + function rightEdge() { + return (containerRow.edge === Qt.RightEdge); + } + + layoutDirection: rightEdge() ? Qt.LeftToRight : Qt.RightToLeft + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: rightEdge() ? parent.right : undefined + anchors.left: rightEdge() ? undefined : parent.left + + Button { + id: sliderToggler + width: 32 + height: 96 + checkable: true + checked: true + anchors.verticalCenter: parent.verticalCenter + + transform: Scale { + origin.x: rightEdge() ? 0 : sliderToggler.width / 2 + xScale: rightEdge() ? 1 : -1 + } + + style: ButtonStyle { + background: Rectangle { + color: "transparent" + } + } + + property real shear: 0.333 + property real buttonOpacity: 0.5 + property real mirror : rightEdge() ? 1.0 : -1.0 + + Rectangle { + width: 16 + height: 48 + color: "seagreen" + antialiasing: true + opacity: sliderToggler.buttonOpacity + anchors.top: parent.top + anchors.left: sliderToggler.checked ? parent.left : parent.horizontalCenter + transform: Matrix4x4 { + property real d : sliderToggler.checked ? 1.0 : -1.0 + matrix: Qt.matrix4x4(1.0, d * sliderToggler.shear, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0) + } + } + + Rectangle { + width: 16 + height: 48 + color: "seagreen" + antialiasing: true + opacity: sliderToggler.buttonOpacity + anchors.top: parent.verticalCenter + anchors.right: sliderToggler.checked ? parent.right : parent.horizontalCenter + transform: Matrix4x4 { + property real d : sliderToggler.checked ? -1.0 : 1.0 + matrix: Qt.matrix4x4(1.0, d * sliderToggler.shear, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0) + } + } + } + + Rectangle { + id: sliderContainer + height: parent.height + width: sliderRow.width + 10 + visible: sliderToggler.checked + color: Qt.rgba( 0, 191 / 255.0, 255 / 255.0, 0.07) + + property var labelBorderColor: "transparent" + property var slidersHeight : sliderContainer.height + - rowSliderValues.height + - rowSliderLabels.height + - sliderColumn.spacing * 2 + - sliderColumn.topPadding + - sliderColumn.bottomPadding + + Column { + id: sliderColumn + spacing: 10 + topPadding: 16 + bottomPadding: 48 + anchors.centerIn: parent + + // the sliders value labels + Row { + id: rowSliderValues + spacing: sliderRow.spacing + width: sliderRow.width + height: 32 + property real entryWidth: zoomSlider.width + + Rectangle{ + color: labelBackground + height: parent.height + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelZoomValue + text: zoomSlider.value.toFixed(3) + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + Rectangle{ + color: labelBackground + height: parent.height + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelBearingValue + text: bearingSlider.value.toFixed(2) + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + Rectangle{ + color: labelBackground + height: parent.height + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelTiltValue + text: tiltSlider.value.toFixed(2) + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + Rectangle{ + color: labelBackground + height: parent.height + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelFovValue + text: fovSlider.value.toFixed(2) + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + } // rowSliderValues + + // The sliders row + Row { + spacing: -10 + id: sliderRow + height: sliderContainer.slidersHeight + + C2.Slider { + id: zoomSlider + height: parent.height + orientation : Qt.Vertical + from : containerRow.mapSource.minimumZoomLevel + to : containerRow.mapSource.maximumZoomLevel + value : containerRow.mapSource.zoomLevel + onValueChanged: { + containerRow.mapSource.zoomLevel = value + } + } + C2.Slider { + id: bearingSlider + height: parent.height + from: 0 + to: 360 + orientation : Qt.Vertical + value: containerRow.mapSource.bearing + onValueChanged: { + containerRow.mapSource.bearing = value; + } + } + C2.Slider { + id: tiltSlider + height: parent.height + orientation : Qt.Vertical + from: containerRow.mapSource.minimumTilt; + to: containerRow.mapSource.maximumTilt + value: containerRow.mapSource.tilt + onValueChanged: { + containerRow.mapSource.tilt = value; + } + } + C2.Slider { + id: fovSlider + height: parent.height + orientation : Qt.Vertical + from: containerRow.mapSource.minimumFieldOfView + to: containerRow.mapSource.maximumFieldOfView + value: containerRow.mapSource.fieldOfView + onValueChanged: { + containerRow.mapSource.fieldOfView = value; + } + } + } // Row sliders + + // The labels row + Row { + id: rowSliderLabels + spacing: sliderRow.spacing + width: sliderRow.width + property real entryWidth: zoomSlider.width + property real entryHeight: 64 + + Rectangle{ + color: labelBackground + height: parent.entryHeight + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelZoom + text: "Zoom" + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + + Rectangle{ + color: labelBackground + height: parent.entryHeight + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelBearing + text: "Bearing" + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + Rectangle{ + color: labelBackground + height: parent.entryHeight + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelTilt + text: "Tilt" + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + Rectangle{ + color: labelBackground + height: parent.entryHeight + width: parent.entryWidth + border.color: sliderContainer.labelBorderColor + Label { + id: labelFov + text: "FoV" + font.pixelSize: fontSize + rotation: -90 + anchors.centerIn: parent + } + } + } // rowSliderLabels + } // Column + } // sliderContainer +} // containerRow diff --git a/examples/location/mapviewer/map/Marker.qml b/examples/location/mapviewer/map/Marker.qml new file mode 100644 index 0000000..095e5f9 --- /dev/null +++ b/examples/location/mapviewer/map/Marker.qml @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5; +import QtLocation 5.6 + +//! [mqi-top] +MapQuickItem { + id: marker +//! [mqi-top] + property alias lastMouseX: markerMouseArea.lastX + property alias lastMouseY: markerMouseArea.lastY + +//! [mqi-anchor] + anchorPoint.x: image.width/4 + anchorPoint.y: image.height + + sourceItem: Image { + id: image +//! [mqi-anchor] + source: "../resources/marker.png" + opacity: markerMouseArea.pressed ? 0.6 : 1.0 + MouseArea { + id: markerMouseArea + property int pressX : -1 + property int pressY : -1 + property int jitterThreshold : 10 + property int lastX: -1 + property int lastY: -1 + anchors.fill: parent + hoverEnabled : false + drag.target: marker + preventStealing: true + + onPressed : { + map.pressX = mouse.x + map.pressY = mouse.y + map.currentMarker = -1 + for (var i = 0; i< map.markers.length; i++){ + if (marker == map.markers[i]){ + map.currentMarker = i + break + } + } + } + + onPressAndHold:{ + if (Math.abs(map.pressX - mouse.x ) < map.jitterThreshold + && Math.abs(map.pressY - mouse.y ) < map.jitterThreshold) { + var p = map.fromCoordinate(marker.coordinate) + lastX = p.x + lastY = p.y + map.showMarkerMenu(marker.coordinate) + } + } + } + + Text{ + id: number + y: image.height/10 + width: image.width + color: "white" + font.bold: true + font.pixelSize: 14 + horizontalAlignment: Text.AlignHCenter + Component.onCompleted: { + text = map.markerCounter + } + } + +//! [mqi-closeimage] + } +//! [mqi-closeimage] + + Component.onCompleted: coordinate = map.toCoordinate(Qt.point(markerMouseArea.mouseX, + markerMouseArea.mouseY)); +//! [mqi-close] +} +//! [mqi-close] diff --git a/examples/location/mapviewer/map/MiniMap.qml b/examples/location/mapviewer/map/MiniMap.qml new file mode 100644 index 0000000..78d2b01 --- /dev/null +++ b/examples/location/mapviewer/map/MiniMap.qml @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtPositioning 5.3 +import QtLocation 5.6 + +Rectangle{ + + function clamp(num, min, max) + { + return num < min ? min : num > max ? max : num; + } + + function minimumScaleFactor() + { + var hscalefactor = (400.0 / Math.max(Math.min(map.width, 1000), 400)) * 0.5 + var vscalefactor = (400.0 / Math.max(Math.min(map.height, 1000), 400)) * 0.5 + return Math.min(hscalefactor,vscalefactor) + } + + function avgScaleFactor() + { + var hscalefactor = (400.0 / Math.max(Math.min(map.width, 1000), 400)) * 0.5 + var vscalefactor = (400.0 / Math.max(Math.min(map.height, 1000), 400)) * 0.5 + return (hscalefactor+vscalefactor) * 0.5 + } + + id: miniMapRect + width: Math.floor(map.width * avgScaleFactor()) + 2 + height: Math.floor(map.height * avgScaleFactor()) + 2 + anchors.right: (parent) ? parent.right : undefined + anchors.rightMargin: 10 + anchors.top: (parent) ? parent.top : undefined + anchors.topMargin: 10 + color: "#242424" + Map { + id: miniMap + anchors.top: parent.top + anchors.topMargin: 1 + anchors.left: parent.left + anchors.leftMargin: 1 + width: Math.floor(map.width * avgScaleFactor()) + height: Math.floor(map.height * avgScaleFactor()) + zoomLevel: clamp(map.zoomLevel - 4.5, 2.0, 5.0) //(map.zoomLevel > minimumZoomLevel + 3) ? minimumZoomLevel + 3 : 1.5 + center: map.center + plugin: map.plugin + gesture.enabled: false + copyrightsVisible: false + property double mapZoomLevel : map.zoomLevel + + // cannot use property bindings on map.visibleRegion in MapRectangle because it's non-NOTIFYable + onCenterChanged: miniMapRectangle.updateCoordinates() + onMapZoomLevelChanged: miniMapRectangle.updateCoordinates() + onWidthChanged: miniMapRectangle.updateCoordinates() + onHeightChanged: miniMapRectangle.updateCoordinates() + + MapRectangle { + id: miniMapRectangle + color: "#44ff0000" + border.width: 1 + border.color: "red" + + function getMapVisibleRegion() + { + return QtPositioning.shapeToRectangle(map.visibleRegion) + } + + function updateCoordinates() + { + topLeft.latitude = getMapVisibleRegion().topLeft.latitude + topLeft.longitude= getMapVisibleRegion().topLeft.longitude + bottomRight.latitude = getMapVisibleRegion().bottomRight.latitude + bottomRight.longitude= getMapVisibleRegion().bottomRight.longitude + console.log("TopLeft: " + topLeft) + console.log("BotRigh: " + bottomRight) + } + } + } +} diff --git a/examples/location/mapviewer/map/PolygonItem.qml b/examples/location/mapviewer/map/PolygonItem.qml new file mode 100644 index 0000000..b2ece6d --- /dev/null +++ b/examples/location/mapviewer/map/PolygonItem.qml @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +import QtQuick 2.5 +import QtLocation 5.6 + +//TODO: remove me when items are integrated + +MapPolygon { + + color: "#46a2da" + border.color: "#190a33" + border.width: 2 + smooth: true + opacity: 0.25 + + function setGeometry(markers, index){ + for (var i = index; i0) + plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin{ name:"' + provider + '"; parameters: appWindow.parameters}', appWindow) + else + plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin{ name:"' + provider + '"}', appWindow) + + if (minimap) { + minimap.destroy() + minimap = null + } + + var zoomLevel = null + var tilt = null + var bearing = null + var fov = null + var center = null + var panelExpanded = null + if (map) { + zoomLevel = map.zoomLevel + tilt = map.tilt + bearing = map.bearing + fov = map.fieldOfView + center = map.center + panelExpanded = map.slidersExpanded + map.destroy() + } + + map = mapComponent.createObject(page); + map.plugin = plugin; + + if (zoomLevel != null) { + map.tilt = tilt + map.bearing = bearing + map.fieldOfView = fov + map.zoomLevel = zoomLevel + map.center = center + map.slidersExpanded = panelExpanded + } else { + // Use an integer ZL to enable nearest interpolation, if possible. + map.zoomLevel = Math.floor((map.maximumZoomLevel - map.minimumZoomLevel)/2) + // defaulting to 45 degrees, if possible. + map.fieldOfView = Math.min(Math.max(45.0, map.minimumFieldOfView), map.maximumFieldOfView) + } + + map.forceActiveFocus() + } + + function getPlugins() + { + var plugin = Qt.createQmlObject ('import QtLocation 5.6; Plugin {}', appWindow) + var myArray = new Array() + for (var i = 0; i" + qsTr("Distance:") + "
" + distance) + break + case "drawImage": + map.addGeoItem("ImageItem") + break + case "drawRectangle": + map.addGeoItem("RectangleItem") + break + case "drawCircle": + map.addGeoItem("CircleItem") + break; + case "drawPolyline": + map.addGeoItem("PolylineItem") + break; + case "drawPolygonMenu": + map.addGeoItem("PolygonItem") + break + default: + console.log("Unsupported operation") + } + } + } + + ItemPopupMenu { + id: itemPopupMenu + + function show(type,coordinate) + { + stackView.pop(page) + itemPopupMenu.type = type + itemPopupMenu.update() + itemPopupMenu.popup() + } + + onItemClicked: { + stackView.pop(page) + switch (item) { + case "showRouteInfo": + stackView.showRouteListPage() + break; + case "deleteRoute": + map.routeModel.reset(); + break; + case "showPointInfo": + map.showGeocodeInfo() + break; + case "deletePoint": + map.geocodeModel.reset() + break; + default: + console.log("Unsupported operation") + } + } + } + + StackView { + id: stackView + anchors.fill: parent + focus: true + initialItem: Item { + id: page + } + + function showMessage(title,message,backPage) + { + push({ item: Qt.resolvedUrl("forms/Message.qml") , + properties: { + "title" : title, + "message" : message, + "backPage" : backPage + }}) + currentItem.closeForm.connect(closeMessage) + } + + function closeMessage(backPage) + { + pop(backPage) + } + + function closeForm() + { + pop(page) + } + + function showRouteListPage() + { + push({ item: Qt.resolvedUrl("forms/RouteList.qml") , + properties: { + "routeModel" : map.routeModel + }}) + currentItem.closeForm.connect(closeForm) + } + } + + Component { + id: mapComponent + + MapComponent{ + width: page.width + height: page.height + onFollowmeChanged: mainMenu.isFollowMe = map.followme + onSupportedMapTypesChanged: mainMenu.mapTypeMenu.createMenu(map) + onCoordinatesCaptured: { + var text = "" + qsTr("Latitude:") + " " + Helper.roundNumber(latitude,4) + "
" + qsTr("Longitude:") + " " + Helper.roundNumber(longitude,4) + stackView.showMessage(qsTr("Coordinates"),text); + } + onGeocodeFinished:{ + if (map.geocodeModel.status == GeocodeModel.Ready) { + if (map.geocodeModel.count == 0) { + stackView.showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode")) + } else if (map.geocodeModel.count > 1) { + stackView.showMessage(qsTr("Ambiguous geocode"), map.geocodeModel.count + " " + + qsTr("results found for the given address, please specify location")) + } else { + stackView.showMessage(qsTr("Location"), geocodeMessage(),page) + } + } else if (map.geocodeModel.status == GeocodeModel.Error) { + stackView.showMessage(qsTr("Geocode Error"),qsTr("Unsuccessful geocode")) + } + } + onRouteError: stackView.showMessage(qsTr("Route Error"),qsTr("Unable to find a route for the given points"),page) + + onShowGeocodeInfo: stackView.showMessage(qsTr("Location"),geocodeMessage(),page) + + onErrorChanged: { + if (map.error != Map.NoError) { + var title = qsTr("ProviderError") + var message = map.errorString + "

" + qsTr("Try to select other provider") + "" + if (map.error == Map.MissingRequiredParameterError) + message += "
" + qsTr("or see") + " \'mapviewer --help\' " + + qsTr("how to pass plugin parameters.") + stackView.showMessage(title,message); + } + } + onShowMainMenu: mapPopupMenu.show(coordinate) + onShowMarkerMenu: markerPopupMenu.show(coordinate) + onShowRouteMenu: itemPopupMenu.show("Route",coordinate) + onShowPointMenu: itemPopupMenu.show("Point",coordinate) + onShowRouteList: stackView.showRouteListPage() + } + } +} diff --git a/examples/location/mapviewer/mapviewer.qrc b/examples/location/mapviewer/mapviewer.qrc new file mode 100644 index 0000000..7a7d1f4 --- /dev/null +++ b/examples/location/mapviewer/mapviewer.qrc @@ -0,0 +1,38 @@ + + + mapviewer.qml + map/MapComponent.qml + map/MapSliders.qml + map/Marker.qml + map/PolylineItem.qml + map/RectangleItem.qml + map/CircleItem.qml + map/PolygonItem.qml + map/ImageItem.qml + map/MiniMap.qml + forms/Message.qml + forms/MessageForm.ui.qml + forms/Geocode.qml + forms/GeocodeForm.ui.qml + forms/ReverseGeocode.qml + forms/ReverseGeocodeForm.ui.qml + forms/RouteCoordinate.qml + forms/RouteCoordinateForm.ui.qml + forms/RouteAddress.qml + forms/RouteAddressForm.ui.qml + forms/Locale.qml + forms/LocaleForm.ui.qml + forms/RouteList.qml + forms/RouteListDelegate.qml + forms/RouteListHeader.qml + menus/MainMenu.qml + menus/MapPopupMenu.qml + menus/MarkerPopupMenu.qml + menus/ItemPopupMenu.qml + helper.js + resources/scale_end.png + resources/scale.png + resources/marker.png + resources/icon.png + + diff --git a/examples/location/mapviewer/menus/ItemPopupMenu.qml b/examples/location/mapviewer/menus/ItemPopupMenu.qml new file mode 100644 index 0000000..998a75b --- /dev/null +++ b/examples/location/mapviewer/menus/ItemPopupMenu.qml @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Menu { + property variant type + signal itemClicked(string item) + + function update() { + clear() + addItem(qsTr("Info")).triggered.connect(function(){itemClicked("show" + type + "Info")}) + addItem(qsTr("Delete")).triggered.connect(function(){itemClicked("delete" + type )}) + } +} diff --git a/examples/location/mapviewer/menus/MainMenu.qml b/examples/location/mapviewer/menus/MainMenu.qml new file mode 100644 index 0000000..baf0357 --- /dev/null +++ b/examples/location/mapviewer/menus/MainMenu.qml @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtLocation 5.6 + +MenuBar { + property variant providerMenu: providerMenu + property variant mapTypeMenu: mapTypeMenu + property variant toolsMenu: toolsMenu + property alias isFollowMe: toolsMenu.isFollowMe + property alias isMiniMap: toolsMenu.isMiniMap + + signal selectProvider(string providerName) + signal selectMapType(variant mapType) + signal selectTool(string tool); + signal toggleMapState(string state) + + Menu { + id: providerMenu + title: qsTr("Provider") + + function createMenu(plugins) + { + clear() + for (var i = 0; i < plugins.length; i++) { + createProviderMenuItem(plugins[i]); + } + } + + function createProviderMenuItem(provider) + { + var item = addItem(provider); + item.checkable = true; + item.triggered.connect(function(){selectProvider(provider)}) + } + } + + Menu { + id: mapTypeMenu + title: qsTr("MapType") + + function createMenu(map) + { + clear() + for (var i = 0; i 0) { + addItem(qsTr("Delete all markers")).triggered.connect(function(){itemClicked("deleteMarkers")}) + } + + if (mapItemsCount > 0) { + addItem(qsTr("Delete all items")).triggered.connect(function(){itemClicked("deleteItems")}) + } + } +} diff --git a/examples/location/mapviewer/menus/MarkerPopupMenu.qml b/examples/location/mapviewer/menus/MarkerPopupMenu.qml new file mode 100644 index 0000000..81cf411 --- /dev/null +++ b/examples/location/mapviewer/menus/MarkerPopupMenu.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +Menu { + property int currentMarker + property int markersCount + signal itemClicked(string item) + + function update() { + clear() + addItem(qsTr("Delete")).triggered.connect(function(){itemClicked("deleteMarker")}) + addItem(qsTr("Coordinates")).triggered.connect(function(){itemClicked("getMarkerCoordinate")}) + addItem(qsTr("Move to")).triggered.connect(function(){itemClicked("moveMarkerTo")}) + if (currentMarker == markersCount-2){ + addItem(qsTr("Route to next point")).triggered.connect(function(){itemClicked("routeToNextPoint")}); + addItem(qsTr("Distance to next point")).triggered.connect(function(){itemClicked("distanceToNextPoint")}); + } + if (currentMarker < markersCount-2){ + addItem(qsTr("Route to next points")).triggered.connect(function(){itemClicked("routeToNextPoints")}); + addItem(qsTr("Distance to next point")).triggered.connect(function(){itemClicked("distanceToNextPoint")}); + } + + var menu = addMenu(qsTr("Draw...")) + menu.addItem(qsTr("Image")).triggered.connect(function(){itemClicked("drawImage")}) + + if (currentMarker <= markersCount-2){ + menu.addItem(qsTr("Rectangle")).triggered.connect(function(){itemClicked("drawRectangle")}) + menu.addItem(qsTr("Circle")).triggered.connect(function(){itemClicked("drawCircle")}) + menu.addItem(qsTr("Polyline")).triggered.connect(function(){itemClicked("drawPolyline")}) + } + + if (currentMarker < markersCount-2){ + menu.addItem(qsTr("Polygon")).triggered.connect(function(){itemClicked("drawPolygonMenu")}) + } + } +} diff --git a/examples/location/mapviewer/resources/icon.png b/examples/location/mapviewer/resources/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..973a50091ada099b37b07d1456e54acb6e773650 GIT binary patch literal 1859 zcmah~cU05K8vRM=QkS;4qVA*lRNAuV>2py@qzVQE6_BFn%32UnkfkLAf)we9^aTZ} z(t|<>NE$5$f`k$XC6o{X0Rn^?ATQ`2yZ^m&?tJro_uiRr=A1caV(p=pveHMS0RWJ- zwlZ@N&h`gG5+sahiby^Hh*0fqubT@?Kmgn*zu`qTMhx6|^2z#H`+pJStNv(<0OD_m zR?|R&38LLH7=HmlG!Ma9Ac$rmZIDo$MJUc3fxiqRfWJe?BM1k8V=p5Jm*IG@P=ym9 z2cB=qfejD&ZieH8Bm{sc*f#VJkh7&U1A^{xsJ;6hSo&&?dR>CD&Uh%k!?0F+iauSuSU1q#dO%m zbX<$=aCp^uJ-+j1LYGrQmvds5OA_f;63I2?i+d{h&TI0WH1gfF?t2+Me`NMN$f7*V zqCCo`c<1yA@g%p`C-3XC{J!V;eSUBI{0pdm7WIb|55P(W;3b1$rGtpF!SM2-hz~=N z6||R?w5Up2bk%T7^>A$U@T;1UxZ08UI(j0Co`j+&*V9w#M^iqIrZ$YdL62vm$FrKo zvoI4mn2FrxiN9JHd7l~it&{IsCkwEXg}ABrxTzxiR1smigfLx7oGxvfE^nLp(8;Xo zWL9@EYe=)TUsxzItDemINSQ-Z*yvt%Q!g9SH~+bR0XwjO8(hE-afmcd8*QniMO-7zQf(% zuWs_!Hu=1*-SzF=&7Hk1{@yl!Z)bO(zqh};x4*Y9*cS){Zpj~vg|FJh-vR0f90G}n zOGrr{mQ_&vLH&rvvEx5$pE58$Yhr3*LWM-JA7fUE|_;E0D#)9&A`{435zu5ZDsI} z65Nj%v9_C>B{>-mbXH3lr*A96Q&u`$Q^QpT;qVq>iYg#bEymRDXF+9TFk{&rmhicGFLk?G`;aZ3NbsO62aSy)P0RKR0u=`D05Ji1d z^%hv6b1=LNbW559tDI<75)a+AM~3V@&%})}v;{4D3x2+jHJ9AAe?d~TmK*$8)4pp` zKITTq!;>P_);MqW4@S>()Z;~cPix8hx<=cRyO6XUQBR(NJ3BVjWCdGn}368Ut-izwpS~`X#V*M zXUm6RnveSCAnr4D!}{@@WAi09JZLAZb`<9&!OSCNnkswO0Ge-~&Ys?~dzED?xK*py z;Z3Q^JPN~C7mp{zITw2duM8JB1y2l>!B&jq){D+C;AY#J>n9+N|6zAGc+Q*F1w(7u zLA^+R(M0ov&bi2Axw_#lA;tU1RDH6 z&HJPwjL3~8QY$@CZ3aG!{qv+PmA(OVq^aXZDZE%d+du6>&85@#HI8;#GR5Q;Mw-BH`XaJL9yJC_V-*t% gdhjLiuUs-bd_{o9{h&Z=1{XL$eoD)0qRHcyZ`_I literal 0 HcmV?d00001 diff --git a/examples/location/mapviewer/resources/marker.png b/examples/location/mapviewer/resources/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..2116dfdf51bfb8ac9556af035d598cf2c68c44e3 GIT binary patch literal 752 zcmVP001Be1^@s6=bY090008FNklP9F+g_dGL z7b4wOL~YbX6}NhC`Y^Yem;_BVH^=jjGv%gj-gDq^?<8~n@6OCQGh-NK7!h@__p?7@ zAHg%1%k{H&7`2$=I6)SB$ey9%mf^nW7pw@t0liA$uW3=@<{h$6c2acLUNScZ#1n1& zj{lW0thX!xPr(xr5KoZZd4{Yl+b9~?SDiKsYw4jl=ahsz49ascH$lOp{)gvA{a+7Rbz6(!?wYhMaEN_*&FvlTY+Qe#GeLd5Y`1f1;(W)TxhmI&f zGMmlT7dqEPnL5c-$rEwKC^iKt`y;L(O{LEFOX&6M`3tgeo|g$ok3Ur@d#&7y#A^h6 z?>^6`UFq7fPoGtQPnFzO*2?UlncYoA0W+aJ?(31K(tFo2L@iE#B&)%{`ZRITEx!AZ zU)hx!$Bv5w?bcmR&+YjDzHG#=UYFZb?pf8h$W$s-$07?*nr~~*-vC?M)D-<;udDUC zx(`a(n9X1Re>5i2b#_ic_8U43M<~(&X=?Gdk$OC$)?v`lX*}=K5dL}Zz8b-2L$~o) id-3sdrSb5U8~YFSpzHYmbwF4E0000PQ<|Z&(Z(QQpe}ECj@=zX;NfjIC%2Ts>e~;XMma*JYD@<);T3K0RYVMA4vcJ literal 0 HcmV?d00001 diff --git a/examples/location/mapviewer/resources/scale_end.png b/examples/location/mapviewer/resources/scale_end.png new file mode 100644 index 0000000000000000000000000000000000000000..94510b1258e33726e65d4d5f539b8dc1dea5c905 GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-W0V2~}Z=?b#6;Bt(5RU7~DID*U6A}^<1tu^B pAL2^zV(}^D?rhdzjZ-+uz+kqK@vYpot*Suv44$rjF6*2UngECF7V7{2 literal 0 HcmV?d00001 diff --git a/examples/location/minimal_map/doc/images/minimal_map.png b/examples/location/minimal_map/doc/images/minimal_map.png new file mode 100644 index 0000000000000000000000000000000000000000..73a16fd58664ce364bfaa6bbe8f8d5249075fed2 GIT binary patch literal 229975 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%0AAh$<02B?s@~i(32qE}8fA4uHI>pz&a)jUb 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ɉa7E-zho9Ypn+7!%X5HD$|VCi$i$ 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 W3&Vqoq~Xo=0X%K!$~tAe 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 +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} + diff --git a/examples/location/minimal_map/main.qml b/examples/location/minimal_map/main.qml new file mode 100644 index 0000000..40e1bb4 --- /dev/null +++ b/examples/location/minimal_map/main.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.Window 2.0 +import QtLocation 5.6 +import QtPositioning 5.6 + +Window { + width: 512 + height: 512 + visible: true + + Plugin { + id: mapPlugin + name: "osm" // "mapboxgl", "esri", ... + // specify plugin parameters if necessary + // PluginParameter { + // name: + // value: + // } + } + + Map { + anchors.fill: parent + plugin: mapPlugin + center: QtPositioning.coordinate(59.91, 10.75) // Oslo + zoomLevel: 14 + } +} diff --git a/examples/location/minimal_map/minimal_map.pro b/examples/location/minimal_map/minimal_map.pro new file mode 100644 index 0000000..5c16525 --- /dev/null +++ b/examples/location/minimal_map/minimal_map.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += location + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/location/minimal_map +INSTALLS += target diff --git a/examples/location/minimal_map/qml.qrc b/examples/location/minimal_map/qml.qrc new file mode 100644 index 0000000..0ff3892 --- /dev/null +++ b/examples/location/minimal_map/qml.qrc @@ -0,0 +1,6 @@ + + + main.qml + + + diff --git a/examples/location/places/doc/images/places.png b/examples/location/places/doc/images/places.png new file mode 100644 index 0000000000000000000000000000000000000000..c8ada27d9b6a612405a6563e54ad54f9d299f66c GIT binary patch literal 233430 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

{6XTt|rus=F80I_+zR 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%0D5oX*4utmgKy@`V1zRMD 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 0) + return place.location.address.text; + + return place.location.address.street; + } + + function categoryNames(categories) { + var result = ""; + + for (var i = 0; i < categories.length; ++i) { + if (result == "") { + result = categories[i].name; + } else { + result = result + ", " + categories[i].name; + } + } + + return result; + } + + function additonalInformation(place) { + var keys = place.extendedAttributes.keys(); + var result; + + for (var i = 0; i < keys.length; ++i) { + var label = place.extendedAttributes[keys[i]].label; + var text = place.extendedAttributes[keys[i]].text; + if (label) { + result += label + ": " + if (text) + result += text + result += "
" + } + } + + if (!result) + result = qsTr("No information") + + return result; + } + + editorialsButton.onClicked: showEditorials(place) + imagesButton.onClicked: showImages(place) + reviewsButton.onClicked: showReviews(place) + findSimilarButton.onClicked: searchForSimilar(place) + + Component.onCompleted: { + placeName.text = place ? (place.favorite ? place.favorite.name : place.name) : "" + placeIcon.source = place ? (place.favorite ? place.favorite.icon.url(Qt.size(40,40)) + : place.icon.url() == "" ? + "../resources/marker.png" + : place.icon.url(Qt.size(40,40))) : "" + ratingView.rating = (place && place.ratings) ? place.ratings.average : 0 + distance.text = Helper.formatDistance(distanceToPlace) + address.text = placeAddress(place) + categories.text = place ? categoryNames(place.categories) : "" + phone.text = place ? place.primaryPhone : "" + fax.text = place ? place.primaryFax : "" + email.text = place ? place.primaryEmail : "" + website.text = place ? '
' + place.primaryWebsite + '' : "" + addInformation.text = place ? additonalInformation(place) : "" + if (place) { + editorialsButton.enabled = Qt.binding(function(){ return place && place.editorialModel.totalCount > 0 }) + reviewsButton.enabled = Qt.binding(function(){ return place && place.reviewModel.totalCount > 0 }) + imagesButton.enabled = Qt.binding(function(){ return place && place.imageModel.totalCount > 0 }) + findSimilarButton.enabled = true + } + } +} + diff --git a/examples/location/places/forms/PlaceDetailsForm.ui.qml b/examples/location/places/forms/PlaceDetailsForm.ui.qml new file mode 100644 index 0000000..ad87ea7 --- /dev/null +++ b/examples/location/places/forms/PlaceDetailsForm.ui.qml @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 +import "../views" + +Item { + id: root + property alias placeName: placeName + property alias placeIcon: placeIcon + property alias distance: distance + property alias address: address + property alias categories: categories + property alias phone: phone + property alias fax: fax + property alias email: email + property alias website: website + property alias addInformation: addInformation + property alias editorialsButton: editorialsButton + property alias reviewsButton: reviewsButton + property alias imagesButton: imagesButton + property alias findSimilarButton: findSimilarButton + property alias ratingView: ratingView + width: parent.width + height: parent.height + + ScrollView { + id:scrollView + flickableItem.interactive: true + anchors.fill: parent + anchors.margins: 15 + + GridLayout { + width: scrollView.width - 15 + rows: 7 + columns: 2 + + RowLayout { + Layout.columnSpan: 2 + Layout.fillWidth: true + + Image { + id: placeIcon + source: "../resources/marker.png" + anchors.margins: 30 + } + + Label { + id: placeName + text: qsTr("PlaceName") + font.bold: true + } + + Item { + Layout.fillWidth: true + } + } + + RatingView { + id: ratingView + size: placeName.height * 2 + Layout.columnSpan: 2 + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + Layout.columnSpan: 2 + height: 1 + color: "#46a2da" + visible: addressBox.visible + } + + GroupBox { + id: addressBox + Layout.fillWidth: true + Layout.columnSpan: 2 + flat: true + title: qsTr("Address") + + GridLayout { + id: gridLayout3 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + text: qsTr("Distance:") + } + + Label { + id: distance + Layout.fillWidth: true + text: qsTr("1000 km") + } + + Label { + id: address + Layout.columnSpan: 2 + text: qsTr("Street Number
xxxxx City
Country") + } + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + Layout.columnSpan: 2 + height: 1 + color: "#46a2da" + visible: categoriesBox.visible + } + + GroupBox { + id: categoriesBox + Layout.fillWidth: true + Layout.columnSpan: 2 + flat: true + title: qsTr("Categories") + + Label { + id: categories + anchors.fill: parent + text: qsTr("category1, category2 ,category3") + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: "#46a2da" + visible: contactDetailsBox.visible + } + + GroupBox { + id: contactDetailsBox + Layout.fillWidth: true + Layout.columnSpan: 2 + flat: true + title: qsTr("Contact details") + GridLayout { + id: gridLayout4 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + text: qsTr("Phone: ") + } + + Label { + id: phone + Layout.fillWidth: true + text: qsTr("000-000-000") + } + + Label { + text: qsTr("Fax: ") + } + + Label { + id: fax + Layout.fillWidth: true + text: qsTr("000-000-000") + } + + Label { + text: qsTr("Email: ") + } + + Label { + id: email + Layout.fillWidth: true + text: qsTr("name@company.com") + } + + Label { + text: qsTr("Website: ") + } + + Label { + id: website + Layout.fillWidth: true + text: qsTr("http:://company.com") + } + } + } + + Rectangle { + Layout.columnSpan: 2 + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: "#46a2da" + visible: informationBox.visible + } + + GroupBox { + id: informationBox + Layout.fillWidth: true + Layout.columnSpan: 2 + flat: true + title: qsTr("Additional information") + ColumnLayout { + Label { + id: addInformation + text: qsTr("AdditionalInformation1
AdditionalInformation2
AdditionalInformation3") + } + } + } + + RowLayout { + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignHCenter + + Button { + id: editorialsButton + text: qsTr("Editorials") + enabled: false + } + + Button { + id: reviewsButton + text: qsTr("Reviews") + enabled: false + } + + Button { + id: imagesButton + text: qsTr("Images") + enabled: false + } + + Button { + id: findSimilarButton + text: qsTr("Find similar") + enabled: false + } + } + } + } +} + diff --git a/examples/location/places/forms/SearchBoundingBox.qml b/examples/location/places/forms/SearchBoundingBox.qml new file mode 100644 index 0000000..81f5fd2 --- /dev/null +++ b/examples/location/places/forms/SearchBoundingBox.qml @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtPositioning 5.5 + +SearchBoundingBoxForm { + property variant searchRegion + signal changeSearchBoundingBox(variant coordinate, real widthDeg, real heightDeg) + signal closeForm() + + goButton.onClicked: { + var coordinate = QtPositioning.coordinate(parseFloat(latitude.text), + parseFloat(longitude.text)); + if (coordinate.isValid) + changeSearchBoundingBox(coordinate,parseFloat(widthDeg.text),parseFloat(heightDeg.text)) + } + + clearButton.onClicked: { + latitude.text = "" + longitude.text = "" + widthDeg.text = "" + heightDeg.text = "" + } + + cancelButton.onClicked: closeForm() + + Component.onCompleted: { + latitude.text = "" + searchRegion.center.latitude + longitude.text = "" + searchRegion.center.longitude + widthDeg.text = searchRegion.width ? "" + searchRegion.width : "0.0" + heightDeg.text = searchRegion.height ? "" + searchRegion.height: "0.0" + } +} diff --git a/examples/location/places/forms/SearchBoundingBoxForm.ui.qml b/examples/location/places/forms/SearchBoundingBoxForm.ui.qml new file mode 100644 index 0000000..75180f6 --- /dev/null +++ b/examples/location/places/forms/SearchBoundingBoxForm.ui.qml @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + property alias clearButton: clearButton + property alias goButton: goButton + property alias longitude: longitude + property alias latitude: latitude + property alias widthDeg: widthDeg + property alias heightDeg: heightDeg + property alias cancelButton: cancelButton + property alias tabTitle: tabTitle + Rectangle { + id: tabRectangle + y: 20 + height: tabTitle.height * 2 + color: "#46a2da" + anchors.rightMargin: 0 + anchors.leftMargin: 0 + anchors.left: parent.left + anchors.right: parent.right + + Label { + id: tabTitle + color: "#ffffff" + text: qsTr("Search Bounding Box") + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Item { + id: item2 + anchors.rightMargin: 20 + anchors.leftMargin: 20 + anchors.bottomMargin: 20 + anchors.topMargin: 20 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabRectangle.bottom + + GridLayout { + id: gridLayout3 + anchors.rightMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.topMargin: 0 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + id: label2 + text: qsTr("Latitude") + } + + TextField { + id: latitude + Layout.fillWidth: true + } + + Label { + id: label3 + text: qsTr("Longitude") + } + + TextField { + id: longitude + Layout.fillWidth: true + placeholderText: qsTr("") + } + + Label { + id: label4 + text: qsTr("Width (deg)") + } + + TextField { + id: widthDeg + Layout.fillWidth: true + } + + Label { + id: label5 + text: qsTr("Height (deg)") + } + + TextField { + id: heightDeg + Layout.fillWidth: true + placeholderText: qsTr("") + } + + RowLayout { + id: rowLayout1 + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignRight + + Button { + id: goButton + text: qsTr("Set") + } + + Button { + id: clearButton + text: qsTr("Clear") + } + + Button { + id: cancelButton + text: qsTr("Cancel") + } + } + Item { + Layout.fillHeight: true + Layout.columnSpan: 2 + } + } + } +} diff --git a/examples/location/places/forms/SearchBoundingCircle.qml b/examples/location/places/forms/SearchBoundingCircle.qml new file mode 100644 index 0000000..e8337b4 --- /dev/null +++ b/examples/location/places/forms/SearchBoundingCircle.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtPositioning 5.5 + +SearchBoundingCircleForm { + property variant searchRegion + signal changeSearchBoundingCircle(variant coordinate, real radius) + signal closeForm() + + goButton.onClicked: { + var coordinate = QtPositioning.coordinate(parseFloat(latitude.text), + parseFloat(longitude.text)); + if (coordinate.isValid) + changeSearchBoundingCircle(coordinate,parseFloat(radius.text)) + } + + clearButton.onClicked: { + latitude.text = "" + longitude.text = "" + radius.text = "" + } + + cancelButton.onClicked: closeForm() + + Component.onCompleted: { + latitude.text = "" + searchRegion.center.latitude + longitude.text = "" + searchRegion.center.longitude + radius.text = searchRegion.radius ? "" + searchRegion.radius : "0.0" + } +} diff --git a/examples/location/places/forms/SearchBoundingCircleForm.ui.qml b/examples/location/places/forms/SearchBoundingCircleForm.ui.qml new file mode 100644 index 0000000..b31d899 --- /dev/null +++ b/examples/location/places/forms/SearchBoundingCircleForm.ui.qml @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + property alias clearButton: clearButton + property alias goButton: goButton + property alias longitude: longitude + property alias latitude: latitude + property alias radius: radius + property alias cancelButton: cancelButton + property alias tabTitle: tabTitle + Rectangle { + id: tabRectangle + y: 20 + height: tabTitle.height * 2 + color: "#46a2da" + anchors.rightMargin: 0 + anchors.leftMargin: 0 + anchors.left: parent.left + anchors.right: parent.right + + Label { + id: tabTitle + color: "#ffffff" + text: qsTr("Search Bounding Circle") + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Item { + id: item2 + anchors.rightMargin: 20 + anchors.leftMargin: 20 + anchors.bottomMargin: 20 + anchors.topMargin: 20 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabRectangle.bottom + + GridLayout { + id: gridLayout3 + anchors.rightMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.topMargin: 0 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + id: label2 + text: qsTr("Latitude") + } + + TextField { + id: latitude + Layout.fillWidth: true + } + + Label { + id: label3 + text: qsTr("Longitude") + } + + TextField { + id: longitude + Layout.fillWidth: true + placeholderText: qsTr("") + } + + Label { + id: label4 + text: qsTr("Radius (m)") + } + + TextField { + id: radius + Layout.fillWidth: true + } + + RowLayout { + id: rowLayout1 + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignRight + + Button { + id: goButton + text: qsTr("Set") + } + + Button { + id: clearButton + text: qsTr("Clear") + } + + Button { + id: cancelButton + text: qsTr("Cancel") + } + } + Item { + Layout.fillHeight: true + Layout.columnSpan: 2 + } + } + } +} diff --git a/examples/location/places/forms/SearchCenter.qml b/examples/location/places/forms/SearchCenter.qml new file mode 100644 index 0000000..d1d82a3 --- /dev/null +++ b/examples/location/places/forms/SearchCenter.qml @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtPositioning 5.5 + +SearchCenterForm { + property string title; + property variant coordinate + signal changeSearchCenter(variant coordinate) + signal closeForm() + + goButton.onClicked: { + var coordinate = QtPositioning.coordinate(parseFloat(latitude.text), + parseFloat(longitude.text)); + if (coordinate.isValid) + changeSearchCenter(coordinate) + } + + clearButton.onClicked: { + latitude.text = "" + longitude.text = "" + } + + cancelButton.onClicked: closeForm() + + Component.onCompleted: { + latitude.text = "" + coordinate.latitude + longitude.text = "" + coordinate.longitude + if (title.length != 0) + tabTitle.text = title; + } +} diff --git a/examples/location/places/forms/SearchCenterForm.ui.qml b/examples/location/places/forms/SearchCenterForm.ui.qml new file mode 100644 index 0000000..df29acc --- /dev/null +++ b/examples/location/places/forms/SearchCenterForm.ui.qml @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + property alias clearButton: clearButton + property alias goButton: goButton + property alias longitude: longitude + property alias latitude: latitude + property alias cancelButton: cancelButton + property alias tabTitle: tabTitle + Rectangle { + id: tabRectangle + y: 20 + height: tabTitle.height * 2 + color: "#46a2da" + anchors.rightMargin: 0 + anchors.leftMargin: 0 + anchors.left: parent.left + anchors.right: parent.right + + Label { + id: tabTitle + color: "#ffffff" + text: qsTr("Search Center") + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Item { + id: item2 + anchors.rightMargin: 20 + anchors.leftMargin: 20 + anchors.bottomMargin: 20 + anchors.topMargin: 20 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabRectangle.bottom + + GridLayout { + id: gridLayout3 + anchors.rightMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.topMargin: 0 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + id: label2 + text: qsTr("Latitude") + } + + TextField { + id: latitude + Layout.fillWidth: true + } + + Label { + id: label3 + text: qsTr("Longitude") + } + + TextField { + id: longitude + Layout.fillWidth: true + placeholderText: qsTr("") + } + + RowLayout { + id: rowLayout1 + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignRight + + Button { + id: goButton + text: qsTr("Set") + } + + Button { + id: clearButton + text: qsTr("Clear") + } + + Button { + id: cancelButton + text: qsTr("Cancel") + } + } + Item { + Layout.fillHeight: true + Layout.columnSpan: 2 + } + } + } +} diff --git a/examples/location/places/forms/SearchOptions.qml b/examples/location/places/forms/SearchOptions.qml new file mode 100644 index 0000000..fe64f15 --- /dev/null +++ b/examples/location/places/forms/SearchOptions.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 +import QtPositioning 5.5 + +SearchOptionsForm { + id: root + property Plugin plugin + property PlaceSearchModel model + + signal changeSearchSettings(bool orderByDistance, + bool orderByName, + string locales) + signal closeForm() + + setButton.onClicked: changeSearchSettings(distanceOrderButton.checked, + nameOrderButton.checked, + locales.text) + + clearButton.onClicked: { + locales.text = "" + distanceOrderButton.checked = false + nameOrderButton.checked = false + } + + cancelButton.onClicked: { + closeForm() + } + + Component.onCompleted: { + locales.visible = root.plugin != null && root.plugin.supportsPlaces(Plugin.LocalizedPlacesFeature); + favoritesButton.visible = false; +// favoritesButton.enabled = placeSearchModel.favoritesPlugin !== null) +// isFavoritesEnabled = true; + locales.text = root.plugin.locales.join(Qt.locale().groupSeparator); + distanceOrderButton.checked = model.relevanceHint == PlaceSearchModel.DistanceHint + nameOrderButton.checked = model.relevanceHint == PlaceSearchModel.LexicalPlaceNameHint + } +} diff --git a/examples/location/places/forms/SearchOptionsForm.ui.qml b/examples/location/places/forms/SearchOptionsForm.ui.qml new file mode 100644 index 0000000..a2df9f1 --- /dev/null +++ b/examples/location/places/forms/SearchOptionsForm.ui.qml @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + property alias clearButton: clearButton + property alias setButton: setButton + property alias cancelButton: cancelButton + property alias tabTitle: tabTitle + property alias orderGroup: orderGroup + property alias distanceOrderButton: distanceOrderButton + property alias nameOrderButton: nameOrderButton + property alias favoritesButton: favoritesButton + property alias locales: locales + + Rectangle { + id: tabRectangle + y: 20 + height: tabTitle.height * 2 + color: "#46a2da" + anchors.rightMargin: 0 + anchors.leftMargin: 0 + anchors.left: parent.left + anchors.right: parent.right + + Label { + id: tabTitle + color: "#ffffff" + text: qsTr("Search Options") + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + } + } + + Item { + id: item2 + anchors.rightMargin: 20 + anchors.leftMargin: 20 + anchors.bottomMargin: 20 + anchors.topMargin: 20 + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.top: tabRectangle.bottom + + GridLayout { + id: gridLayout3 + anchors.rightMargin: 0 + anchors.bottomMargin: 0 + anchors.leftMargin: 0 + anchors.topMargin: 0 + rowSpacing: 10 + rows: 1 + columns: 2 + anchors.fill: parent + + Label { + id: label + text: qsTr("Locale(s)") + visible: locales.visible + } + + TextField { + id: locales + Layout.fillWidth: true + placeholderText: qsTr("") + } + + RadioButton { + id: favoritesButton + text: qsTr("Enable favorites") + Layout.columnSpan: 2 + } + + ExclusiveGroup { id: orderGroup } + RadioButton { + id: distanceOrderButton + text: qsTr("Order by distance") + exclusiveGroup: orderGroup + Layout.columnSpan: 2 + } + + RadioButton { + id: nameOrderButton + text: qsTr("Order by name") + exclusiveGroup: orderGroup + Layout.columnSpan: 2 + } + + RowLayout { + id: rowLayout1 + Layout.columnSpan: 2 + Layout.alignment: Qt.AlignRight + + Button { + id: setButton + text: qsTr("Set") + } + + Button { + id: clearButton + text: qsTr("Clear") + } + + Button { + id: cancelButton + text: qsTr("Cancel") + } + } + + Item { + Layout.fillHeight: true + Layout.columnSpan: 2 + } + + + } + } +} diff --git a/examples/location/places/helper.js b/examples/location/places/helper.js new file mode 100644 index 0000000..0e8851c --- /dev/null +++ b/examples/location/places/helper.js @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +function formatDistance(distance) +{ + if (distance < 1000) + return distance.toFixed(0) + " m"; + + var km = distance/1000; + if (km < 10) + return km.toFixed(1) + " km"; + + return km.toFixed(0) + " km"; +} diff --git a/examples/location/places/items/MainMenu.qml b/examples/location/places/items/MainMenu.qml new file mode 100644 index 0000000..3e6ff0e --- /dev/null +++ b/examples/location/places/items/MainMenu.qml @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtLocation 5.6 + +MenuBar { + property variant providerMenu: providerMenu + property variant settingsMenu: settingsMenu + + signal selectProvider(string providerName) + signal selectSetting(string setting); + + + + Menu { + id: providerMenu + title: qsTr("Provider") + + function createMenu(plugins) + { + clear() + for (var i = 0; i < plugins.length; i++) { + createProviderMenuItem(plugins[i]); + } + } + + function createProviderMenuItem(provider) + { + var item = addItem(provider); + item.checkable = true; + item.triggered.connect(function(){selectProvider(provider)}) + } + } + + Menu { + id: settingsMenu + title: qsTr("Settings") + + function createMenu(map) + { + clear() + var item = addItem(qsTr("Search Center")); + item.triggered.connect(function(){selectSetting("searchCenter")}) + item = addItem(qsTr("Search Bounding Box")); + item.triggered.connect(function(){selectSetting("searchBoundingBox")}) + item = addItem(qsTr("Search Bounding Circle")); + item.triggered.connect(function(){selectSetting("searchBoundingCircle")}) + item = addItem(qsTr("Search Options")); + item.triggered.connect(function(){selectSetting("SearchOptions")}) + } + } +} diff --git a/examples/location/places/items/MapComponent.qml b/examples/location/places/items/MapComponent.qml new file mode 100644 index 0000000..baa43a2 --- /dev/null +++ b/examples/location/places/items/MapComponent.qml @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtPositioning 5.5 +import QtLocation 5.6 +import "../helper.js" as Helper + +Map { + id: map + property bool followme: false + property variant scaleLengths: [5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000] + + function calculateScale() + { + var coord1, coord2, dist, text, f + f = 0 + coord1 = map.toCoordinate(Qt.point(0,scale.y)) + coord2 = map.toCoordinate(Qt.point(0+scaleImage.sourceSize.width,scale.y)) + dist = Math.round(coord1.distanceTo(coord2)) + + if (dist === 0) { + // not visible + } else { + for (var i = 0; i < scaleLengths.length-1; i++) { + if (dist < (scaleLengths[i] + scaleLengths[i+1]) / 2 ) { + f = scaleLengths[i] / dist + dist = scaleLengths[i] + break; + } + } + if (f === 0) { + f = dist / scaleLengths[i] + dist = scaleLengths[i] + } + } + + text = Helper.formatDistance(dist) + scaleImage.width = (scaleImage.sourceSize.width * f) - 2 * scaleImageLeft.sourceSize.width + scaleText.text = text + } + + center { + // The Qt Company in Oslo + latitude: 59.9485 + longitude: 10.7686 + } + + gesture.flickDeceleration: 3000 + gesture.enabled: true + onCopyrightLinkActivated: Qt.openUrlExternally(link) + + onCenterChanged:{ + scaleTimer.restart() + if (map.followme) + if (map.center != positionSource.position.coordinate) map.followme = false + } + + onZoomLevelChanged:{ + scaleTimer.restart() + if (map.followme) map.center = positionSource.position.coordinate + } + + onWidthChanged:{ + scaleTimer.restart() + } + + onHeightChanged:{ + scaleTimer.restart() + } + + Keys.onPressed: { + if (event.key === Qt.Key_Plus) { + map.zoomLevel++ + } else if (event.key === Qt.Key_Minus) { + map.zoomLevel-- + } + } + + Timer { + id: scaleTimer + interval: 100 + running: false + repeat: false + onTriggered: { + map.calculateScale() + } + } + + Item { + id: scale + visible: scaleText.text != "0 m" + z: map.z + 3 + anchors.bottom: parent.bottom + anchors.right: parent.right + anchors.margins: 20 + height: scaleText.height * 2 + width: scaleImage.width + + Image { + id: scaleImageLeft + source: "../../resources/scale_end.png" + anchors.bottom: parent.bottom + anchors.right: scaleImage.left + } + Image { + id: scaleImage + source: "../../resources/scale.png" + anchors.bottom: parent.bottom + anchors.right: scaleImageRight.left + } + Image { + id: scaleImageRight + source: "../../resources/scale_end.png" + anchors.bottom: parent.bottom + anchors.right: parent.right + } + Label { + id: scaleText + color: "#004EAE" + anchors.centerIn: parent + text: "0 m" + } + Component.onCompleted: { + map.calculateScale(); + } + } + + MapQuickItem { + id: poiTheQtComapny + sourceItem: Rectangle { width: 14; height: 14; color: "#e41e25"; border.width: 2; border.color: "white"; smooth: true; radius: 7 } + coordinate { + latitude: 59.9485 + longitude: 10.7686 + } + opacity:1.0 + anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2) + } + + MapQuickItem { + sourceItem: Text{ + text: "The Qt Company" + color:"#242424" + font.bold: true + styleColor: "#ECECEC" + style: Text.Outline + } + coordinate: poiTheQtComapny.coordinate + anchorPoint: Qt.point(-poiTheQtComapny.sourceItem.width * 0.5,poiTheQtComapny.sourceItem.height * 1.5) + } + + PositionSource{ + id: positionSource + active: followme + + onPositionChanged: { + map.center = positionSource.position.coordinate + } + } + + Slider { + id: zoomSlider; + z: map.z + 3 + minimumValue: map.minimumZoomLevel; + maximumValue: map.maximumZoomLevel; + anchors.margins: 10 + anchors.bottom: scale.top + anchors.top: parent.top + anchors.right: parent.right + orientation : Qt.Vertical + value: map.zoomLevel + onValueChanged: { + map.zoomLevel = value + } + } +} diff --git a/examples/location/places/items/SearchBar.qml b/examples/location/places/items/SearchBar.qml new file mode 100644 index 0000000..e5f1998 --- /dev/null +++ b/examples/location/places/items/SearchBar.qml @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +ToolBar { + + property bool busyIndicatorRunning : false + property bool searchBarVisbile: true + + signal doSearch(string searchText) + signal searchTextChanged(string searchText) + signal showCategories() + signal goBack() + signal showMap() + + onSearchBarVisbileChanged: { + searchBar.opacity = searchBarVisbile ? 1 : 0 + backBar.opacity = searchBarVisbile ? 0 : 1 + } + + function showSearch(text) { + if (text != null) { + searchText.ignoreTextChange = true + searchText.text = text + searchText.ignoreTextChange = false + } + } + + RowLayout { + id: searchBar + width: parent.width + height: parent.height + Behavior on opacity { NumberAnimation{} } + visible: opacity ? true : false + TextField { + id: searchText + Behavior on opacity { NumberAnimation{} } + visible: opacity ? true : false + property bool ignoreTextChange: false + placeholderText: qsTr("Type place...") + Layout.fillWidth: true + onTextChanged: { + if (!ignoreTextChange) + searchTextChanged(text) + } + onAccepted: doSearch(searchText.text) + } + ToolButton { + id: searchButton + iconSource: "../../resources/search.png" + onClicked: doSearch(searchText.text) + } + ToolButton { + id: categoryButton + iconSource: "../../resources/categories.png" + onClicked: showCategories() + } + } + + RowLayout { + id: backBar + width: parent.width + height: parent.height + opacity: 0 + Behavior on opacity { NumberAnimation{} } + visible: opacity ? true : false + ToolButton { + id: backButton + iconSource: "../../resources/left.png" + onClicked: goBack() + } + ToolButton { + id: mapButton + iconSource: "../../resources/search.png" + onClicked: showMap() + } + Item { + Layout.fillWidth: true + } + } +} + diff --git a/examples/location/places/main.cpp b/examples/location/places/main.cpp new file mode 100644 index 0000000..c677e42 --- /dev/null +++ b/examples/location/places/main.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +static bool parseArgs(QStringList& args, QVariantMap& parameters) +{ + + while (!args.isEmpty()) { + + QString param = args.takeFirst(); + + if (param.startsWith("--help")) { + QTextStream out(stdout); + out << "Usage: " << endl; + out << "--plugin. - Sets parameter = value for plugin" << endl; + out.flush(); + return true; + } + + if (param.startsWith("--plugin.")) { + + param.remove(0, 9); + + if (args.isEmpty() || args.first().startsWith("--")) { + parameters[param] = true; + } else { + + QString value = args.takeFirst(); + + if (value == "true" || value == "on" || value == "enabled") { + parameters[param] = true; + } else if (value == "false" || value == "off" + || value == "disable") { + parameters[param] = false; + } else { + parameters[param] = value; + } + } + } + } + return false; +} + +int main(int argc, char *argv[]) +{ + QGuiApplication application(argc, argv); + + QVariantMap parameters; + QStringList args(QCoreApplication::arguments()); + + // Fetch tokens from the environment, if present + const QByteArray mapboxMapID = qgetenv("MAPBOX_MAP_ID"); + const QByteArray mapboxAccessToken = qgetenv("MAPBOX_ACCESS_TOKEN"); + const QByteArray hereAppID = qgetenv("HERE_APP_ID"); + const QByteArray hereToken = qgetenv("HERE_TOKEN"); + const QByteArray esriToken = qgetenv("ESRI_TOKEN"); + + if (!mapboxMapID.isEmpty()) + parameters["mapbox.map_id"] = QString::fromLocal8Bit(mapboxMapID); + if (!mapboxAccessToken.isEmpty()) + parameters["mapbox.access_token"] = QString::fromLocal8Bit(mapboxAccessToken); + if (!hereAppID.isEmpty()) + parameters["here.app_id"] = QString::fromLocal8Bit(hereAppID); + if (!hereToken.isEmpty()) + parameters["here.token"] = QString::fromLocal8Bit(hereToken); + if (!esriToken.isEmpty()) + parameters["esri.token"] = QString::fromLocal8Bit(esriToken); + + if (parseArgs(args, parameters)) + return 0; + + QQmlApplicationEngine engine; + engine.addImportPath(QStringLiteral(":/imports")); + engine.load(QUrl(QStringLiteral("qrc:///places.qml"))); + QObject::connect(&engine, SIGNAL(quit()), qApp, SLOT(quit())); + + QObject *item = engine.rootObjects().first(); + Q_ASSERT(item); + + QMetaObject::invokeMethod(item, "initializeProviders", + Q_ARG(QVariant, QVariant::fromValue(parameters))); + + return application.exec(); +} diff --git a/examples/location/places/places.pro b/examples/location/places/places.pro new file mode 100644 index 0000000..baeef40 --- /dev/null +++ b/examples/location/places/places.pro @@ -0,0 +1,44 @@ +TARGET = qml_location_places +TEMPLATE = app + +QT += qml quick network positioning location +SOURCES += main.cpp + +RESOURCES += \ + places.qrc + +OTHER_FILES += \ + places.qml \ + helper.js \ + items/MainMenu.qml \ + items/SearchBar.qml \ + items/MapComponent.qml \ + forms/Message.qml \ + forms/MessageForm.ui.qml \ + forms/SearchCenter.qml \ + forms/SearchCenterForm.ui.qml \ + forms/SearchBoundingBox.qml \ + forms/SearchBoundingBoxForm.ui.qml \ + forms/SearchBoundingCircle.qml \ + forms/SearchBoundingCircleForm.ui.qml \ + forms/PlaceDetails.qml \ + forms/PlaceDetailsForm.ui.qml \ + forms/SearchOptions.qml \ + forms/SearchOptionsForm.ui.qml \ + views/SuggestionView.qml \ + views/RatingView.qml \ + views/CategoryView.qml \ + views/CategoryDelegate.qml \ + views/SearchResultDelegate.qml \ + views/SearchResultView.qml \ + views/EditorialView.qml \ + views/EditorialDelegate.qml \ + views/EditorialPage.qml \ + views/ReviewView.qml \ + views/ReviewDelegate.qml \ + views/ReviewPage.qml \ + views/ImageView.qml + +target.path = $$[QT_INSTALL_EXAMPLES]/location/places +INSTALLS += target + diff --git a/examples/location/places/places.qml b/examples/location/places/places.qml new file mode 100644 index 0000000..a511f0e --- /dev/null +++ b/examples/location/places/places.qml @@ -0,0 +1,507 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 +import QtPositioning 5.5 +import QtLocation 5.6 +import "items" + +ApplicationWindow { + id: appWindow + property Map map + property variant parameters + property variant searchLocation: map ? map.center : QtPositioning.coordinate() + property variant searchRegion: QtPositioning.circle(searchLocation) + property variant searchRegionItem + + property Plugin favoritesPlugin + + function getPlugins() { + var plugin = Qt.createQmlObject('import QtLocation 5.3; Plugin {}', appWindow); + var myArray = new Array; + for (var i = 0; i < plugin.availableServiceProviders.length; i++) { + var tempPlugin = Qt.createQmlObject ('import QtLocation 5.3; Plugin {name: "' + plugin.availableServiceProviders[i]+ '"}', appWindow) + + if (tempPlugin.supportsPlaces() && tempPlugin.supportsMapping() ) + myArray.push(tempPlugin.name) + } + myArray.sort() + return myArray; + } + + function initializeProviders(pluginParameters) + { + var parameters = new Array() + for (var prop in pluginParameters) { + var parameter = Qt.createQmlObject('import QtLocation 5.3; PluginParameter{ name: "'+ prop + '"; value: "' + pluginParameters[prop]+'"}',appWindow) + parameters.push(parameter) + } + appWindow.parameters = parameters + var plugins = getPlugins() + mainMenu.providerMenu.createMenu(plugins) + for (var i = 0; i0) + plugin = Qt.createQmlObject ('import QtLocation 5.3; Plugin{ name:"' + provider + '"; parameters: appWindow.parameters}', appWindow) + else + plugin = Qt.createQmlObject ('import QtLocation 5.3; Plugin{ name:"' + provider + '"}', appWindow) + + if (map) + map.destroy(); + map = mapComponent.createObject(page); + map.plugin = plugin; + map.zoomLevel = (map.maximumZoomLevel - map.minimumZoomLevel)/2 + categoryModel.plugin = plugin; + categoryModel.update(); + placeSearchModel.plugin = plugin; + suggestionModel.plugin = plugin; + } + + title: qsTr("Places") + width: 360 + height: 640 + visible: true + menuBar: mainMenu + toolBar: searchBar + + MainMenu { + id: mainMenu + onSelectProvider: { + stackView.pop(page) + for (var i = 0; i < providerMenu.items.length; i++) { + providerMenu.items[i].checked = providerMenu.items[i].text === providerName + } + + createMap(providerName) + if (map.error === Map.NoError) { + settingsMenu.createMenu(map); + } else { + settingsMenu.clear(); + } + } + onSelectSetting: { + stackView.pop({tem:page,immediate: true}) + switch (setting) { + case "searchCenter": + stackView.push({ item: Qt.resolvedUrl("forms/SearchCenter.qml") , + properties: { "coordinate": map.center}}) + stackView.currentItem.changeSearchCenter.connect(stackView.changeSearchCenter) + stackView.currentItem.closeForm.connect(stackView.closeForm) + break + case "searchBoundingBox": + stackView.push({ item: Qt.resolvedUrl("forms/SearchBoundingBox.qml") , + properties: { "searchRegion": searchRegion}}) + stackView.currentItem.changeSearchBoundingBox.connect(stackView.changeSearchBoundingBox) + stackView.currentItem.closeForm.connect(stackView.closeForm) + break + case "searchBoundingCircle": + stackView.push({ item: Qt.resolvedUrl("forms/SearchBoundingCircle.qml") , + properties: { "searchRegion": searchRegion}}) + stackView.currentItem.changeSearchBoundingCircle.connect(stackView.changeSearchBoundingCircle) + stackView.currentItem.closeForm.connect(stackView.closeForm) + break + case "SearchOptions": + stackView.push({ item: Qt.resolvedUrl("forms/SearchOptions.qml") , + properties: { "plugin": map.plugin, + "model": placeSearchModel}}) + stackView.currentItem.changeSearchSettings.connect(stackView.changeSearchSettings) + stackView.currentItem.closeForm.connect(stackView.closeForm) + break + default: + console.log("Unsupported setting !") + } + } + } + + //! [PlaceSearchSuggestionModel search text changed 1] + SearchBar { + id: searchBar + //! [PlaceSearchSuggestionModel search text changed 1] + width: appWindow.width + searchBarVisbile: stackView.depth > 1 && + stackView.currentItem && + stackView.currentItem.objectName != "suggestionView" ? false : true + onShowCategories: { + if (map && map.plugin) { + stackView.pop({tem:page,immediate: true}) + stackView.enterCategory() + } + } + onGoBack: stackView.pop() + //! [PlaceSearchSuggestionModel search text changed 2] + onSearchTextChanged: { + if (searchText.length >= 3 && suggestionModel != null) { + suggestionModel.searchTerm = searchText; + suggestionModel.update(); + } + } + //! [PlaceSearchSuggestionModel search text changed 2] + onDoSearch: { + if (searchText.length > 0) + placeSearchModel.searchForText(searchText); + } + onShowMap: stackView.pop(page) + //! [PlaceSearchSuggestionModel search text changed 3] + } + //! [PlaceSearchSuggestionModel search text changed 3] + + StackView { + id: stackView + + function showMessage(title,message,backPage) + { + push({ item: Qt.resolvedUrl("forms/Message.qml") , + properties: { + "title" : title, + "message" : message, + "backPage" : backPage + }}) + currentItem.closeForm.connect(closeMessage) + } + + function closeMessage(backPage) + { + pop(backPage) + } + + function closeForm() + { + pop(page) + } + + function enterCategory(index) + { + push({ item: Qt.resolvedUrl("views/CategoryView.qml") , + properties: { "categoryModel": categoryModel, + "rootIndex" : index + }}) + currentItem.showSubcategories.connect(stackView.enterCategory) + currentItem.searchCategory.connect(placeSearchModel.searchForCategory) + } + + function showSuggestions() + { + if (currentItem.objectName != "suggestionView") { + stackView.pop(page) + push({ item: Qt.resolvedUrl("views/SuggestionView.qml") , + properties: { "suggestionModel": suggestionModel } + }) + currentItem.objectName = "suggestionView" + currentItem.suggestionSelected.connect(searchBar.showSearch) + currentItem.suggestionSelected.connect(placeSearchModel.searchForText) + } + } + + function showPlaces() + { + if (currentItem.objectName != "searchResultView") { + stackView.pop({tem:page,immediate: true}) + push({ item: Qt.resolvedUrl("views/SearchResultView.qml") , + properties: { "placeSearchModel": placeSearchModel } + }) + currentItem.showPlaceDetails.connect(showPlaceDatails) + currentItem.showMap.connect(searchBar.showMap) + currentItem.objectName = "searchResultView" + } + } + + function showPlaceDatails(place, distance) + { + push({ item: Qt.resolvedUrl("forms/PlaceDetails.qml") , + properties: { "place": place, + "distanceToPlace": distance } + }) + currentItem.searchForSimilar.connect(searchForSimilar) + currentItem.showReviews.connect(showReviews) + currentItem.showEditorials.connect(showEditorials) + currentItem.showImages.connect(showImages) + } + + function showEditorials(place) + { + push({ item: Qt.resolvedUrl("views/EditorialView.qml") , + properties: { "place": place } + }) + currentItem.showEditorial.connect(showEditorial) + } + + function showReviews(place) + { + push({ item: Qt.resolvedUrl("views/ReviewView.qml") , + properties: { "place": place } + }) + currentItem.showReview.connect(showReview) + } + + function showImages(place) + { + push({ item: Qt.resolvedUrl("views/ImageView.qml") , + properties: { "place": place } + }) + } + + function showEditorial(editorial) + { + push({ item: Qt.resolvedUrl("views/EditorialPage.qml") , + properties: { "editorial": editorial } + }) + } + + function showReview(review) + { + push({ item: Qt.resolvedUrl("views/ReviewPage.qml") , + properties: { "review": review } + }) + } + + function changeSearchCenter(coordinate) + { + stackView.pop(page) + map.center = coordinate; + if (searchRegionItem) { + map.removeMapItem(searchRegionItem); + searchRegionItem.destroy(); + } + } + + function changeSearchBoundingBox(coordinate,widthDeg,heightDeg) + { + stackView.pop(page) + map.center = coordinate + searchRegion = QtPositioning.rectangle(map.center, widthDeg, heightDeg) + if (searchRegionItem) { + map.removeMapItem(searchRegionItem); + searchRegionItem.destroy(); + } + searchRegionItem = Qt.createQmlObject('import QtLocation 5.3; MapRectangle { color: "#46a2da"; border.color: "#190a33"; border.width: 2; opacity: 0.25 }', page, "MapRectangle"); + searchRegionItem.topLeft = searchRegion.topLeft; + searchRegionItem.bottomRight = searchRegion.bottomRight; + map.addMapItem(searchRegionItem); + } + + function changeSearchBoundingCircle(coordinate,radius) + { + stackView.pop(page) + map.center = coordinate; + searchRegion = QtPositioning.circle(coordinate, radius) + + if (searchRegionItem) { + map.removeMapItem(searchRegionItem); + searchRegionItem.destroy(); + } + searchRegionItem = Qt.createQmlObject('import QtLocation 5.3; MapCircle { color: "#46a2da"; border.color: "#190a33"; border.width: 2; opacity: 0.25 }', page, "MapRectangle"); + searchRegionItem.center = searchRegion.center; + searchRegionItem.radius = searchRegion.radius; + map.addMapItem(searchRegionItem); + } + + function changeSearchSettings(orderByDistance, orderByName, locales) + { + stackView.pop(page) + /*if (isFavoritesEnabled) { + if (favoritesPlugin == null) + favoritesPlugin = Qt.createQmlObject('import QtLocation 5.3; Plugin { name: "places_jsondb" }', page); + favoritesPlugin.parameters = pluginParametersFromMap(pluginParameters); + placeSearchModel.favoritesPlugin = favoritesPlugin; + } else { + placeSearchModel.favoritesPlugin = null; + }*/ + placeSearchModel.favoritesPlugin = null; + + placeSearchModel.relevanceHint = orderByDistance ? PlaceSearchModel.DistanceHint : + orderByName ? PlaceSearchModel.LexicalPlaceNameHint : + PlaceSearchModel.UnspecifiedHint; + map.plugin.locales = locales.split(Qt.locale().groupSeparator); + } + + //! [PlaceRecommendationModel search] + function searchForSimilar(place) { + stackView.pop(page) + searchBar.showSearch(place.name) + placeSearchModel.searchForRecommendations(place.placeId); + } + //! [PlaceRecommendationModel search] + + anchors.fill: parent + focus: true + initialItem: Item { + id: page + + //! [PlaceSearchModel model] + PlaceSearchModel { + id: placeSearchModel + searchArea: searchRegion + + function searchForCategory(category) { + searchTerm = ""; + categories = category; + recommendationId = ""; + searchArea = searchRegion + limit = -1; + update(); + } + + function searchForText(text) { + searchTerm = text; + categories = null; + recommendationId = ""; + searchArea = searchRegion + limit = -1; + update(); + } + + function searchForRecommendations(placeId) { + searchTerm = ""; + categories = null; + recommendationId = placeId; + searchArea = null; + limit = -1; + update(); + } + + onStatusChanged: { + switch (status) { + case PlaceSearchModel.Ready: + if (count > 0) + stackView.showPlaces() + else + stackView.showMessage(qsTr("Search Place Error"),qsTr("Place not found !")) + break; + case PlaceSearchModel.Error: + stackView.showMessage(qsTr("Search Place Error"),errorString()) + break; + } + } + } + //! [PlaceSearchModel model] + + //! [PlaceSearchSuggestionModel model] + PlaceSearchSuggestionModel { + id: suggestionModel + searchArea: searchRegion + + onStatusChanged: { + if (status == PlaceSearchSuggestionModel.Ready) + stackView.showSuggestions() + } + } + //! [PlaceSearchSuggestionModel model] + + //! [CategoryModel model] + CategoryModel { + id: categoryModel + hierarchical: true + } + //! [CategoryModel model] + + Component { + id: mapComponent + + MapComponent { + width: page.width + height: page.height + + onErrorChanged: { + if (map.error != Map.NoError) { + var title = qsTr("ProviderError"); + var message = map.errorString + "

" + qsTr("Try to select other provider") + ""; + if (map.error == Map.MissingRequiredParameterError) + message += "
" + qsTr("or see") + " \'mapviewer --help\' " + + qsTr("how to pass plugin parameters."); + stackView.showMessage(title,message); + } + } + + MapItemView { + model: placeSearchModel + delegate: MapQuickItem { + coordinate: model.type === PlaceSearchModel.PlaceResult ? place.location.coordinate : QtPositioning.coordinate() + + visible: model.type === PlaceSearchModel.PlaceResult + + anchorPoint.x: image.width * 0.28 + anchorPoint.y: image.height + + sourceItem: Image { + id: image + source: "resources/marker.png" + MouseArea { + anchors.fill: parent + onClicked: stackView.showPlaceDatails(model.place,model.distance) + } + } + } + } + } + } + } + } + + Rectangle { + color: "white" + opacity: busyIndicator.running ? 0.8 : 0 + anchors.fill: parent + Behavior on opacity { NumberAnimation{} } + } + BusyIndicator { + id: busyIndicator + anchors.centerIn: parent + running: placeSearchModel.status == PlaceSearchModel.Loading || + categoryModel.status === CategoryModel.Loading + } +} diff --git a/examples/location/places/places.qrc b/examples/location/places/places.qrc new file mode 100644 index 0000000..42026a6 --- /dev/null +++ b/examples/location/places/places.qrc @@ -0,0 +1,42 @@ + + + places.qml + helper.js + items/MainMenu.qml + items/MapComponent.qml + items/SearchBar.qml + forms/Message.qml + forms/MessageForm.ui.qml + forms/SearchCenter.qml + forms/SearchCenterForm.ui.qml + forms/SearchBoundingBox.qml + forms/SearchBoundingBoxForm.ui.qml + forms/SearchBoundingCircle.qml + forms/SearchBoundingCircleForm.ui.qml + forms/SearchOptions.qml + forms/SearchOptionsForm.ui.qml + forms/PlaceDetails.qml + forms/PlaceDetailsForm.ui.qml + views/SuggestionView.qml + views/CategoryDelegate.qml + views/CategoryView.qml + views/EditorialDelegate.qml + views/EditorialPage.qml + views/EditorialView.qml + views/ImageView.qml + views/RatingView.qml + views/ReviewDelegate.qml + views/ReviewPage.qml + views/ReviewView.qml + views/SearchResultDelegate.qml + views/SearchResultView.qml + resources/categories.png + resources/left.png + resources/marker.png + resources/right.png + resources/scale.png + resources/scale_end.png + resources/search.png + resources/star.png + + diff --git a/examples/location/places/resources/categories.png b/examples/location/places/resources/categories.png new file mode 100644 index 0000000000000000000000000000000000000000..b2d73ea0e0c017e24d80de0e8129465db9e44e2e GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H0wnYHF4+L2JUv|;Lp07OCrJ1lxN%?yBfp75 z=$(W=^87LWU61~*=o0>*mA4=~gGJ)~ac1TK(f8^H&o0qaJ1S|y!yBa3y5)tMrsBj& cF6>ea3V@;|>0q(VGh978nDCnq>C9%DPk#q*=F zu<2v{vKY_M*#Ra+je56GQC1n%&V{JH7+WXYh3Ob6Mw<&;$TifHC#} literal 0 HcmV?d00001 diff --git a/examples/location/places/resources/marker.png b/examples/location/places/resources/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..2116dfdf51bfb8ac9556af035d598cf2c68c44e3 GIT binary patch literal 752 zcmVP001Be1^@s6=bY090008FNklP9F+g_dGL z7b4wOL~YbX6}NhC`Y^Yem;_BVH^=jjGv%gj-gDq^?<8~n@6OCQGh-NK7!h@__p?7@ zAHg%1%k{H&7`2$=I6)SB$ey9%mf^nW7pw@t0liA$uW3=@<{h$6c2acLUNScZ#1n1& zj{lW0thX!xPr(xr5KoZZd4{Yl+b9~?SDiKsYw4jl=ahsz49ascH$lOp{)gvA{a+7Rbz6(!?wYhMaEN_*&FvlTY+Qe#GeLd5Y`1f1;(W)TxhmI&f zGMmlT7dqEPnL5c-$rEwKC^iKt`y;L(O{LEFOX&6M`3tgeo|g$ok3Ur@d#&7y#A^h6 z?>^6`UFq7fPoGtQPnFzO*2?UlncYoA0W+aJ?(31K(tFo2L@iE#B&)%{`ZRITEx!AZ zU)hx!$Bv5w?bcmR&+YjDzHG#=UYFZb?pf8h$W$s-$07?*nr~~*-vC?M)D-<;udDUC zx(`a(n9X1Re>5i2b#_ic_8U43M<~(&X=?Gdk$OC$)?v`lX*}=K5dL}Zz8b-2L$~o) id-3sdrSb5U8~YFSpzHYmbwF4E0000V@;|>0q@p}s978nDCnp?WIL391jps+> z#>S7;kN+Rz{2}_5UF)a6!^}da5W(CjlH3yJ360MKr$|~K*tuf?R literal 0 HcmV?d00001 diff --git a/examples/location/places/resources/scale.png b/examples/location/places/resources/scale.png new file mode 100644 index 0000000000000000000000000000000000000000..c4f08122ada97f0848409a7b4de914274d04d55e GIT binary patch literal 98 zcmeAS@N?(olHy`uVBq!ia0vp^ra;Wb!3HFgIj$80DNRoo#}JF&PQ<|Z&(Z(QQpe}ECj@=zX;NfjIC%2Ts>e~;XMma*JYD@<);T3K0RYVMA4vcJ literal 0 HcmV?d00001 diff --git a/examples/location/places/resources/scale_end.png b/examples/location/places/resources/scale_end.png new file mode 100644 index 0000000000000000000000000000000000000000..94510b1258e33726e65d4d5f539b8dc1dea5c905 GIT binary patch literal 93 zcmeAS@N?(olHy`uVBq!ia0vp^OhC-W0V2~}Z=?b#6;Bt(5RU7~DID*U6A}^<1tu^B pAL2^zV(}^D?rhdzjZ-+uz+kqK@vYpot*Suv44$rjF6*2UngECF7V7{2 literal 0 HcmV?d00001 diff --git a/examples/location/places/resources/search.png b/examples/location/places/resources/search.png new file mode 100644 index 0000000000000000000000000000000000000000..ce8c27aa6e387f5ac9cec3fe14c33dd99caca5d1 GIT binary patch literal 259 zcmeAS@N?(olHy`uVBq!ia0vp^Vn8gy!2%?eOIddVsZ*XVjv*T7=T3^|J#4_^%Bv{A zwD6K*07t6;M{59EBaj56#s{@Jt8X2dy6t9sVc)txJCxD|EG|s5-fY8qne|V~v6~sL zfm{@G=xuWP8-|V zmG|wJW;?gYF71;}bqfqzJ;VA+V4&8%>nGmE{Zk4En3CIa_hnDBN7jN#9LGLb1f?%N z<(47GIc=`eOee+7k8ZPmt=u{-``1%&#EP)zqJjq^g28AcLO?%AlpqE~#2|^N zs~}!@=UUeLVnr84MWeBiUpdLIP)o*%drn_f*9Qd*BsMD|N)%B|0v}*PK zmCUQ5zE20x4-5f4t5Nvb;0AaCYC*4RQotneFYp)_>;i47LHHULRtfL(&n7Ua8WhkS zJk*5uH7i(A4GNeJvc~Q!&VsgSSHNCFXk8Jn!H6^q-wQkzc3-&yxxx2o4t}$MWB9%q z3$9a|Orru?gQJ4Lj$sjRz}Pe@;3M$V5SC2-f8o7NX;i=hVYj7BK->bI(=++UBE8;8hPVAsuriFK>>ro ze-`ssID%X3CJSom3iy_HR~O6|a2Z`uk6Lt}0E=%AE($v@D4D*#zz({#-D@E9TZ1~>dlHD*eoQ!bpzgW zOHfX!E>ozi&RS%lKsrw9*vtff7dWl?F~keMO#bf+X1^2kwxz@2oJ#TmA5y6T`vs2L zbR1s-+JzlnY%%hP?=4E`w;gaYoa(X zkxPu>GrpTZT(F%nXuSR%=oH7vu{ZeHQfzoJ20G#%m=NGR> zAgN1FzYFOvU&JBC6fjp{i0T=^aaIxv=nI|+>CbDX@In2%e1I|~rPapn26*6Hq5AW* zuB;Q{N=y@<-5rZH+iL$Hlc!zWtY3WKc=8DkIrAa=3}#jMQ=AHzB;@2msf`wS2h8AxYmM-^J$OKq z3E6ehX^8ELEw{{F%VIoXG)4!eej&TjQ~0u^8**Hys>0DpRe!c_4c!i(*WBH~zyFZC zPGK6cDr>BXLjfZ>dfTcuRn*W0g<&OLv6}-P6zD-J>LUN%5gxEwsK(4iQAA%^uKT`p zk%4b`IF>?+e!d9fW~DCXoW(E63@>m*uPO6$JA$QJ@g-#*dj6uQ70?*4>vRI#M3Fq= z?h{?-`BS)9scM9cQ7hmVq5P%vHZ`vA{c#s+trm;)P53hEhY#ReSWP@~-o{^sh_x1H zYoLj5J$S`m#iPMU_c$ItfLmchL(SWxT}uznOe13v{{1UuuXoV~ok6Cp 0 ? model.title : qsTr("Untitled editorial") + font.bold: true + + wrapMode: Text.WordWrap + elide: Text.ElideRight + maximumLineCount: 2 + } + + MouseArea { + anchors.fill: parent + onClicked: showEditorial() + } +} diff --git a/examples/location/places/views/EditorialPage.qml b/examples/location/places/views/EditorialPage.qml new file mode 100644 index 0000000..76f956d --- /dev/null +++ b/examples/location/places/views/EditorialPage.qml @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + id: root + property variant editorial + width: parent.width + height: parent.height + + ScrollView { + id: scrollView + flickableItem.interactive: true + anchors.fill: parent + anchors.margins: 15 + + ColumnLayout { + width: scrollView.width - 30 + spacing: 10 + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: "#46a2da" + } + + Label { + text: editorial.title + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + Label { + text: editorial.text + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: "#46a2da" + } + + Image { + Layout.alignment: Qt.AlignHCenter + source: editorial.supplier.icon.url(Qt.size(width, height), Icon.List) + } + + Label { + text: editorial.supplier.name + Layout.alignment: Qt.AlignHCenter + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + Button { + id: button + text: qsTr("Open url") + Layout.alignment: Qt.AlignHCenter + onClicked: { + Qt.openUrlExternally(editorial.supplier.url) + } + } + } + } +} diff --git a/examples/location/places/views/EditorialView.qml b/examples/location/places/views/EditorialView.qml new file mode 100644 index 0000000..8b93f3c --- /dev/null +++ b/examples/location/places/views/EditorialView.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 + +//! [PlaceEditorialModel view] +ListView { + id:view + property Place place + signal showEditorial(variant editorial) + width: parent.width + height: parent.height + model: place.editorialModel + delegate: EditorialDelegate { + onShowEditorial: view.showEditorial(model) + } +} +//! [PlaceEditorialModel view] + diff --git a/examples/location/places/views/ImageView.qml b/examples/location/places/views/ImageView.qml new file mode 100644 index 0000000..6ece893 --- /dev/null +++ b/examples/location/places/views/ImageView.qml @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 +import QtQuick.Controls 1.4 + +Item { + id: root + property Place place + width: parent.width + height: parent.height + + GridView { + id: gridView + + anchors.fill: parent + + model: place.imageModel + + cellWidth: width / 3 + cellHeight: cellWidth + + delegate: Rectangle { + width: gridView.cellWidth + height: gridView.cellHeight + + color: "#30FFFFFF" + + Image { + anchors.fill: parent + anchors.margins: 5 + + source: url + + fillMode: Image.PreserveAspectFit + } + + MouseArea { + anchors.fill: parent + onClicked: { + listView.positionViewAtIndex(index, ListView.Contain); + root.state = "list"; + } + } + } + } + + ListView { + id: listView + + anchors.top: parent.top + anchors.bottom: position.top + width: parent.width + spacing: 10 + + model: place.imageModel + orientation: ListView.Horizontal + snapMode: ListView.SnapOneItem + + visible: false + + delegate: Item { + width: listView.width + height: listView.height + + Image { + anchors.fill: parent + source: url + fillMode: Image.PreserveAspectFit + + MouseArea { + anchors.fill: parent + onClicked: root.state = "" + } + } + + Button { + id: button + text: qsTr("Open url") + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + onClicked: { + Qt.openUrlExternally(supplier.url) + } + } + } + } + + Label { + id: position + + width: parent.width + anchors.bottom: parent.bottom + visible: listView.visible + + text: (listView.currentIndex + 1) + '/' + listView.model.totalCount + horizontalAlignment: Text.AlignRight + } + + states: [ + State { + name: "list" + PropertyChanges { + target: gridView + visible: false + } + PropertyChanges { + target: listView + visible: true + } + } + ] +} diff --git a/examples/location/places/views/RatingView.qml b/examples/location/places/views/RatingView.qml new file mode 100644 index 0000000..1f71355 --- /dev/null +++ b/examples/location/places/views/RatingView.qml @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 + +Row { + property real rating: 0 + property int size: 0 + + Repeater { + model: Math.ceil(rating) + Image { + source: "../../resources/star.png" + width: size + height: size + } + } +} diff --git a/examples/location/places/views/ReviewDelegate.qml b/examples/location/places/views/ReviewDelegate.qml new file mode 100644 index 0000000..08cdef8 --- /dev/null +++ b/examples/location/places/views/ReviewDelegate.qml @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 +import QtQuick.Controls 1.4 + +Item { + id: root + signal showReview() + + width: parent.width + height: icon.height + 8 + + Image { + id: icon + + width: 64 + height: 64 + + anchors.verticalCenter: root.verticalCenter + anchors.left: root.left + anchors.leftMargin: 4 + + source: model.supplier.icon.url(Qt.size(64, 64), Icon.List) + fillMode: Image.PreserveAspectFit + } + + Label { + anchors.top: icon.top + anchors.topMargin: 4 + anchors.left: icon.right + anchors.leftMargin: 4 + anchors.right: root.right + anchors.rightMargin: 4 + + text: model.title + font.bold: true + + wrapMode: Text.WordWrap + elide: Text.ElideRight + maximumLineCount: 2 + } + + RatingView { + anchors.bottom: icon.bottom + anchors.bottomMargin: 4 + anchors.left: icon.right + anchors.leftMargin: 4 + anchors.right: root.right + anchors.rightMargin: 4 + + rating: model.rating + size: 16 + } + + MouseArea { + anchors.fill: parent + onClicked: showReview() + } +} diff --git a/examples/location/places/views/ReviewPage.qml b/examples/location/places/views/ReviewPage.qml new file mode 100644 index 0000000..19fbd77 --- /dev/null +++ b/examples/location/places/views/ReviewPage.qml @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +Item { + id: root + property variant review + width: parent.width + height: parent.height + + ScrollView { + id: scrollView + flickableItem.interactive: true + anchors.fill: parent + anchors.margins: 15 + + ColumnLayout { + width: scrollView.width - 30 + spacing: 10 + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: "#46a2da" + } + + Label { + text: review.title + width: parent.width + wrapMode: Text.WordWrap + } + + Label { + text: Qt.formatDateTime(review.dateTime) + width: parent.width + Layout.alignment: Qt.AlignHCenter + } + + RatingView { + size: 16 + rating: review.rating + } + + Label { + text: review.text + width: parent.width + wrapMode: Text.WordWrap + Layout.alignment: Qt.AlignHCenter + textFormat: Text.RichText + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 1 + color: "#46a2da" + } + + Image { + Layout.alignment: Qt.AlignHCenter + source: review.supplier.icon.url(Qt.size(width, height), Icon.List) + } + + Label { + text: editorial.supplier.name + Layout.alignment: Qt.AlignHCenter + wrapMode: Text.WordWrap + textFormat: Text.RichText + } + + Button { + id: button + text: qsTr("Open url") + Layout.alignment: Qt.AlignHCenter + onClicked: { + Qt.openUrlExternally(review.supplier.url) + } + } + } + } +} diff --git a/examples/location/places/views/ReviewView.qml b/examples/location/places/views/ReviewView.qml new file mode 100644 index 0000000..669a06d --- /dev/null +++ b/examples/location/places/views/ReviewView.qml @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 + +//! [ReviewModel delegate] +ListView { + id:view + property Place place + signal showReview(variant review) + width: parent.width + height: parent.height + model: place.reviewModel + delegate: ReviewDelegate { + onShowReview: view.showReview(model) + } +} +//! [ReviewModel delegate] + diff --git a/examples/location/places/views/SearchResultDelegate.qml b/examples/location/places/views/SearchResultDelegate.qml new file mode 100644 index 0000000..d6b3717 --- /dev/null +++ b/examples/location/places/views/SearchResultDelegate.qml @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 +import "../helper.js" as Helper + +Item { + id: root + + signal showPlaceDetails(variant place,variant distance) + signal searchFor(string query) + + width: parent.width + height: childrenRect.height + + //! [PlaceSearchModel place delegate] + Component { + id: placeComponent + Item { + id: placeRoot + width: root.width + height: Math.max(icon.height, 3 * placeName.height) + + Rectangle { + anchors.fill: parent + color: "#44ffffff" + visible: mouse.pressed + } + + Rectangle { + anchors.fill: parent + color: "#dbffde" + visible: model.sponsored !== undefined ? model.sponsored : false + + Label { + text: qsTr("Sponsored result") + horizontalAlignment: Text.AlignRight + anchors.right: parent.right + anchors.bottom: parent.bottom + font.pixelSize: 8 + visible: model.sponsored !== undefined ? model.sponsored : false + } + } + + GridLayout { + rows: 2 + columns: 2 + anchors.fill: parent + anchors.leftMargin: 30 + flow: GridLayout.TopToBottom + + Image { + // anchors.verticalCenter: parent.verticalCenter + id:icon + source: place.favorite ? "../../resources/star.png" : place.icon.url() + Layout.rowSpan: 2 + } + + Label { + id: placeName + text: place.favorite ? place.favorite.name : place.name + Layout.fillWidth: true + } + + Label { + id: distanceText + font.italic: true + text: Helper.formatDistance(distance) + Layout.fillWidth: true + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 15 + height: 1 + color: "#46a2da" + } + + MouseArea { + id: mouse + anchors.fill: parent + onClicked: { + if (model.type === undefined || type === PlaceSearchModel.PlaceResult) { + if (!place.detailsFetched) + place.getDetails(); + root.showPlaceDetails(model.place, model.distance); + } + } + } + } + } + //! [PlaceSearchModel place delegate] + + Component { + id: proposedSearchComponent + + Item { + id: proposedSearchRoot + + width: root.width + height: Math.max(icon.height, 2 * proposedSearchTitle.height) + + Rectangle { + anchors.fill: parent + color: "#11ffffff" + visible: mouse.pressed + } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 30 + + Image { + source: icon.url() + } + + Label { + id: proposedSearchTitle + anchors.verticalCenter: parent.verticalCenter + text: "Search for " + title + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: 15 + height: 1 + color: "#46a2da" + } + + MouseArea { + anchors.fill: parent + onClicked: root.ListView.view.model.updateWith(index); + } + } + } + + Loader { + anchors.left: parent.left + anchors.right: parent.right + + sourceComponent: { + switch (model.type) { + case PlaceSearchModel.PlaceResult: + return placeComponent; + case PlaceSearchModel.ProposedSearchResult: + return proposedSearchComponent; + default: + //do nothing, don't assign component if result type not recognized + } + } + } +} diff --git a/examples/location/places/views/SearchResultView.qml b/examples/location/places/views/SearchResultView.qml new file mode 100644 index 0000000..6e6c415 --- /dev/null +++ b/examples/location/places/views/SearchResultView.qml @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtLocation 5.6 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.2 + +//! [PlaceSearchModel place list] +ListView { + id: searchView + width: parent.width + height: parent.height + + property variant placeSearchModel + signal showPlaceDetails(variant place, variant distance) + signal showMap() + + model: placeSearchModel + delegate: SearchResultDelegate { + onShowPlaceDetails: searchView.showPlaceDetails(place, distance) + onSearchFor: placeSearchModel.searchForText(query); + } + + footer: + + RowLayout { + width: parent.width + + Button { + text: qsTr("Previous") + enabled: placeSearchModel.previousPagesAvailable + onClicked: placeSearchModel.previousPage() + Layout.alignment: Qt.AlignHCenter + } + + Button { + text: qsTr("Clear") + onClicked: { + placeSearchModel.reset() + showMap() + } + Layout.alignment: Qt.AlignHCenter + } + + Button { + text: qsTr("Next") + enabled: placeSearchModel.nextPagesAvailable + onClicked: placeSearchModel.nextPage() + Layout.alignment: Qt.AlignHCenter + } + } +} +//! [PlaceSearchModel place list] diff --git a/examples/location/places/views/SuggestionView.qml b/examples/location/places/views/SuggestionView.qml new file mode 100644 index 0000000..960b5a2 --- /dev/null +++ b/examples/location/places/views/SuggestionView.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 + +//! [PlaceSearchSuggestionModel view 1] +ListView { + id: suggestionView + property variant suggestionModel + signal suggestionSelected(string text) +//! [PlaceSearchSuggestionModel view 1] + snapMode: ListView.SnapToItem +//! [PlaceSearchSuggestionModel view 2] + model: suggestionModel + delegate: Item { + width: parent.width + height: label.height * 1.5 + Label { + id: label + text: suggestion + } + MouseArea { + anchors.fill: parent + onClicked: suggestionSelected(suggestion) + } + } +} +//! [PlaceSearchSuggestionModel view 2] + diff --git a/examples/location/places_list/Marker.qml b/examples/location/places_list/Marker.qml new file mode 100644 index 0000000..c1f33de --- /dev/null +++ b/examples/location/places_list/Marker.qml @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Rectangle { + width: image.width + Image { + id: image + anchors.centerIn: parent + source: "marker.png" + Text{ + y: parent.height/10 + width: parent.width + color: "white" + font.bold: true + font.pixelSize: 14 + horizontalAlignment: Text.AlignHCenter + text: index + } + } +} diff --git a/examples/location/places_list/doc/images/places_list.png b/examples/location/places_list/doc/images/places_list.png new file mode 100644 index 0000000000000000000000000000000000000000..bf09a0314174aba8e59a2c609e563d2f0e0312a0 GIT binary patch literal 18195 zcma&Nby!qU`zQ)XIh4TAjl|F(-Q6-05=w(gcXxvfE!|y$lyrAW4IM+*(2exP@BF@V z&wb86_mA~Fd%v;bUF*%YcepB89t%JUKtMpiQdE#pM?gS)c}8+VfAs?CocLw;^75$q z{-f;6>+|#TEOcUvl3pQa2oVAC{^kAo@A>)p3roSw@$>WP)$P;u)ARG~aMjEXCIAzQ z)tifhqjh?9;{LgDW}|jy3mXxkY*_9E~s^jYE?(+E={(Ro}{Iv0W-KDLi z_58Gae|-@zj%7;r>iK?ZBUP53j!rgh0|A;!-tPVVeegQ~tJfbAwOk~$x0*lq zJEvF3kdd#uZ1#4yDL$8PlF|%aKc{a!zrvKXWgvt*Z-pzYWXSce%L}gl!gvKTP zhIMvz9~#W_I2%eqps|sw=05z_sP1%ec19gYRq>fDVDkR2JrzLxcQXRhbwum@*db4%g>TI{bbclAlFHKOUcQD49*o zEKj6=yPuEcBUJdJBI+YZQlLg~vAZm;<8)~9Eg>eV*j?pjVBlgTX5+Wro}@yl770X_ zxF|0t%Z4Yes>vt2mW2FE^uUpEOs}K68zH+!QA0~rZP|&pU!n>bzirNYDRD`Ya6)0% zuh~$|uPFz*hCeKnFRSXcb$qxaG|Nnx9m?lLyqhl<%TJz9oSXIye9O|)Q(Hr%cKO7X z=9*0s%dtqfvoO#$TZkx$;bxQX&xHRO0hm8HIOSqi7vGa}JdaJa}ZFefc zcwDnPGgW$*V|rv}UpWO|SR323toiFD28BlLsHa>O6rBcz-WA~!`Bc-rC;+0fI#>fi zVrTvv0)lj}qKxE6_r=3xvCM;-Tk^1HY@))`CD5JuG}G_>PN zjmc*)$7tRWakA=q5o8ar>j6+4Ht>Tc2QyTd$_-U&tb1oezD=S=iv=+yvi=6zmY}b; zD}NO*CC~Dk=BFi&-^YoM6OIo0L)cEfZ|v8J=sl9eANMr^SAyL-VgCd|di6C&^rt!9 zGgo+RUPUk74qs1W02gk;msje>`h5=#Z@7{QBau3-O0MnSfBdllxz%A#87fx~*`!12 zG%0!|;lUBh4}#wHJ-2k?JvT}6Cj_DN0y zp%Lw?6j+1+bzjL{AR4fNONWL)imlq+dFe4%#%!IafJ#D(CXUR=P#aVx8K64&=6 zA3_~cf?d${A{90fK?IZF6mo;JYDExfFT!mnVL-*k%jy4FKP{_|R+|^~ghg)3an7mT zn7gr3Au6xozlKhVX(*iu+O06`-}XS>Ea{A9(k$s&6VSmPcOJHiQJB@phxCG@rY+4s z{5BV6$O`snDKGOUa-(8Gfyd>`r`ig~;59LZJ6hPW5}e{su`VL|spDpdl9dLn8;Ck? zS?6>hM?7}!TEE9%^NwIo#2!nN6*f`{5#O?P;QVVZa9O9~oAIt6I`6h#JivscpGS1$ z&qDA^{I;Nd_0g?Vs}{dP^I_bN;91jKHsnma-;aPo^N?|=h}6%jU9m4?yWWP%yXKEh z^Ouu-4vus;IdEzPkCSh^cC0V8YT(u{z!W=@dX>@2euShUUfJW!fLx$&EM zrm*Mf2B4>r_oGA<+4wgqfhMm>4Hn^d7ws;Or{VGFnV#kksJB`KRP)^9DWE_Q!fsP4 zO!#IjkYSj~b3XAE4sIeX^VJ*G&OitX0mgoKmyxt66CP*35sFL|E;3ynD6l`UT85sB zaC+YAP|h@x{^@M=xVc=3He7tI_hg`LiT`){pN0ztekUIzZ-o*Jwpf%Sbei1g&EU0n z?*zvB69v2!nIo-JB#-dFJ@3$9U*!&mYJAlm*AC%)J~JKv4u?chCG)fH$8#2waH*gsnoj{x;kQxL;C!a z8~QOJ_VoqUjbF@|*Oxgc8(Y9|l#O02asg`(wAz`|X>FAD<@0wA3)3g`;l~L2ZqQgO z>hHy21xPK0hF`S54}}Suef$oa=z} z=Hni>qLm?n4-m_n;VcFOiPLnSvK6@Vz}1y1fpBS*0z5k=D#V&mKb5I+QTP8o^ffF~ zpG1>T=L#+uPvzdN2fiCRgHY7{?jAJRQde}BeO;>QK$Cdc^82awUf@T2$Humhp?Hwz zL2jD)oNr56gx{}FtwAM(C(preEf_h*qkn}Kk3p#{L zT;&F?`Acox^@QDN6FmXFC(U~HihX8&Dl6%L=zh{aI{cRXu2^(*|VgqU(P#-0X&c2%< z(i8jYRI5gzJ}t;{PO=5lRT!R-s92+=qRTT=ILVHI2!B2s`qBRU8NzksX#l?<+b*0k zOmKmG*7g*g$7OXGU{=~ek^7Jm-XY@iclsPU0K&y6xL6Ad!}!KJ=@wJw8@~ZlU<@y( z750~GugWwCr&5sM`!0jJJ`Z+*J4VNJ$G`4iik}!mTs`x6Yn0(cT^}=`zE1e}fb{~& z`11<-eHS~ih+K3gq%e{AJ2S0ri*zw4zrCe_kUMIhxDuP-6K2i%tNGQhD^_c7rH7E> z*74;e<^~S1qP-L1`nWT+6eiGr(z43x`*!m!CVu|J;v5W^wVSQ@&Ltv~GtV(Si$^5nT0gj)MpS zUSlaR+b1QAWg$;}v(EeUnR@fV<+3cX#E6c-w+4VL^LIMuotD;Q-Mu--TDnIrpdyuM z&+OO!vS}jW569XcfJ29qPQ9uVquRc5wx22WXS5ii_qyQ_e<1)J0^OyV{%DcbfE6-< zz1Y_$)%f4hKZ#8!ErmGo0azDdX>DH#Cdw%E$uKNBCO@JOHK7X&e2xd~@Rs@Yd7Iu= z0tle>y7T%=76|KnyL7)h1*^JubXmUkYJm$;984?zY&_x;H(Q)S5lI-0{Ctd2Pc8l|>Tq{H-inMYqR7lp3oMspqN?alD?s<}kdgXDY0s?objD{Q>$ z-@<`)xvGdCNa*{hdx{w<*cKJghzL7Ma125l-h{5XcHzih$25np$wxk?@7q_lH(&aGA1j_w3;Qu_iWsJ0ik&5jjxf(dYGt zmh$aIBz1~)ihS?e?>F}(mEWmx@m8HqY$|_!VUf0LIiV6gs{L`{aO~i>d-Lcz8tAJB z+~6W)>zqJ~P|uZjUs{vr5o??zC0!VLkdNIRi(OgH|13Rn%?b&eJs;4Nd55Pa(I-h8 zZbHkPL~OqEV7-(U z{r-_IZ!Li)c91%64^!BbsZm^5bI~Dp9j>PmsC8uGd=gBuYcbO;n4BfP3D1bGd29!O z?r=!u=Hf*K7=1DNpau`m`N|_%CMH_tYF?FW*c2&3YAXHvw-Kmx!KTC?%a{Xia;8m) zz)CZ(WXEx%5OC2xtE&kDzo@){f!c8;H)Om_`2BI)EP9S>2yOBM7xuPf=2~OnwKhrw z4I7XQSloWhMy7ki5jHR_SfcJ*u@-4DY!;M*bg=Y(k@SQ5X}lw^QKin;G~%Bvv=JDc z>mQNrQjGZvHq1V3vzMvWQz;_@eu!}}@p#b3MV70UWBnxg0wc2$k={GV8QU?a7@+2v zVzVPDr}ZA!>+gulq+v=fCIF}}AFwzmeafe{`tJ0zIo=>DkF+m4&wg|SEaUUk36A;s z%mfLAyeF}+s4TvIChXG(ev#;M={>-!$o0)B!51!72ybs1=~0=kZvTm_%;?h^Z2Qv} zLzXSu)iKB3#_Q0?IGLv+_sLWBb407Gh8|XrZtJ{gGbd}7Yt({y_+KSost_tmaL)I9 zpD)?A<|R`~JsfVP(SDfC#*IC#=EPtw9^UyNhsVmi(>XdBeK+kuR5rD7s4tB$%W3nq z)Y{eSHr`?&)0OlUGJBxdUZRp_qHI8(cX*4VIUV^iMxFqW&UWzpxO!n^X=rZGP z6ir^QO{GYX*D2xyOMFCye}xB*A~9++<)@K0V;6_v9Zn#Kf*HNFBWZtV7OHg`Z&dU~ z#sdQ^DMK1Qe8EYcG4{dDPM_!X%F4Qf6}DJtEgl=hh~+qeVMpH5`tH_|ROd-9t+D4t zp9=X`vSN}pTQ!=0DgonHzTv(uYbGy~J2OAmd=t*h*_8E_5SWg7=^Y#dU3b=wcdq;~m%c-l{F|)Rwj;PF^Ub0J$8eyegT~`6 z{k=QTX!oe+y?g!4fpa{)f$F5J@7`l9>g3JBr}w?}?}gOklQl?m_*9xzQJ%WuGTy7Z za~hwqc@yh1$CIyew1Od(-K&A!U+K={8zrhLsBdr{a`YsE_Bc)F6Rk%I$ViTsxkp`k z{QFk>z6+A1ZuMsAa62FlX&2LTrdDs#lKsi#)0fEY2~Kr&u9S#z2vaOhHYP46V;?!u zG2LNjm}*Z7QAu@HL6Q@>h>^@n>O~3en-clOsD2Yq0uI2iIP{LYMG9~(w8Tb9i!f&c z1nnZ>pw|3muE*mN;)*|RN}Xg673)hujg~6PD5{a3p2u0NyZo+)DVjWfd+JDuk7wO3 zv2T1$4mn!K^>wEtqeP@`Dhen*p`Yi4IC2SKMJL;xtsfIT{Z_hc;f+XD853vp>@N2Jeu z>}$g5R8PinE>-(`kaS9_BxG_ z`lt;Ku?|}D=V7qn-n|t9q7Irgn?CQns+?9>CbeCZ?%*EKFs(&CD8W@Vh@$WKn4y2~ zue%t*NWQ?wv14#)!T#%oDzp7r|KoRyl3&e2O8(Jk@yThQ22W9NanUcr5|L4MGz0_u zja*N}yaKw3@eV<*ZVeC<=RBBb$`%BJ@{2tz$M%H54cHVX0Pyv^MzZoY;%E;(V7&5(*J!%?qf}$*CLa3BlE{ZWpBp8q3 zk$VKFIy6DtGf~cC#c-c5sHHLWzT?LACUZH}erj~orImkk*`uYw?Y^nO>*o4V%p0*U zk+(T~?nyR3y=wcoq>&rs?M991{<6&Ei@guRI=vEfRh<=m~j4oUSI|WQi#$39Ifnb;#B6k#rr{%P7@> zvV>OVj^V`oT3r1#uh$}OzqYocnWf7>L&18h6fvyifKEo|plaXZ5n`*ZVd z)cKEBHYaUIyd9KbRYML|Us+tm1@sJnq}3*CrCt{fSoTGnWfn#yTwOWtIaZIaR6?73 z#xE~F+V$UL{)vC1rs{SG#VOc=JqGSYs{#ABw(i6XunkApI1stIC9q?M3c1wb+`~SH zq3?8%@TGHcr#uosc>;}umRU||n1A{APEKY|w-5sEBfN@*wHsnpRox;(V{qLf^ZX;b zcNl`m+mkTFf>+;B1$I}eypBuy4LQSWta!Ci{S1ZY;_|;GK?!!iWrb{VYrAXhp+q8m z5b)IcA~Z*D3fXuI+2F{ilZA14R-9s_vV>LYha!ExhodkE+F~icmFy~-_znsTeBWX> zh0!>-cJ2maK}HcFY2h1#bib#?bje6KPWO_XuS-w&-a6%d9b&uW9N_o&b;_5y7O#W4 zDjD6R?N{q>P$dGgzXpzYrApZj7YuB^#gah%gV=_EMvU_zbiM3%UhKisGrIg}?R9pZ z{|c3d*2^>$K@&a%`+Gh6!CMJtla{S?LKTOpi^wh=NjVbsz-j(3fM$a_2jzB2f=Y0S z-2?`r&Hns?)v3>}+}WO>0irybtRxzIo}+l(s@>Sx6*fv6%!Ucr+zu(R)F5x#JeYkr zw^oyJd#k&tMzHJ_8rMVs*pGfhv~%nU#d0A!B<|XoyPKUNNF6(wQUjh0g6uJxjRw%` z2g3N;ui7r+|LwrxlpVdBHLcwLp$Q>TxYn^Be;TZCh;4pTeH2pt zwNJX@9ZN2>^XPzd+Ij!Ty@H++%;y5;^6m$2`f7P*LkhBMhIplnfX2CVAqStFTz* zf1S!zRHh#ZEz@@`v|r;>KZqBuBLo=o`!Ko>H{NA$*kpYM9y3#3LXg82}R-i`%W{L0jKIH`;tim#KJCxLwP-D zsZrr;lwgou(zF}x^(&+`wrfa5an|D4r~m<`4<{(FEKaRtqx#-AN$J$JK8D+0sGtI- zLnNgp@;Vz7C{Q1#(S;mhASZd{9886<&`7@0!kmRLv_q*oS1lnEaD%z!D$tfTPLP7K z^>EY~Zu^C*dYPm2#Ge-Gzax#gQsUGnNxr*ttb#p|Xm7vg(YIEOsxll^gKVb_WDBS} zRUDh^J$9FtR@=kbT+xrbm@Zd)vlNsAn)(vZzLLzgQocUywGv{ZSXTJ3ToN!Q)gk%N zx=tR4&RCo^^~-D>VMu5az2HTb*tz;-#a#==mljSn4ClR74E66`eQ}%55OY3>LyPHR z8Nq9XjT3kw-|$6y6=})kmIsUryCVDj#L||I8rc7ML_}P8lLf7@13!N;Q>%|iA^B3Dgm&)Q;Y8`~&Zj~~F ze#8S6pZH<3$ z@G81WNb>EMyx_BO-p*;1XIyd^hjO-{I0jX{Ut#o56vII|Jepl+x{CYdB~)epWJ+D@ zW09PjQ^tRCUyUfdSEg(HhqV)nGJtXAS@CUTLm?ucIoK-RP!9k5cEsa&Q6?w{^oV9U#+>B#lbSiNJl-UXlG}5>!(e`I^Fi8_Ngn1%Y@=E zE;qCYiD3RfH~Hg_C@r%HQjwb6g#pC@Ok8XHkt#*$u>oZTcPQ#_$!yzgt zfAMxsmUe$c4gY6=aYgZG{NcEZ_e_xPWHKxJm~pmH4BpAlki5_iOxc(F7`0tM^BwCv z(S$vXtdQeP^8`9??PW=%Y$B)k97bj-++Kh>7$a}}Qv|?>w`$JHy;bc~kUSsHy#Rb| zC+w}}g`zU!<4(?6InsQC|5vqE%U|DVxmJ;6`)hx9gMd20YXoON6z^rC295{zMh46@ z>U@dzAiG!=L7A;EOa(pt6Drm`|9hGL>Czv`F{jkuS}p+Fn@V1FtBX?JDjdnU0JOG@ z+k@sFRBanh3(zVC?a!X6LR3G7a_ExlFy^8+%lc~$1!!uC4R!Eir~-4kr%5U863kEA z=LFVTEshG}on-e=KJ^xX-YoFn?oPBH&^ z-_@;{+&^JE%^fy$Vc&S=l7DfijxNM8T0J$B-C4Fy8EqHd82x>Ko&AOvtvUQduz_^w zqVu`I^cuwkYsRIM3&+D_HJNviYUxBA+on&|9EC;$cYVAsIHS)WY|O|AbM=CC(Uq%B zh)gM;=yg*B3&Q`o+vZ!L49yb6vClYtmh%D_Rulh~ZuP9<(kWIRdQVPvZB z#-CHQ5=y@oeKjskU?{wO|J(bEyJuSBHRp#Dx82Lp=M~`*mgSion9B}%TgwQnt)(L~ z6}FJN`nVJ0u$|$oo9P@;I*5F-oah9#afKF73NGpkGU^M#kz_x>3ow9=4EpTMu^=G} zYN+B|hs|{YxLJZG(>Xxug(AcmiURmY1XUyg6~u#I8oR0jqkc~8e~GXM=pa+llF^O4 zJZ>p_HXnF+B(cq;Mjs=W?9@W#70IJwoo+Iu8h|LnoqK-Aiy z8q(DXRR6ZEnbRs) zFbp6#i7+!E2^BY)$3_adFRt{oc{GZKAPGy?Q&1uRSrovA`8I)?yf1vX90>_@T zT`}Q8A>ugHC!#X=O93W!nRB)s$1-sWpRK8_jW%hVXEV zzLKpTip+e)o{Bu)ve<%i&ABe=-GYO%f6ZQ$hu%nM>L;G%P?6tw0ckn*DhFH$d?sduF7T+!kwAk`xG=fonK_OR#hFL%rHBf*B0bo!XZ9)T6 zeTN8>76Nyx)`76Zj;MLwv={46A!DJio8scyH zn4IUi93%fy$CMjS!!9&-auigTYEuc?1it3oimSkU7~40Msd`Ex>dsbT|= z;+(;zQ2_12`^T@*&G3AW$~;Aky(;r(tWZ43r@NdC(q zvYm@wz#%!@pZ2A*-Ssj;6uY-m9Te0Em&y}({U6PB6^2X6WPh8Zz6Hx0#L=lbZxpng zbiHBvLhx)iL5`Y)LR;EHxc3MxO~Vuu%s-H2>&gx> zBq{FcFB);i_^tKIZ1Sap2+B_lQD{;#gV(vgOXSs!ZPqQ`QO}<3)ycb1mvcSC%3l{y za_5_Yvr`m!O7ZUR8;spw7>jr(t0BsJzv@gP*F3NT0W!G6&il1S$%Q&HLgQeYb z`Ys>&`;bKpn@1VE5ZIA!P#6{7;7yCM!VyN8p=}1`01mIRRe^p;V21Osn6KO8LXWv&uFv8COkWhrLU%f`3v#2^BKb2SWU-a)yP1+GIyY*$)z8#Pz5Kkg@i*6boaI`m4J zX5(Jsoq;y*7eDn9eXIWnN6{y~ix^Xgm8*%|*kG*$gx z-v?EY{<1{v4kCrVe1ORjYxhs!O<2#VwSTvdC7(PZ4sE|Ed;P~R1-fb_TDL>02^(mX z0kms)Iy_U97)=@3VugV`5q|_}#`}=^-tb}HhY&I_-Q^M|R!BMUYC`O!SKK#A`W~aQ zd2n@|2}x89rAGId@j?j=KYgeRY)E8j*g&x$fwPf)w@h?{(4Gpm$4GQ($w=r>QQE(L z(&l}R{YJe10rRyU#KZHD-!YVl+dvqIUx=Z$*8$U2Q*Bbn$cqXcXv~4Y6XPC5WaNZw zC%2qn%mXTHjGMBZLi${vZ@3`xUBCFjMDkbE_eH;Q$7JB7zyk8s3vNL3s5am0($i;_ zxlYybhUcJ*OSRZ|aNnYKYxmQ*<8DA5W3&s^>j0_2frP}TbrN_1{{MEK*3M9?jx>={ z{>)~ILME}bE}5yrIT5{nMMfZ>5PWaS_&qeG#ac7Tu2VPpx!KIO;-zRH`r@u>V)hOD zZc#ob?7kC(2X|{R!XVunBt3~;!bzs|l`rKAQJa508k6kyqxsrhua5v#&VPU|FTe-( z+>yrhKZzNaSubM<0i4OxjE>a7Lb+9wie^M7trk3?CgwP;cp}{L#w_YR`u`?VF9_cw z$5jaUO0kIJ5_BH%zNkx)ee-1@WSP!FBQ2iI{716`MJ+GO{%<^B2BimtcN4yZ2+81P zXJ}zOd!{qIK>1&U@l5}}*Z)6(!@qC=dj$5(1$aw$c1?N@C&Z1hy;^_1&sL#o%ut>G z5#Qd@<_Oz_qm9|51CEP)A>;t*g}Zin&TX$H?H@fmJJkgIAvUK|Q}1#T>Q{R79b{jOTb=u!bNm!^R5oD5^GmP+ojsBJ!YGF|l0RQv~ z>*}!`b*SR@gXo(&T0zpsPJg<(N`u>5%c4N)nFY( zMXU!_D$(;%$EU>6Uv+_u(r-VdDa|O%*NMot^MqlCY^GQDcA0=K@2N-?Uc$JHw*eih z`6wSkW++QX#)(xxr@7Ue5b19p2H~G5=QVj^|6ZJ0zV4f!#!N)!v#h#vmqM&J*LkgA zRN18q%TJbwa}>d1jdTd@xygG7M1`iK85z!FgryCu<%F(PYGWN-qap#_HQu43O7T~Y z3}`G|NNys6uH4U^Fru&CVG3Y#ER$Jmqw_Z8Nf2nOQ9pIG=NR2kEiyy}&b$tBV5Yl7 zTd%|!c3TqaF_MjI;KZ`|qUt++ToV*O5E)55N>TrPDfBIFaQD%v_O0#P=Eo(5Ty#B4 z&?RNq^f@(8tiZHGs||7O<}!sjTjS&u*|osC@uhrpszkY&I~*4$XEKzsS17+M_@$-7 zOk6;VFK3ewq#ED3;jlt&5aabmqQ3exjPtAb#mefR9>mwsMs=c$XU}^kXN_us23p-s7Chut=^s z4r3JuO@o+#h%6{)N}=}I##Iq`jJ6+G-)gsNmLHL4?@ycQtefF{j}ooams;o?kav-B z8Qs4ZAirYgBMn|3@)Ru~7C!zlTMxby{kXVYaMHMKWSL~QL?5|c5sn0Ip2;Vh=3^8k zU&Rn@drUTc({_4A%=%@(Vwo~#p0swyRDHVZYtvl6s6^MmVtRu#&$H#Z@21&>yZ+}c zX{a01{_U1QB#y{;FM*AdE}IPJoJMq6+3Iq7z;hVJ<3sXTH(5GKyhzJ>dr*rjyQG{a zg7$Q7)@%aLvVlPg&pVSIwo&cS-=%LxW3~^Myo`p{jL{YCbfU|@j2wmr8Kj>1v|G0p z3Qi%Ky&gNS7J>)Us%q{nZWTy}^f#DN$eO!Eno}2)XrEHs-=uXHgwL#eL44mwh(KVQ zZYnC!@blI?Zt@4o_{rJZT9+^u31b)N>prsa2NY5pM<~BYhsP>$0IgB@(jl*JaNYoO zMaeD{iuAV&9cjSwRZb@#O?U7Q(QgwG`1VVM=n}hb(Kw%Cy!7DyJD93_;O0FR8G*kH zR_R8fmG8YkdTI?a+o5{1RN>YF z>st@{MB6g>fJ=5_b>eJAsH#y&e^qR|L>!5o2ylB4^!aaa2h(xK0d2j6jOoAO(W)6je3`6 z7>gF8{;xMvPj~lG3bVWS8qUz5^oH_GwY`i8QrU)E$ShxpWJXQA?egvRVyN*SrijoN zFMMO|xt%yZK9S)}_0Je71c!#CPaP@hA4=%)HP=O?|w_o>E^K$?yJHIPtpbkO~EF-=I{)Y8n$l!P;! zBc~G8O7j;2;jMh2ik*V{`Sk)ar*nK29pW!ssSsC8C&8DgP%={FUnr4!i}S%yGM(>` zdmx6Bh16poujf3%hj;KnQW3U3|_ zo4L^0Q{4WOx!cZd!;Opg?yW|;s`+OjRdX?+P-~0R%g2V)Qaq+V0gzFjK99IaT!_G{ zAqhgZ(v02qAY?vrg%T2$#_F)_6$u4pG{B5JBej&Wn>x^4mls$yfFrKop!|V(Jh%4Z zgX5nCZzmOIy)&K$Tu3w$nbgaNP;4-`8u5C#w9H)mu=ET!=lf~PhvAPr^g{AjnirYATZ=#DMmI7e3z<)40*W= zsy7KOHHR@pvPWh6Ziuj?qX4YHcS~;uKmYdE@P5>n7ZXGQI(!0uml{#**lqCj_Lt;j zx2;PfDkSW=fcdiK<{+9mToeK?@4`}e87Xr2fVS=QF${C#Us-T%pYA3<5D72)x8eh7 z86U|)X4)OZbE|~la|O1Cf|yGojK{*9rj7F5DhFCrtW<+ij5gNRW04Iz1>!UAtj??Z zZ{zJ0aCYJk__t>GsC;kd2b>qqhy*Zz_RyKVhirG4`B4EGksR@I+BqnSD1LBDW?4k9 z>$B^767BC9&U&)Wb=`B%7uk}xTb>!{GE{@)ZRo%lb2;PF8hJ{Kg944MLrmLc!myC9 z{fx^zsBNA0IF#p+BW#XP8pD`xgMnae#yJjppyfo<=5sTXsHz%^>k8PBDo6=4r~TER$XEDFq_1va7(5Hxo62~d5{6w zoS+VPXc`V(SROP7w_8Ft6Dl+E$>$^Bu#FFW^OPtj?BjV&)S8O?27k~tJ-cr`UKmLD z;%fQK0(h4*3|(bjKd!2Zlx26A?AB1}tj!gt#fw=UEH?Mrhutamga2(zi`{L;-;?5u zc;DT)I!0dyB|M~%*ch5Zj{DO;E>NHUmg!yseM4u8qJB5`37{a-)WCU}u_ojtKM;G# z2lBJbK`a+)Dcj7*h2zF`f1d|k+VqDNZ(a;tvFsmEQWtSCB=(dWsz~LcKVN|54o^}v z<`zGjknSE=VDuRd?|=JHAQn@}u1hH@H7V8oAJ&)S^fke=8?gEDPg4Gj(ek?bi;0Z_ z3+Wg6s?!ZRA-&wB-p}t9bZ87AS*b&VIK*R)$kVpVLV{Zo;*U-9<88Up|J=JeB>wqg zp4N%$u-jGmVkch;m_o+nQ48>&xK8U(MYpb(a6~YkT}|P?DMzX^RQAOvf?kT0vO@5@ zm--|SUJ&s@HE_Sd_AeP}F^B^-ll9eFQ5Y&Wj3_qbcFfQ2&poYOHNLg}QjQkQ_7&UU z&+T=m=m+EDqaDNi`6ZzKZ+u7gjRWJ7m)IR5bczvyvCXzNI%-VO1y+zPJSrO-O ziv>L0kxQ+|_}Bkp+~5U%qC90bQz^ypRG67HoZ_R|suhNYaj3o+^$+0^wu0)T@`bWz z%jfp1Cbi^KHEse&nnxyJy`jR{OnUNX0e+9s$F{0rTlKki z4QWx$Y!KITehM??k&zGe zZ2HNf(J6X7SfW*X`j?4o$;>R6I}d$Mz2)~!lFwD|8}k__s_zo?-?_h7mvhTswKMR( zRN}fkYJ1lv`umf;nxfwLpiHR-r~Ssr`DI0xai5#Jz+@Ey>Gr`nxls3~bLF{{@vyBNt?eHK9J*wT( zAHUD6Uz2MF9V^YlQ^Y*P}&|nYU~@PNDhsLyog3 z|MJ*73qg4HuVbD^GCwzHft=N=m%#AGywH?U^p?&Eo;&ui)B)K*S}*YHDf#OMi;EZg zx^kGq20#88(Edj14tq5*zM)<7H1_OwpTQMJHNnFP{$33?vj!51t{jLq?gynJslFFu zo9lQ))Yn7}3xbv(dU~t&*4qzlXgi~oryV|5J|lL&N_=wR+x<$u3rvhd$N)#EOPRge z09c3=ov>z#`I?wLO9MKab4&2hFk+}3H9H2KxJZTd68<^DkSE$#UDqd*o9P@;1~!Q5zBWmB(;(?vXOz9 zsjak`BnlBw92YW3`(~P7pTEM$QJP#)ls|1^az+1T^1&>a);+!SZ|MQ^Lv_Cotd*JG zxTKV>vOjo)Jz%5dLP6nVUU48tw`-3y*#eOHZE8}7*b8czvw7|THp+23e|=uA#nf&r z+CDJL$LOZW}}7wbCFwAV%{PGc-Bd49HH*CFaS zgfRLd^WgX~wMz#m+MXgJkX-=oV)GUC(q(?s6Mj*iSY7<5nOVm4js?Wa299sD7W7!m6n}z!YaZb~X9+*$KpruY7H~1{%!} z$#jH17NCc4GwPe_#_RuFQ*OApJ#y5A9hjLmN4%7lga6b1Rj#-?HppMZW$PAz&X}qC z|F7vUrviXPs3CVq#W~u`pGvQzRb7%*I~AWZp3N#qzHd= zsE=xP^VA!;iFfl6O}zFBBQ&p#$-GSWWcV=#Db4y}-_70vACMHd5`{rm>;|~&Q37~2 zy?f@>O;7*1^kQHFyP4w$-&x70uvzSbOqt4E37Mj)0uM$F0w{C@U{U*iz+9R?Wzp%> znW88@M5|jz&`1)e5&VPNI>2k?BEEVrXQi4zsjq?+X4%_y101?RpluMc<>nUYxF4wZ zhrjk0WFoNR2ZOZDT; zB#1RVQ|Tvo29t z1_yvm`vlb|l%G^2!{Vt{T}*xev~k*kB2gkTIcUs$oOJtxLHXA@y5ZaXv`#Cb3~%M* z=IV%pLbpC&i38fTq0YpW$f`ga(a?+aIT-_33mjB4nlg zPhW!;DoM&cs-dpMG5LSZy4q}g)_^zENFl$F0Y4-DbC$cq zv~5}u$LCZCHW$XlafEdrY^6f*YcHUGy(!p*^zbp%{H|am5&^;XZN2di z1>N*0kb(+5YWJ7YC;dmMvl^3BOW#mK65q6pOM^^jFJR*%U(OCen-@#-L3mMyWpK%d zjF*2cq<8+#7|LLlCF&^Es;HZR?2!#8+$|zfRlN($=UGo1Ht7W6uK(D1n@U-!G7HNk zW99|K9+34*8dA=@I%VS^kb zfy>TKGh^HwG^JA zsKZForl!RJWiE(SP4fAYT(&wR*&wTZIi?hX2X0w@L7`ZP7FIZQ2=*xdE) zU+KSI@mSF6W;Mm6ry~X(Pd1tu3Ld8Z;S9dnon%9iYeKSkY&zwoZ&WssGm)46p*Ots zxQ2oWvMihzTBNGfkZT%i<&obMlg`e2JW1pzmS?N9nJLNe)Ju)3$ukHA6wO$?u>58o zls>*l-UNos2`XX1{QrBoc7bG>XE&^cA$mu)HchWvrhk1*z!NR1*ce+(dT$gv?`J{q zZ#Im@2FIcm6g)5eI)(y4=~W2DmN&QScwTzE7fe`j<#vpRf^=ISM&1UC&Tp(tEFz@bHx?l;s}je=nC%$ZImdD=#d`E8v-h z@jJ$>VknfD^c?(IhptsnuxxbZ<>P6@Xq%aiCoy=yP=G`V#!j{YkSOEqD>{bz42me@ z#6^o1Empl@c6V>OemHe=p28Y?6P z1rDr6P5 z1slh_bba5vbhTIHrF;Cr+wWr1JxAepG3nyYv^NUjq}!noIq4jQOHMlOv9~rj&q)8u z*GCN*1v%-cy?dYP>!i0o*SQjtx}8*&&%c)_cc$c||A`A@2z)6ACq0^nrH3``Z+oU7 zqUkZ_l_M|xmCrtz&fDmhOG_J}swc^L={{P7f}He@kC{;D;H0Hqs4Q&Heb7T z_B%6|+#^LD*w9&hcX9;U=rGgk)*sv4(~ZYXmy$dsdR>hmZl`WOWi4o-U7$je?`ave~x#^^kKct~ta)s+pA zEh))`m{CwDl#V)XzANR7^qBm~Nz_JXopudY zjPq9I+(i`Rr2p{d-yi&_hKPNBCw)0G(#>h!xj@N(L-7HP74#9yxszVe`QE8t{fGpi zpqzB3qz_E}SVke7bV4DVbVA{glaBq1GwXNx&2T{|JaEz@isPod`heGV#yvyfnUg;C zt^)K(ymY}YPf>X0q_bJfe4l?5q42;-XVaWDRlYoH)d>psob)WdWL$=So)zvn>7e0P zPCB7*-$@S-nWtZlU4Bx8!UHEAOZ;06KC}jyODH^Z()n6(lHZh!LOAJ!LOAJ!LUhsv z3W3523gM&^3gM&^3gM&^3gM&^3gM&^3gM&^3gM&^3gM&^3gM&^3gM&^3gM&^3gM)S z+cAMcpb#ho3V}kP5GVu+)Wt!uOrT&WmI)LBMIRIaKoKBN*ig(BC>V;lmkw&}5hxgn zp4LHwb_9q*?HB|UhXX_f90m%o*wHgLU_jU0o>q|H)q3dwuU1CkAHD{{Y=xVb*8l(j M07*qoM6N<$g30|;@&Et; literal 0 HcmV?d00001 diff --git a/examples/location/places_list/doc/src/places_list.qdoc b/examples/location/places_list/doc/src/places_list.qdoc new file mode 100644 index 0000000..bc19d56 --- /dev/null +++ b/examples/location/places_list/doc/src/places_list.qdoc @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example places_list + \title Places List (QML) + \ingroup qtlocation-examples + + \brief The Places List example demonstrates how to search for and display a list of places using a \l ListView. + \image places_list.png + + \include examples-run.qdocinc + + The \c {Places List} example demonstrates how to search for a list of places + in a certain area and displays the result using a \l ListView. In this particular case, a search + for places associated with the term \c pizza is performed. + + \section1 Performing a Place Search + + To write a QML application that will show places in a list, we start by + making the following import declarations. + + \snippet places_list/places_list.qml Imports + + Instantiate a \l Plugin instance. The \l Plugin is effectively the backend + from where places are sourced from. Depending on the type of the plugin, + some mandatory parameters may be need to be filled in. The most likely type + of PluginParameter are some form of service access token which are documented + in the service plugin. As an example see the \l + {Mandatory Parameters} {HERE Plugin} documentation. In this snippet the \c osm + plugin is used which does not require any further parameter: + + \snippet places_list/places_list.qml Initialize Plugin + + Next we instantiate a \l PlaceSearchModel which we can use to specify + search parameters and perform a places search operation. For illustrative + purposes, \l {PlaceSearchModel::update} {update()} is invoked once + construction of the model is complete. Typically \l + {PlaceSearchModel::update} {update()} would be invoked in response to a + user action such as a button click. + + \snippet places_list/places_list.qml PlaceSearchModel + + Finally we instantiate a \l ListView to show the search results found by + the model. An inline delegate has been used and we have assumed that + every search result is of \l {Search Result Types} {type} \c + PlaceSearchesult. Consequently it is assumed that we always have access to + the \e place \l {PlaceSearchModel Roles} {role}, other search result types + may not have a \e place \l {PlaceSearchModel Roles} {role}. + + \snippet places_list/places_list.qml Places ListView +*/ diff --git a/examples/location/places_list/main.cpp b/examples/location/places_list/main.cpp new file mode 100644 index 0000000..8ae8d27 --- /dev/null +++ b/examples/location/places_list/main.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc,argv); + QQuickView view; + view.setSource(QUrl(QStringLiteral("qrc:///places_list.qml"))); + view.show(); + return app.exec(); +} diff --git a/examples/location/places_list/marker.png b/examples/location/places_list/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..2116dfdf51bfb8ac9556af035d598cf2c68c44e3 GIT binary patch literal 752 zcmVP001Be1^@s6=bY090008FNklP9F+g_dGL z7b4wOL~YbX6}NhC`Y^Yem;_BVH^=jjGv%gj-gDq^?<8~n@6OCQGh-NK7!h@__p?7@ zAHg%1%k{H&7`2$=I6)SB$ey9%mf^nW7pw@t0liA$uW3=@<{h$6c2acLUNScZ#1n1& zj{lW0thX!xPr(xr5KoZZd4{Yl+b9~?SDiKsYw4jl=ahsz49ascH$lOp{)gvA{a+7Rbz6(!?wYhMaEN_*&FvlTY+Qe#GeLd5Y`1f1;(W)TxhmI&f zGMmlT7dqEPnL5c-$rEwKC^iKt`y;L(O{LEFOX&6M`3tgeo|g$ok3Ur@d#&7y#A^h6 z?>^6`UFq7fPoGtQPnFzO*2?UlncYoA0W+aJ?(31K(tFo2L@iE#B&)%{`ZRITEx!AZ zU)hx!$Bv5w?bcmR&+YjDzHG#=UYFZb?pf8h$W$s-$07?*nr~~*-vC?M)D-<;udDUC zx(`a(n9X1Re>5i2b#_ic_8U43M<~(&X=?Gdk$OC$)?v`lX*}=K5dL}Zz8b-2L$~o) id-3sdrSb5U8~YFSpzHYmbwF4E0000 + + marker.png + places_list.qml + Marker.qml + + diff --git a/examples/location/places_map/doc/images/places_map.png b/examples/location/places_map/doc/images/places_map.png new file mode 100644 index 0000000000000000000000000000000000000000..4982df233d227d35680a3886cefa7e1a2416ef95 GIT binary patch literal 167995 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≤F)kLCqS(lL_LN_)Fjl%ZeL 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 +#include + +int main(int argc, char **argv) +{ + QGuiApplication app(argc,argv); + QQuickView view; + view.setSource(QUrl(QStringLiteral("qrc:///places_map.qml"))); + view.setWidth(360); + view.setHeight(640); + view.show(); + return app.exec(); +} diff --git a/examples/location/places_map/marker.png b/examples/location/places_map/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..2116dfdf51bfb8ac9556af035d598cf2c68c44e3 GIT binary patch literal 752 zcmVP001Be1^@s6=bY090008FNklP9F+g_dGL z7b4wOL~YbX6}NhC`Y^Yem;_BVH^=jjGv%gj-gDq^?<8~n@6OCQGh-NK7!h@__p?7@ zAHg%1%k{H&7`2$=I6)SB$ey9%mf^nW7pw@t0liA$uW3=@<{h$6c2acLUNScZ#1n1& zj{lW0thX!xPr(xr5KoZZd4{Yl+b9~?SDiKsYw4jl=ahsz49ascH$lOp{)gvA{a+7Rbz6(!?wYhMaEN_*&FvlTY+Qe#GeLd5Y`1f1;(W)TxhmI&f zGMmlT7dqEPnL5c-$rEwKC^iKt`y;L(O{LEFOX&6M`3tgeo|g$ok3Ur@d#&7y#A^h6 z?>^6`UFq7fPoGtQPnFzO*2?UlncYoA0W+aJ?(31K(tFo2L@iE#B&)%{`ZRITEx!AZ zU)hx!$Bv5w?bcmR&+YjDzHG#=UYFZb?pf8h$W$s-$07?*nr~~*-vC?M)D-<;udDUC zx(`a(n9X1Re>5i2b#_ic_8U43M<~(&X=?Gdk$OC$)?v`lX*}=K5dL}Zz8b-2L$~o) id-3sdrSb5U8~YFSpzHYmbwF4E0000 500) { + // 500m from last performed pizza search + lastSearchPosition = currentPosition + searchModel.searchArea = QtPositioning.circle(currentPosition) + searchModel.update() + } + } + } + //! [Current Location] + + //! [PlaceSearchModel] + property variant locationOslo: QtPositioning.coordinate( 59.93, 10.76) + + PlaceSearchModel { + id: searchModel + + plugin: myPlugin + + searchTerm: "Pizza" + searchArea: QtPositioning.circle(locationOslo) + + Component.onCompleted: update() + } + //! [PlaceSearchModel] + + //! [Places MapItemView] + Map { + id: map + anchors.fill: parent + plugin: myPlugin; + center: locationOslo + zoomLevel: 13 + + MapItemView { + model: searchModel + delegate: MapQuickItem { + coordinate: place.location.coordinate + + anchorPoint.x: image.width * 0.5 + anchorPoint.y: image.height + + sourceItem: Column { + Image { id: image; source: "marker.png" } + Text { text: title; font.bold: true } + } + } + } + } + //! [Places MapItemView] + + Connections { + target: searchModel + onStatusChanged: { + if (searchModel.status == PlaceSearchModel.Error) + console.log(searchModel.errorString()); + } + } +} diff --git a/examples/location/places_map/places_map.qrc b/examples/location/places_map/places_map.qrc new file mode 100644 index 0000000..2239529 --- /dev/null +++ b/examples/location/places_map/places_map.qrc @@ -0,0 +1,6 @@ + + + marker.png + places_map.qml + + diff --git a/examples/location/planespotter/Plane.qml b/examples/location/planespotter/Plane.qml new file mode 100644 index 0000000..167f08d --- /dev/null +++ b/examples/location/planespotter/Plane.qml @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtLocation 5.6 + +//! [PlaneMapQuick1] +// Plane.qml +MapQuickItem { + id: plane + property string pilotName; + property int bearing: 0; + + anchorPoint.x: image.width/2 + anchorPoint.y: image.height/2 + + sourceItem: Grid { + //... +//! [PlaneMapQuick1] + columns: 1 + Grid { + horizontalItemAlignment: Grid.AlignHCenter + Image { + id: image + rotation: bearing + source: "airplane.png" + } + Rectangle { + id: bubble + color: "lightblue" + border.width: 1 + width: text.width * 1.3 + height: text.height * 1.3 + radius: 5 + Text { + id: text + anchors.centerIn: parent + text: pilotName + } + } + } + + Rectangle { + id: message + color: "lightblue" + border.width: 1 + width: banner.width * 1.3 + height: banner.height * 1.3 + radius: 5 + opacity: 0 + Text { + id: banner + anchors.centerIn: parent + } + SequentialAnimation { + id: playMessage + running: false + NumberAnimation { target: message; + property: "opacity"; + to: 1.0; + duration: 200 + easing.type: Easing.InOutQuad + } + PauseAnimation { duration: 1000 } + NumberAnimation { target: message; + property: "opacity"; + to: 0.0; + duration: 200} + } + } + } + function showMessage(message) { + banner.text = message + playMessage.start() +//! [PlaneMapQuick2] + } +} +//! [PlaneMapQuick2] diff --git a/examples/location/planespotter/airplane.png b/examples/location/planespotter/airplane.png new file mode 100644 index 0000000000000000000000000000000000000000..080460dd623908ac19f6d6add6663ddc926fcaf1 GIT binary patch literal 831 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%0AI|#@b*v2#)$59lUh)^gy5Z-!wOR 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 literal 0 HcmV?d00001 diff --git a/examples/location/planespotter/doc/src/planespotter.qdoc b/examples/location/planespotter/doc/src/planespotter.qdoc new file mode 100644 index 0000000..574f2c7 --- /dev/null +++ b/examples/location/planespotter/doc/src/planespotter.qdoc @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example planespotter + \title Plane Spotter (QML) + \ingroup qtlocation-examples + + \brief The \c {Plane Spotter} example demonstrates the tight integration of + location and positioning data types into QML. + + \image planespotter.png + + The \c {Plane Spotter} example demonstrates how to integrate location and positioning + related C++ data types into QML and vice versa. This is useful when it is desirable to + run CPU intensive position calculations in native environments + but the results are supposed to be displayed using QML. + + The example shows a map of Europe and airplanes on two routes across Europe. + The first airplane commutes between Oslo and Berlin and the second airplane + commutes between London and Berlin. The position tracking of each airplane + is implemented in C++. The Oslo-Berlin plane is piloted in QML and the London-Berlin + plane is commanded by a C++ pilot. + + \include examples-run.qdocinc + + \section1 Overview + + + This example makes use of the \l Q_GADGET feature as part of its position controller + implementation. It permits \l {Cpp_value_integration_positioning}{direct integration} + of non-QObject based C++ value types into QML. + + The main purpose of the \c PlaneController class is to track the current + coordinates of the plane at a given time. It exposes the position + via its position property. + + \snippet planespotter/main.cpp PlaneController1 + \snippet planespotter/main.cpp PlaneController2 + + The example's \c main() function is responsible for the binding of the + \c PlaneController class instances into the QML context: + + \snippet planespotter/main.cpp PlaneControllerMain + + Similar to QObject derived classes, \l QGeoCoordinate can be integrated without + an additional QML wrapper. + + \section1 Steering the Planes + + As mentioned above, the primary purpose of \c PlaneController class is to track the current + positions of the two planes (Oslo-Berlin and London-Berlin) and advertise them as a property + to the QML layer. Its secondary purpose is to set and progress a plane along a given + flight path. In a sense it can act as a pilot. This is very much like + \l CoordinateAnimation which can animate the transition from one geo coordinate to another. + This example demonstrates how the \c {PlaneController}'s position property is modified + by C++ code using the PlaneController's own piloting abilities and by QML code using + \l CoordinateAnimation as pilot. The Oslo-Berlin plane is animated using QML code + and the London-Berlin plane is animated using C++ code. + + No matter which pilot is used, the results to the pilot's + actions are visible in C++ and QML and thus the example demonstrates unhindered and direct + exchange of position data through the C++/QML boundary. + + The visual representation of each \c Plane is done using + the \l MapQuickItem type which permits the embedding of arbitrary QtQuick items + into a map: + + \snippet planespotter/Plane.qml PlaneMapQuick1 + \snippet planespotter/Plane.qml PlaneMapQuick2 + + \section2 The C++ Pilot + + The C++ plane is steered by C++. The \c from and \c to property of the controller + class set the origin and destination which the pilot uses to calculate the + bearing for the plane: + + \snippet planespotter/main.cpp C++Pilot1 + + The pilot employs a \l QBasicTimer and \l {QTimerEvent}{QTimerEvents} to + constantly update the position. During each timer iteration + \c PlaneController::updatePosition() is called and a new position calculated. + + \snippet planespotter/main.cpp C++Pilot3 + + Once the new position is calculated, \c setPosition() is called and + the subsequent change notification of the property pushes the new position + to the QML layer. + + The C++ plane is started by clicking on the plane: + + \snippet planespotter/planespotter.qml CppPlane1 + \snippet planespotter/planespotter.qml CppPlane2 + + \l {azimuthTo}() calculates the bearing in degrees from one coordinate to another. + Note that the above code utilizes a QML animation to tie the rotation + and the position change into a single animation flow: + + \snippet planespotter/planespotter.qml CppPlane3 + + First, \l NumberAnimation rotates the plane into the correct direction + and once that is done the \c startFlight() function takes care of + starting the plane's position change. + + \snippet planespotter/main.cpp C++Pilot2 + + \section2 The QML Pilot + + The \l CoordinateAnimation type is used to control the flight from Oslo + to Berlin and vice versa. It replaces the above \l ScriptAction. + + \snippet planespotter/planespotter.qml QmlPlane1 + + The \l MouseArea of the QML plane implements the logic for the course setting + and starts the animation when required. + + \snippet planespotter/planespotter.qml QmlPlane2 + +*/ diff --git a/examples/location/planespotter/main.cpp b/examples/location/planespotter/main.cpp new file mode 100644 index 0000000..76f8cc2 --- /dev/null +++ b/examples/location/planespotter/main.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ANIMATION_DURATION 4000 + +//! [PlaneController1] +class PlaneController: public QObject +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate position READ position WRITE setPosition NOTIFY positionChanged) +//! [PlaneController1] + //! [C++Pilot1] + Q_PROPERTY(QGeoCoordinate from READ from WRITE setFrom NOTIFY fromChanged) + Q_PROPERTY(QGeoCoordinate to READ to WRITE setTo NOTIFY toChanged) + //! [C++Pilot1] + +public: + PlaneController() + { + easingCurve.setType(QEasingCurve::InOutQuad); + easingCurve.setPeriod(ANIMATION_DURATION); + } + + void setFrom(const QGeoCoordinate& from) + { + fromCoordinate = from; + } + + QGeoCoordinate from() const + { + return fromCoordinate; + } + + void setTo(const QGeoCoordinate& to) + { + toCoordinate = to; + } + + QGeoCoordinate to() const + { + return toCoordinate; + } + + void setPosition(const QGeoCoordinate &c) { + if (currentPosition == c) + return; + + currentPosition = c; + emit positionChanged(); + } + + QGeoCoordinate position() const + { + return currentPosition; + } + + Q_INVOKABLE bool isFlying() const { + return timer.isActive(); + } + +//! [C++Pilot2] +public slots: + void startFlight() + { + if (timer.isActive()) + return; + + startTime = QTime::currentTime(); + finishTime = startTime.addMSecs(ANIMATION_DURATION); + + timer.start(15, this); + emit departed(); + } +//! [C++Pilot2] + + void swapDestinations() { + if (currentPosition == toCoordinate) { + // swap destinations + toCoordinate = fromCoordinate; + fromCoordinate = currentPosition; + } + } + +signals: + void positionChanged(); + void arrived(); + void departed(); + void toChanged(); + void fromChanged(); + +protected: + void timerEvent(QTimerEvent *event) override + { + if (!event) + return; + + if (event->timerId() == timer.timerId()) + updatePosition(); + else + QObject::timerEvent(event); + } + +private: + //! [C++Pilot3] + void updatePosition() + { + // simple progress animation + qreal progress; + QTime current = QTime::currentTime(); + if (current >= finishTime) { + progress = 1.0; + timer.stop(); + } else { + progress = ((qreal)startTime.msecsTo(current) / ANIMATION_DURATION); + } + + setPosition(QWebMercator::coordinateInterpolation( + fromCoordinate, toCoordinate, easingCurve.valueForProgress(progress))); + + if (!timer.isActive()) + emit arrived(); + } + //! [C++Pilot3] + +private: + QGeoCoordinate currentPosition; + QGeoCoordinate fromCoordinate, toCoordinate; + QBasicTimer timer; + QTime startTime, finishTime; + QEasingCurve easingCurve; +//! [PlaneController2] + // ... +}; +//! [PlaneController2] + +//! [PlaneControllerMain] +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + + PlaneController oslo2berlin; + PlaneController berlin2london; + + QQmlApplicationEngine engine; + engine.rootContext()->setContextProperty("oslo2Berlin", &oslo2berlin); + engine.rootContext()->setContextProperty("berlin2London", &berlin2london); + engine.load(QUrl(QStringLiteral("qrc:/planespotter.qml"))); + + return app.exec(); +} +//! [PlaneControllerMain] + +#include "main.moc" diff --git a/examples/location/planespotter/planespotter.pro b/examples/location/planespotter/planespotter.pro new file mode 100644 index 0000000..d3be902 --- /dev/null +++ b/examples/location/planespotter/planespotter.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +TARGET = planespotter +QT += qml quick positioning positioning-private location + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/location/planespotter +INSTALLS += target diff --git a/examples/location/planespotter/planespotter.qml b/examples/location/planespotter/planespotter.qml new file mode 100644 index 0000000..b218d14 --- /dev/null +++ b/examples/location/planespotter/planespotter.qml @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtQuick.Window 2.2 +import QtPositioning 5.5 +import QtLocation 5.6 + +Window { + width: 700 + height: 500 + visible: true + + property variant topLeftEurope: QtPositioning.coordinate(60.5, 0.0) + property variant bottomRightEurope: QtPositioning.coordinate(51.0, 14.0) + property variant viewOfEurope: + QtPositioning.rectangle(topLeftEurope, bottomRightEurope) + + property variant berlin: QtPositioning.coordinate(52.5175, 13.384) + property variant oslo: QtPositioning.coordinate(59.9154, 10.7425) + property variant london: QtPositioning.coordinate(51.5, 0.1275) + + Map { + id: mapOfEurope + anchors.centerIn: parent; + anchors.fill: parent + plugin: Plugin { + name: "osm" // "mapboxgl", "esri", ... + } + + Plane { + id: qmlPlane + pilotName: "QML" + coordinate: oslo2Berlin.position + + SequentialAnimation { + id: qmlPlaneAnimation + property real rotationDirection : 0; + NumberAnimation { + target: qmlPlane; property: "bearing"; duration: 1000 + easing.type: Easing.InOutQuad + to: qmlPlaneAnimation.rotationDirection + } + //! [QmlPlane1] + CoordinateAnimation { + id: coordinateAnimation; duration: 5000 + target: oslo2Berlin; property: "position" + easing.type: Easing.InOutQuad + } + //! [QmlPlane1] + + onStopped: { + if (coordinateAnimation.to === berlin) + qmlPlane.showMessage(qsTr("Hello Berlin!")) + else if (coordinateAnimation.to === oslo) + qmlPlane.showMessage(qsTr("Hello Oslo!")) + } + onStarted: { + if (coordinateAnimation.from === oslo) + qmlPlane.showMessage(qsTr("See you Oslo!")) + else if (coordinateAnimation.from === berlin) + qmlPlane.showMessage(qsTr("See you Berlin!")) + } + } + + //! [QmlPlane2] + MouseArea { + anchors.fill: parent + onClicked: { + if (qmlPlaneAnimation.running) { + console.log("Plane still in the air."); + return; + } + + if (oslo2Berlin.position === berlin) { + coordinateAnimation.from = berlin; + coordinateAnimation.to = oslo; + } else if (oslo2Berlin.position === oslo) { + coordinateAnimation.from = oslo; + coordinateAnimation.to = berlin; + } + + qmlPlaneAnimation.rotationDirection = oslo2Berlin.position.azimuthTo(coordinateAnimation.to) + qmlPlaneAnimation.start() + } + } + //! [QmlPlane2] + Component.onCompleted: { + oslo2Berlin.position = oslo; + } + } + + //! [CppPlane1] + Plane { + id: cppPlane + pilotName: "C++" + coordinate: berlin2London.position + + MouseArea { + anchors.fill: parent + onClicked: { + if (cppPlaneAnimation.running || berlin2London.isFlying()) { + console.log("Plane still in the air."); + return; + } + + berlin2London.swapDestinations(); + cppPlaneAnimation.rotationDirection = berlin2London.position.azimuthTo(berlin2London.to) + cppPlaneAnimation.start(); + cppPlane.departed(); + } + } + //! [CppPlane1] + //! [CppPlane3] + SequentialAnimation { + id: cppPlaneAnimation + property real rotationDirection : 0; + NumberAnimation { + target: cppPlane; property: "bearing"; duration: 1000 + easing.type: Easing.InOutQuad + to: cppPlaneAnimation.rotationDirection + } + ScriptAction { script: berlin2London.startFlight() } + } + //! [CppPlane3] + + Component.onCompleted: { + berlin2London.position = berlin; + berlin2London.to = london; + berlin2London.from = berlin; + berlin2London.arrived.connect(arrived) + } + + function arrived(){ + if (berlin2London.to === berlin) + cppPlane.showMessage(qsTr("Hello Berlin!")) + else if (berlin2London.to === london) + cppPlane.showMessage(qsTr("Hello London!")) + } + + function departed(){ + if (berlin2London.from === berlin) + cppPlane.showMessage(qsTr("See you Berlin!")) + else if (berlin2London.from === london) + cppPlane.showMessage(qsTr("See you London!")) + } + //! [CppPlane2] + } + //! [CppPlane2] + + visibleRegion: viewOfEurope + } + + Rectangle { + id: infoBox + anchors.centerIn: parent + color: "white" + border.width: 1 + width: text.width * 1.3 + height: text.height * 1.3 + radius: 5 + Text { + id: text + anchors.centerIn: parent + text: qsTr("Hit the plane to start the flight!") + } + + Timer { + interval: 5000; running: true; repeat: false; + onTriggered: fadeOut.start() + } + + NumberAnimation { + id: fadeOut; target: infoBox; + property: "opacity"; + to: 0.0; + duration: 200 + easing.type: Easing.InOutQuad + } + } +} diff --git a/examples/location/planespotter/qml.qrc b/examples/location/planespotter/qml.qrc new file mode 100644 index 0000000..6903ec0 --- /dev/null +++ b/examples/location/planespotter/qml.qrc @@ -0,0 +1,7 @@ + + + planespotter.qml + Plane.qml + airplane.png + + diff --git a/examples/positioning/geoflickr/doc/images/qml-flickr-1.jpg b/examples/positioning/geoflickr/doc/images/qml-flickr-1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..42514ff06dc3549246d39e01719c202624419e58 GIT binary patch literal 66794 zcmV)bK&iipP)tnpW| z@?5|5U%>QPx$|Db_MooLOP}*nr}9;`^IygG*3!^4G&BGJ04yskFfcG18yiqiP%0`a zGBPqlLPHrD8COCJ+!2A|fIL9k*4)Fs!{X!SXJ%){!N=?B>Onp~ zYH4b1Y;7+tFmZ5ky|up!3JZ~rk$QQ1&C%D2iH*Uy!N|?jPEc22VQSag;*^t@Sy^AY zvb%VEnoCSlcXV{)=j^GZsJy(xuB@_CR9yJ?_`Si&wXU^(f}nDCl%k}lsHm=qgoe=6 z+iGofgNLKDw72Nx<=xocy1BevTxO=9r7S2XW@c`fnw)}yhVk(5fr6N?r>vl!pu@?~ zrmMAab9Zn6^7SMVOMWVNgy)IX5RGACQQv+TQEg)z_YysBUnJtFXM6 zlA=5_F}Ab7jf|b>>+g<}x1X7uKSE1~jjd`}Su{34(ag)kyt_?CLZGFvL`PA}#KL)I zXjV^0CMPh7dqz<`J4ii4zQfdWYf-+nv|?p@TSY`3Au5J)c`PqFBpVo#p~jt(j}{ms zaV{cGNG@DdHmskQa%ekaJ~)npZL^mnAqfhZgM*HHCO;}Dx2vQ|Txm2XB2h3fo{2N2 zm2RiE+J044er+43qP=!QKbwkGj%R2^OJR*VC9Rf(SR@_4sAIaMKe3Wb#*$`8;da|0cgvko?u8VcNpDnpBvqr} zX>zz-qT*Yp3%CbT=6s>IOb492;#YXk zg||Rw&$7dPktpZ0*Exk(c134$9d6%$NzuMHzpj^fZZ|Qzk}8M^6T>_r;J(K2I46GMP6_Qm3m1EC4mmLsGJ71bq^OSQCbWVp1K%ejw~5^7?t#p z_6|2|kqV-lKSe%7M|W*Ko8IxqRA@8g5`%w;C+?x)ct23tTw?oL@%wiN5_ai%vl5yz zo7asij087`(Z1VjEwO!l|L!D+WImr~f0sb|C2w4zlAKF!qS|kH?IpIar8ReDem6^h zt&rW_G_J4+rPPl!uf;yg+bp4jmT1(}wrhfF%kGxzJkKLkk-u7EL+!~>BTUG;#*R=v zKRm^=1&S`6v-hw@dkL{wqFFN&t$OV1E;^^$nmbbWJJO^#evVZM5Z40{1WXJB)5)%} zBSd}4(W!t-I;VZuW~P@Y1C36zM021Zc~$6a)#&-b#G$Y4x?8VN`+gkxp8%{tl&N3_ zFoQ^U<)<0@I@}71(K)g0j+VGMR*VggXoANa`q+^&_Fyo?3yX4l=HH8A}Cv(x!eHQW^ph7bl-Jc7H#ea&x&n2vdP zmE*+c%1ID^i#q6jTsL~tmgp4;JOEq8@0bnuOVg#Mu^m=^jSlv28s%x&%>oC7Vv z!$leLEP_Yd`@jz=-*eizS1n<*dypgW!@7p+_}vIv+izE#zKh--e|vA*v6)~7zloa} zg&xQVJKgQ=&P4P*&~S_}1gdu00I_p?;Av|Gvjoj|a>^L=5=IAsdX?pMJ>8(euoEh6 z{bv6Eb@u5c-Yl1=^NUqGq$dz7KvLX2Q3Ty?E7t$HgTA;nrrn@F(Cylz$IMT&`>03b`?CD-OqJ&|O}+M>S1dfIk7zL;NJLNQw`792M) z9+Rm%B{6R%o$NZD@eucb+7fGMi|uZKi6eO=7@C15NfO|GV3t@*+idrMO&;1^u~WCH zal=3ufKMWDjKm6)?u@DkhQOr3KNfZ_Fpy&vbcvj@l3ed*+wFdU{UarhEen4z7W0Ds z2VdEJUSHXLUTb9c`tXeXcHj5gX1#e|^k>y~b*%aSQvGyZs@`SW3ZEYz*U8R(+eAM= zYTzd6D%H?vn!rMaAv6O_7O#7M;maLy&iVd3uajN3@x%a@Ol5ClPSFW2V2+F@bhFFf z3N#{j7QD6wIG|;L2$0*ntdiX>S`Y$b#$`MeqmhPLtR0Wyzl=AsJhS%dLN3_-;kaHg zX15>`E)0&VWVaV0!_;-rsXAW{;GDAxiE2tD{#`^;8BniOF(pMrLtecHgBZP6bedh_ zG~)Z{xgu&kKJF;PB%Gy_VU`L2;nq{8${3ni8<{D^gPjYK6bcUWBSzz5@dH2zMr`~E z=K~o@G^>pWlOfz|_)Dm3?uedN$*wnKiMy^teq|QPnGI!etU=X0ypSpjv(%A94&4he z25~r|x88e9l(Wl8tU4E*e(W*)R>Pb9tmk<;qy}nmVC@{6nK$cpeS1SoAF{*f!7_jh z!9v6)3U)r0n_sk-QI6U$5Qa};*{vsP6iU4TTxTO}Do`tRRH=`ae4RZ5uQ_XfJD9L^ zSNp^Vi4zBg$$W8wk1R-W?ybfypx%uoMNC&hXzRt(y_;dYr@z1O$_nd}_z+ePaknDG z#;GMAevQ;eTs;I)QCR3WwLw|~143Z1d04I`r1+G}(sSgg%>|(InLTO{HnTx!|;X>KHyITSjHHo1Ba~IUL z#9s7$yD;F8V2o7LG;P86sR(=O5(nQd%0L|`z4A&0gjF@+rY2S0T?L|6a3Pqc`mKbq+KeT<(%aXIILHvBmhNwYS6XX?ba(z^=S-@ zA4sLSic&O!iOyW?4Nxwmv@F#*RGf}l`qI)Q6*nr#yw$i_S zcT++-CktBV!1lEav}G*So%^Oz)5d}g<#bSJ=d@k(5O=-PQoHYQy*uWA+S@OOy@tDe zzu9dun}*H94`J&gy2zU#PlBY`vJGmQI&UJX;*ljv_QHHy3d&sNeZW$KJlMuj5e9e) zi@GJQ)ocOe84JADO1wz-;z9>s$eGaqhONX_3Bzw-lGNj9Mji#6sZ*XimNXgo^galq zf@Pntc@3Py>U1({mbX;kV{=&YSLDf&9A!$?oW6-`JDJMhvYj(jU0_fOq=+X9$8{vghn3WF>UISKMS>3_7F%TC)s6oy}<*4Wq>T0$Yc%sA7@)QwZM zr3xUL9)cq!%8E@Cv7;=KMzugH4-hFEHeI!EP^k~se|*WM0dS$g5mDh?oRM>TL>wDuJz3tAh!qldk-0exyokV4OlX2+mn%$QRHUoTp(s( zOyFw3gq4;9gkw@CHXn5{55XYIs3Mwwd5jK(6vW7GAdC4rgVC zTHfg1THGC^aZAPd%iVpf&ak51h*c%6mbW*abG>|e$_{w1f89BX6Mc7ZCzwtmUA6au zX8)|!?pH48sr`{oWlEcRa0k4M+UR{?)q1yA`_R|!x3GWS?+4r1g5WKUvrB(;W+1gx zhDbAzdy@r9Arkm6gTOnD#IvME zR|k86b=N1uvjKhZ#&uh!wCGC53-&Ik;@yy;YgxDS9@Eo+POKUoqPN6=TAl&Qn99{L zJyT}_jS6@0mJccv8#7v*NVvnNy_J)vhsCoUqq0I!P25N+v^yUZ3#V1!&Jac3r$S^# zgx_4nWr8v7$`oWquoicLihv5kKsg6nD}%YLT+{5?HPCNb~|``)iYY2(4l-v)YNUFnl&amv$hG|_NnfH zZ5pRn;Hn@J%#fJ_Q9&3k4JQqEo=&7-3G&VgmMEsWV16X?MFsNCe9sIF3Q;gW3j(o% z6bmkMq3MgLFah(C5P75CTHJY|0-@u0QaRPF?4AO4;Ptk{JZ-Pe8*k~&V--FQCuanX zye5He(xe}eXX^oNRS${D6M~;6UA5`GdxiQpBw!QLuc^m{b4xw@m_{e|IW;~)THOY+ z@yRpu4~7y4N-Rk;ajKNi#0#vvXpt^v5+o2np09-LJ%q(%7=~&F=T_#)oq_s)C_avO zQH;k(MTl9rd877P+{HZP!1wKhX+QYzvRd3IsBQWxNfJFHE-HPxm6KYx%N~*E(Xsc4 zUQ|b)^bvut-hg(JMTB zBoqVMU7j6L%j(c`4;WyP;*Re-NarkMhpojO^Bo_hRMKjaM32L*YJPpWsY{ASNr5|` z`jayn$jJ^34`_5a9W|;nedYh8@)3Q(f2~h0X-7Yz=z_|nl7BQJdTHFX6-Phx=&cx1 z6OF(>`-opfmVTz{Db-!WkjgR?Qg8&s(@s)L+8l!qucZ>3k%);oIYv&Xd~-CR7G}0` z5i-hu&@SgCf7UJ<*0};;3?;cQlbk4#THv?(mrywX4?Yf_hbn8=muqn+{1o&B=Mz=Z zoB49Lx)nZ_j!5L(o*}K3sbNiC)6k-6x7+X9)R5wsp0w!t)vH&N0X;?DsY|N(KHPPw zu@}Le1folQY(pX3&(yL<(`ZanHKy_tstdzs3Y0H}Q0B;a7!^GNQgwd8qPsBs!xdM` zbd_=EGZy(icilqWyawEKSHQHx;mcoacu7xYF)}}s$pn%7;|gG(SFsss4yq-92jAe7 z*5=MZLCDex{nLGj%M!1;VV8U%m)zz0CPSKbdNfSN^u2$zi)^{=P+h{Elf?R*1~1_5 z86Cd6d3`jx>C%Zz-NDTU-3r;Dlk;QDqydtxGU|#8s$mT?91eTPM4>R(z(L7J;fwMf zaOX1f22b5Z+;pUqyLuS&ex{B|&hbS{?v~9-n3=?pwG!{_f^$b>{(p_HpEA`dhzx z|KjKOI=XY+k2hzl8|9}@Zhr9Io$3^h1vlKtBVhl_owHQZN19)Z@(FX@coST3aA)t= zUF)ue+6fbjkY1|BLa^jW3$E1N;oVtv7ul^gr6t0(h0>3qx235A=Zdb(ikK&1dg_5P1y&$g*80 zUMuqH&51YcCOCnn^mM3-j@J~TiNT5z60V}J@679>zpLJMaUBAu7Ut0+2#F?f1)(yk zutPLdapxR<45JMPahHla4B6ZH#fNw!0_xsg)psl|uik__VwmeeErQIw!X%6}LiTeH z!-524O|M|k)TV-&r-q4s`xPnUPw<~uM01~`U%o-!lN+&t)1JI3ZtIS%1R}W8*CZ%> z(wH*TAP2&hc5&~! zIN_vaS8S!~&49i*%(myAC7?R3OHngV0n4({$3UK@9kacjlvmK80N<|*?C;sQ3qaVR`s9`<)Yjj{e1imJ|; zEfOH7H1sNoShm|Of$Gj~5A05pWufkRb*b-V&W}EHl5?WvY;^)w=Z0?0Z$M{Wms|FH1dxF$xu8l#&Vo-bwL^|Aky7a55b zaun+}3uD~T-6%^54T+#fwwdy_Tmlt}9t;!@lMZr=S2%}kDc$W%RNBitQYD#)3c^I3sJ~f!Z6Jb?)Wv!E+*H) z{$b&@aZN$}8A~aNsJnW&i@^K`w_>Se)a!1v?(7g)LSb^31IQT*r0pSF#47RxgB9Jn z6WYRWf;(m|aS!E=Tsf95UaG0Z(F^NuV-EtUy!m`_whRGwio8*Rs^iX}K5c0twzv&K zP9fb%40o3b=zhBDl&JIfMCcwCUK_WXQ`nMcQmhJ$gS%L{zZJwNNNnrQP0a@~pOw*F ztCCWM6`zHOs`c{TvsgH@1baC-(Y8pR)ayaqSywKyz-M;nN0($`2N}_j(D}>7d~vl5 zsV|kb!}Ns?*0SrfWXdSFK#cvz(3xzn!)@I0>$yvN?yT`(zwjRPcTowJ0@o7*BH`Fj zC)K@;*g+n}ZgST)A1Ip)AgEjb?+{%5NH@AeuyS0qjts7(btfsQJH;K!-4H!&uFl`S zwA!n2PRv3=9(?-p@27Xw0 zZQQ9>(+rBmVhM|n_t|qN7Eq}>?9PA%`~4lW=fY^N4n=%Jm((2T)FmkhA!^WWAMV01 zMIGLq_IGLX(WN62ogDhatS2m+w?BY7uq(T&P9ZCjvc*Mg(7B4Rl8tC0pY5fBhr%;Y zq6u(2#1wa}ye96xI}3y?f$m9n&Pts#yAiwJ)YMtN`bxp?U@tpwN}w%S(f!x%i4fRF ztl`l#eC}?kmieLF)gg0wVRPR0eJ-wg6qKhEZOoYssD-n0XCevto|tlyXF;;$9!u;5 zL`!WdfPXD{$TBzfj0gMf=&}AzNU@}(@VL9tU@S38rMBmp)HEM{AbHTb12U>B@`O|z z5leB0-|q|sxDz%NI;6X4$Z||5wy55Oqsf6oa$0q)XZ^+5uI##Cj7Y*xC#P*eBBIP* zJrkv_Cz|3`b&tx6mHme}h;;>I2(<2|xUIXY&050qNcpSlE?EK@G>37brui5@0uPV5 z>ju1D2<|8lG9|^GC1eDvgqKZ$-QnETAs>31B&cH(u##9p49&=+83J2-5O=H9T)Diwnm@nz^Wyj4m+15s zOMLNod9hgN*S2R^@SkYB1Xz6zX4*WQ&o5?JxxllVv)Rkd?-$F>1-`C4-{8k$F~!AK zRPZY!XO*k7tE<^^K5&lvEwlRFo^!k&o4W@39r4ZPyp216NY^LI;(9(mTP`;9<<);s zxBBOwe_ns^$N3Wbu$cc|c57DRj)7-|m**GQDt7988vXO@a0 z=uAp9wPAv|X`&MYRFsm|_+rW?Xxx{YDjVrqNKuj0k3zB1EHpJ+Ox*<*WQ*>K8;DPu zcz)ATtG@Vxr!&(#Go3Sc?z#7OKjz+Bg~BWaBI44$<-m&R(?BpB@UO4j&`*6H2yDjP zUa!yR6GhQGM-Jbj_p^^bcAs;{=i=7`fp8$O5)cE7cwNg&VaB(-d~3a+9+?k7yn5Ei zaiS;)0s}Z44#~`$u}I2G)TxT1?Zsw?IRi;DOJ-xbk??T__vAPut5<7kDoYu2_Y;YBar-Q$|jtLj-m{C$3w|vEE%4lznaR%TwY*2jo~I)JmHPK$iCch$RWWm6&qD zXymlleN&Kt{Ex#9;f7g~U2sdBDAkn0@YHzQO6RC6OJh}0OU=tn}@bb$qBUoRtOMT7M zt*L?f+98W69T|G%6(`nLhK5G;2u1{Cl?w@oM`$rjT0TuKuT93VZFty_ZzA!=bqnK< z^_27ef630o@X4YW_h0gF)xFulpvb% z26LTZ$X3XSS#nQUvM`F0$d=YLYM9E}wX2sdKJjEBhOd-*8k^PWRF6wK9xp=7tT?5) zaS-v*{a>N5Uu|-)_`}KY53&95+TG3VTd~{Qv&x;#h3(zlc-$M0`+}iRFo;cP##>uE zFU%xE`(k`^HW}NFMw6S-XgnT3bdDwbiMld5W0!g9sXZhb6dMN6sc3k_Ue|O9At1UC zoe9oA_1gSyEEb#Fj2$Nv2Z=;rH<@?5?@#U@r;ZN}&YeSnF2hR}@?|6_h?Y?Y!EQ%b zH?_BSzaO@ zCkzRTJnX!v_P7!Cu+ZuXdabq*0GISzL`Fiw;LBpt57^hqAe#zEp=KDyt`p=U~KKUQCYHjX(a#n^rw5nwr2i)-^rK zD@^A)L={ftA`c)jf81DIe)lE!{Z;Rt@9_7)#=Yl(?@VSo$ z-)2)s$=G~qGnUsiI-1%}b&bwWZ66%(9~@t9E6nmjIw+UctyS0B+SA%@pDsh$0x7S; z^auv9R->;)NA=@nx@Wso_)CcHn!dtnWwWxN(qOkky;msdrNztTg(hQTW7A!oW0N+D z2d2rf>B$dwaMYo83>SzSHc`@^^A|8`^L-YvHiT3o)I z;U!|^q*z1`w2QEd9{2E&6Gvnd;F;6GH~@%$Jj4smdPI>Ct`+hOrzFcSerUG#I~tr; zo7HLqF1HcyDQQ=P5z2r<(RF+0iH?KYTiXX)qbdJE*YQ>=m5i+TCP!0UNfIv_J&G>O z%+7v@KXM2S4!-o!;QZot!0Qe9-gbw=k=X9`QAqQ_b~u$t#LA!iix-(3ltXeOE7#uM zu2S_(PADkdp-UO^sfzl1Or%G{{;Q?w$sT=USpz0B!o)cF>UNzjqu$Huq?gM(ng;#? z>xxKpbyvDZudM&_%gW;9IrEin9M7EZ?0lvZ80$|sg2{E-RS1Z zntLTUIyMIFb|aBcble8<@}S<)N2J|HAJx`+!@;ONKKSYG?xuhD#s@bx-SJP|rB9y! zi+D*cY7tNS?E4~?DS$D60>b@Ep zola{PomsFUzx$9<-rz?sJ@?%A-#_<~b~CaV>WcdIK|sTjr+W5?05(F}B8q^h>H=Op1}w9E2lwWGfioTWs3 z$uQaZ>=nIc@U`L5PD^bU^5C(t&aSS`XGWhHm9-m7J-8G2{tl#ja6f$b+HzoH)ql_J z5Bxq5SnT0$|!$|FnHs;919UF*HMRPjg?fmA6$ z3W5~We~|hvL7b{HiZmb`Z50_n&PUT|!UvaMEJGQF#wN=Xo>4_Km-pPsOk(HnDu4@B zXu_*+LPz)x%ysm{1!{{rM<09<;QIOQ-KC|wcky-iDsY!}&n&f|Cu)|r3B(;h9laT$ z*T|?p6*i=2!y}(ml+_U~8zq53b~g><8I+`|%HFf*8?I?Z%dpL2lLWaw>v2Nw^0O4a zr_>gmmX2%?%9zK;Tgw;98G%}{^qkRWG_s$2Yfgy=uOY5|3bBCz24j9ZfCzUeggxBBS z_!x!nDfKlNF-JRd zR=XVDtacN6ueXwOQKOm>Oj-+?)?5~mcTIRsFadZbB2unux!PrUiI^vdtX``ZMNXnS zJF@a|mJGy(O3r6Bpy+Hz60ChnEufauZ5U`b+Xf6cj0M@9wXu=H_rmFvRnpRtb}R9l zv13t-6wk2Y+1KmqF?bs*O<{qV6JYp?G0jp3%^aXC>`-RApcEBQn^psCTTx+g5emxq zSv|d$I~miTGrbCCTqu(JK7#Y#xR^nEO1kOlvog$U7$8d2&ajn2hh{Kxe zqGafQv46l2VWb*usn9!j-QKF}aaz;5VS#v!jTFA8RL;a$?ZQkQ;db)I+LA-CFc!AF zB%PcXtoSxlyFVjH)VM*rg4ry{UlQ4mcxy;6Ho-*(?+2E?Hi!T9b1} z(8-5c0>T1!5(QqL&yi8RCbF$MChf}8#z`>fKoVGnoegZ@?(HxHL3> z1D(vpKX!5FzsV+pL?aNdn1LR9`iz^!g=|)D$e6cWY1-JQF`Kx zIbcc63}hkkFy@jiFS7 zsDqar7O70ovj_$X6{LngdApk3)`=+mB!yxcCUo%B8N0LtxAh$(C%7`QWjSinl!R1> zP!Y9}hy+d-p*z5FWW38A? z_AVR!uyxr(i1XQfhUm|b2j&V`Y$fsCSD*j%)0Z2Q3H-cMvvX)RgdRf)D_?XIzx{bit{|zRB>DcQuo*s-TT-V*n@N(GQ zeHIIko_*}3u8Fw9=nA*V?3KiK_rLh*+b>`5)-WuWCpOLCLsA5qPwmY^8JfNtQ(kDs zzJrX3UtE+4R77e3aGo(z`b_%=eo2_=$J0`)v&9Ev{X) zcOEX`WgsHHda4a~btzX8-~IN??JqY6oe9FF^}dkI7St5TJbP}%m|+NI0Q4nxp=@kI z0fNZQ6Nxbewqwq5p2WaR1xrvCA+n%-wfs#kP8-KY5b+FB8k;or8ib5NRFG;ga|4o? zP5{YpkAyJNwp5(pG>L|Ks4C))QO7Xy&ISG_sON+xlPz|HP!`;sk63E}2JyIP!uh7$ zc<70cJ>Lqf9_;w1jC z!IT04_KHCbNKlTrki@c?kqE&o!2~m#QZWE|7XjuqHJa<>LrTz>^W?e5LZRLI~fjadnNJR2fzRMCMQt(0h7PbqjEF%8mkq ze%dw#*Ddb2d3yZxD{P9zdI_n99EvG!WvQ?rnS^{I5<-!rK;&`d3kleYdl5N*76jD- z_!Iau9(h<(FJJ`zH&A^xVaDCo2OqrEKB2`BL%ce;>9y$f1MKb+C+N*PqPx>K>~JOV z-FLrz{l~}UR=3gathanvgvE;hd-A9+CphsdTqTm%I?$5T**;|q^@bTi|8 zs(!~I@(t_-balregm~M6!?1fbVof~K@ti!xx=6=qwL1Z>yZ-s$-eAm%;j~uci2Q@R z)~yK#5nj7n*LFVYwHu0MX{{Esyxq<@S)mOuWmUO?u?8M|vj+h^Nv9#zA|jld%Nvlu z%V2+l$^~&Z+5YBNl?~q*41vMrCEolHiL}57#t80C@Mv=wCo75X-hb)IKaTHrJEK-@ zt>HM}3XKwigQ>{#1W8Ss<-(WpsOa6?0iX%k40Fqlqg?K}Uf@|U450Y98WaDfg;Cls z12rsQ(KVLW^*dT#YMow`k~B@POV>y*V0m0z>#m*%nu0Pt_WEhf&2X)rv)a=DxO-Y_ z-C-HhxTC3T2{qNSWhq8uWZA&6ibgmnmT1Qh5J0lIyoJC8B0wxaDcWgEto0F|P;Y(z zeecvzcY2BqjuOt2fC4sJOO_?cO5(e(KmGQPv%P)?cre^Vvp8H1=bUDJ7}Bcb9g$Tn z?t>f4LLNA{&38(H3LJqK)V^D`JURy*7*s0CB8X*JwR)>kDFt<1_H?GsbpJs&viH}| zv`W&>-eFkp-Rq=%4$AskkJg$rv9a`p+e$m-EZYgwff{SJR!ws6~`P5<1zUz?^`wZ~-(SHt<5eJ^^-zr?5JrjmVnf*kC*c zkMNrB%VZ_--S3#ITs$%A8BSlukmv=!EC zYik`^B&|pAbT!?*Nc-u8TyuI+X{PNC~WJg!f+w_9nA-Ufi| zGrh4s*4n{fouKvD_jh>lxzH$h#*gZJJGg2M$>V5~HJO+S+zArs*43jbP% zUFhDGKI2_I814u1+VNt3CW$Pxm@Kt(%a*9jBvO+5vB)tNM*~rC%M@ zQ}Etu=qoYM?Y4YHUt^jq#NWQZedqE7%09j%aQ7uUq>jZGzxx?^%UBFMi!An!=tb-S z49jqLM_rZ`8NHH)1!kw`8xOA}zPtVUmyh0fV$x~U*i(&CgzJf~JIqxEdus0t_t!B#BD+x%S$}JQa$78cx{8q< zWjM!bP~J4-oI~I0qC&(=GeBUq67{~ z9Of$GJ1i6X{%oT&Y5DTuyd2Pcgj>o}H0Hz5%m+6?oDzYc&pufZOt3_L(sh_9aaK}X z@%JK{P~t{|!-}F7-%Z&RAXpho^szF<5AN;m-t$znv%mGAyR);qe}AiV59{~E!Tmj4 z0L=mZ--gF`pL&8m=XP*^egE-&%vGLx@R0*$J+*(Ze_w6i!^dYHR)_a?A1--MZEQWb zp@g7q22mh_5S~L!xl@lmx3&_dFEikpS|$rXW>B~d9tTG?-~i*<=_!(v^ON&;VQD~S z-nzb=y>rs-9qW_&&c;gOyW2l+Z@)aiay>3LVhm;!)fO>pA-aJ2X$Oz!!hf-I#;#e! zU>N^PsuC+X42j1jqTy3RPdGjO5dA<0OBG#I5UP_n2rdflE-ubaZk-(Z4+!Gs=%}+U zcJg_XT<){y@m;Y!Ec7Sl?d7<9*F5*W$#b>BF_i*evntDlbH}VSIj>908P-LJj!g|g za&s@-Hot^^!@JN1{ut3rf7-5xwaethY(~Eb%=_7H$}G3k;_{51KD40XijH!pv)bkf z3|t49y3h<*RZq3o?kqMc&%pzqZ!_P(75Mh<)jPM|zkjqcL)MMq?>>M3`Qyil)!TQR zT#~}Dk|v#AKhQR@NuAmmr?}NrJDi1jTC>t+PV!vI5_1M?!Lm{Zr|az!rCpRSL0+&s z;e__aOVyrc%Mlivs?;9jI*ltm9H8mtF^@>)gs&cz&PY0ZzNRB|JFY9BK}r6umypKO zENJ%tSx0MLPz??K22e7$er^4_aOL_E7{T9t|F-e<$qD>gi{ncLGpe-C+A_SxlIb-e z4z1w=c`2ObRtd*6&y6gMleSh;SGCnGStg-8pzKXJZL~?p^P55xXY$B#LtZ8{dVwYo zpr70+iq$0z;wn@PMvqPj?KItv{o{tcN?p0gGzJhKPYO46a7dqJ$Cc#iFc{f%60ZsZ@xGs2w={j2665HRp4VDkcoe zo6Fa(ty~|&-yM!|&DX|LabJw~|Brp(?7xkVwyF1R>2RXvOXq0=JZa~xKaSz=PQ|#U zEI6YX(I{A_vYBE!zsE7+ToZ>}*MHytcs~GN)JM-UrtGAhxBfVSze`WaL=oqfYgMag zN8@e+^v6Ep^>qZtow#6JY6sJEUC#*Jks(pb_WP&-!?f_@hyC$nNY;(u@6tz+c1*a# zM@mEwqHlL6c1h>=A|_IQSL#mJ-}_Y^6f|egQ?Y+MPF;0Xtj)8-? zD6X~h)*r|4cgrzO;!`CK;-WZd=dC}E>hDbOGv**JivNwjJJfJ|SEx$osZ!5A8MhpM z&7mQT;qP`^I}@g&o#TBnt{Hg2Q06C)LG&;TPMuj7o>bwRQL#!YK=!p#}t%GBC1<4u%>kUU(xK zR*_=o6jX}r%FEs;2zw>o`aDU@DkAE|{PvvWG%L^Ge0WaIv-El9|9ivanSa(@O?ij5 zLFx)^H7-`AG!n>_qG6#{b3*ne@SWu;8qc5h={ju>*8U)sYQuHnhX=~mcrOS!VW_XrY^EQugs2fFwLvXsRfwjD;|~XMgXWlSHFP^ z)E5gDhULTa(ynU?EwGLzp+hylt2y#=sk>UKik)u+tJRzZxqmeexRXA8+W$Ht z+&C%FJnC%MzJ|bF>FmM?TJ4W- zK@F{2@v9xs34Z1P24B26?7#DT2iW!ZZ*@AuH*al&_R6U!AJ}Rps(K1nb>N9_YvHq4 z@JQR#Y}fZd$g-yeQFn+*=YAB~fH%M?X7vkfs)me;9(Nk34s@`u2w7Q za^y%UXY+#PjGao&Vw96KFL*HzNv^a=u4=or;n0qvB=WSxw!yTiC$^{36vjzpr-{-W z2cNb)aCMc;KV8n#B3J1T=Y_mn7RBY{u^*N*DVMX^>Z*{kiSKr9D86gh9q&I`;^{nJ za=Q%`q~iFn^X5x?qnGdp6}#st6|uFy6?UGDV`R5WT2_1r2zUG4Hv|mCwIP9F%na1(l@B)=T)4) zJ#e?S+lMS3U>Dzd)*4zpqt!aucBl|rTTdVTN#jac*2^r0cPfC1ia_PJm@S>@jH4&I z0uoY9Mj559-a|-TQGaIz?PBh@j(6OyN@S(^O3hP1#fU#%NpwBMxVe&7#NfOj?ZV(H zv0U0V79lC%olA3W`od`JGBB(wWlM0eFfk)P@xrK=N|mHirU|q}*@*=X3GAdQCV83` zOR0W1SK$NaN%4Sw3D{Ek!p1C}&sEXHcRM#1-{D>b^#YVI0chLEsY~w{okL(pNbk*C zyLGX&7>c%T-bR1B)zLAb{v}v4-|NP7>$tPqEt7Me9|4r@!;NYx#t7_aT!Xu&IB_^3 z`eYqy){Cf1I5h--jI_Yk9fD5jx>feS#i(ee7;E?FL(E79k4wE2*r}D2h)jf5mU%0H zri6%6fJy~X0`O7a@fiGuP*5pJ;WA1iSBSzRQjI%5oSIR)iQ=fct zIywFByYD97;iYj>UOoEbQfylo|u@sY!9@R6vOz(N}jyIEJ)D0)Y2*TLEZF{YenW2aXZDR8o$ zla0F>jM5ZJvnt63WImr4MUl7jJU8%r>`=%v5Bhb@ z{fzDBYu)kl4aRpn*YVwRpPyii5O-mx*m)M6?A6YmwfPpDMt!IiRF@CK?0fzOY+trJ zeG1#cVb{{h{o60Mdk(Pcjy8`yXY+N+4z~?PD|VIFVTVEe2F4i=LP~lS$=WGpi;5PF z2vL*qBnuW079@!D$Obx@r&9*1?V2$pBzM94Jn*a3aZ+W;0&aO%SFnuvR~-J0D5g-7tK`T)HiL#xKK8qXbn=*K&Ltn}HB zAO118!T4_HI=*{uA2*tJ$LL#~?blvCeEW^g)4Ra#^Umx0@4VdE$Ep{W)qm&p+sB<- z&VJ|i@$0YeKD&AF>i*uz^Y64+AN|gTe1KOs510n|R`paMF=&3ARQ#$AuVA4sf-E~K zRl&6aV=xdA;R5_F39RWfsD73VysqVW>r?FXVe>K^Id&wO75biX9DD5Q*4*)Y$F_80 zdDBQl4v%cE2L@)90kyU+3@>07rnwY3XpraJZR}R;PHS3ts9)u!>QwWbt+`i25b$Ad zbc6BTP7~k3nu&Xr7m!eSOWWMnx1p%|Vb|X7bZ(z?UV`P(q1@cx>}+m>lXW(~=xoN% zcSg@{Zf}2n59i%|U}tq9ll7Rg?)E;xw6Begy>Tg*IGT^lP!&6Ks!#nAWpl^t4=F5b zR3qy2C}Qq(dS=$_h-Sr}jWhg*-Le?YC>B)s)Tb;IzU6zNl6DG1Wk*EGMPP@5&!Lfa zyId_>EtYfLWphjcDhL=urnIq(hS%M$9Vp-Nx9zY!u7g( z1QtnS7w&9vY+-_n@*%V^rLIe1>ya(+k2evO{&aDshun7uJ{$N0?zd^!3WO0b?sKEv zBAgEhBXDT!qT!O$->Jch?7sc^gP*b8fvx8EXWd|Yx6{OTaKD3pa&vM|{6`U+su~Qo zvZ`Jg37ed99psgjNo*`^fqyKak(CO9H+vB<6wX976_CO@#(h^3`K})K$7|{Z_H^p@ zuWyDRLfYJD8QgWrfVT$}uv9B->id>%MeBCqnC(y)?SY|jW2?LntmU1jPp+&KdP2(I_`KCuQU7Q!&O}GsHu#<~X>Q zuGKw*6j|}*v{!o}woDn2d&xD*jIF`odzI_F+irgBz{J*e6zpKgp;dS_0 zhj)YV-OhD<_nhWZ${vDVW^>T$)=e9g$nf7$(mJUlEafYuLS{$8mTTe$DaG%V*db(4PSC)di#mZpkony+M9 z3oIDCnhBVS1H_d8Cd!GOBFM_^raxT_*x>uKqq8-3GFvRBmbL|ZijTfOJ%C-`M;G6e zYp%~U-;37mc26N^Y52>p)2`$Bea*K*&vP^{w57lm-Eq5-9Z)w+6X|D)=|bpCctq(O zf`!P=Q*Uqg0BR)@N@!!3gJFzGuwlm+2+z%1Jl1~q0f56@=3fJaTK`jg_wlEo^NL53 zCJi*~rSvn)pl144sg2O2Q7W&sJ{4KkOH8h~8!(RSfMq`$Pk;LU;+yuxPd}aY0bb*G z-eNjkv|vZrx)@GI#>T~T6npp+Mg%kcKiP!kF&k)F*<&@^EP&@}gs4hnd{+9_u2m!-@OZZ{}EV+dRV=%@99<5sYuGmljxM+_kO)F+%;xieGajTeS3|a;wUV7 zanC&mLx(ZJLb_PkG%5WK+xI_BFD}{_)9FuV$gY;x8cY`paJ#LKrjtqhL@=BjjdXC! z6eod)>vnzdJ8x(9+c*`4@jrtXqLvWLR)XVQ*a){Ikbqqe?sPJ;Jw*%%y98xeB9LIH zpcu@ssVpUt%2q&B8HiefQkDoImJvK4RCoa-BtS@rO@P?n`Hs_su)X&4bvQhCxtA~WT2|KM(D7g4idHpjY&8@ulpCVThQ%zea8g-i*0vhih<i#WcZayEhkmvAIU5K7TEn=<15|%=XZqefwSRWknFf}y8Lzpvx8eb zRJ+==27`Xz?ibN{I4N)yF&`=;q|m5f+>2^L(}q=Xv>;K%bzww|N2dirxrPpWf(-5jZFnHX;54BRj$m8?ULb_{w5JEEhE45Z+-vs zSYHkG^&_WW@Wtn!fA;xb+Nb>PZqf@WJNn&Lar8TFl3|JU!k|y=fNn%D+;ct!2841r z5~3IS5|66l7}M=`8Lx+4l-YPJw3%o$2eCY8b)rfH#W*F@DjH1NY!8;fE{^;pNMd@4 z%R&02-7xW27b$2$a$q=^s{BbsaT!r#ii~2_MGTvURfQEK?i7V9q5^SBu^Kff?8-Ng za2i=RIBFq!=Eio(@Y^q?2MUDT2+eTs?SJe0U;m~*e*NS31216A$I1Z-C~pkE`#JuoSn5C zu^I0csys8xQRZ!BvuarhIV)>Rn`)!L(pqPn!6>r;iq^84Z*F;IOkAit@G4Fj+a<#< zO)6OAJ*Hazf4>{M-p@Zjw|(0!*M9dEx#4Gf(x>(%znf&oJA7esm5XM1r@847yzU6w zq;%A)N3G>Zi1E|9&}L>9E3}HbnwPqwh+z_Wug&cD2ZRqdt=4dK1!sXazXLk3TU>Sr zU?=mDW6?!YqdjHxNxPv_>$D`w&e&pBR^r`VkCQ}8#YbVKa=XG*QiFSp``h z*N_zC0*c6loD_x{5+9A$_CYQ*wkyCe{=4ak;&!n9zNIk=9t^ceC6#yjx2KQwEa|Ch zu6yRT!>3=d{p@_{iu>-Hzwf^LW@h#yzZWnFYHd$)J0q%!He3?Ot!tN8qx0 zA?O{w^U^4UfXwh~Vom_Lk*b2Ak9x_*JQxtdK_?z65^Yl_ znf73H+4uk{g;L-xp5C9ChH{g3!(F#kPQyckugbd+BWc`hPt`<;sfrvMFl)Sc(&V~o zkuuhFZ4N<0=)rArj2Py3GyxyAhERnY+qGeM6{3x*aFd1qCpCdlj$zla)nhp6B^2B# zSN~n>ORpY3`usJIKlsr4Uz*bQCBJ)W%+9%Jfj0`q9y?M|mFku!VAOaJV$rX|VR(ND zS`49Vc;Wnln#+UTSfi?-I>umo;&O=LydotaBsmT-4iiU?-0ioFRx2K=!q{diTWBo@ z-F`7ngdL0H;dkP6s!6-q-HIVno0(@=H3B-LD@k6p(3Pr!#LO0rh^z9RAV?=SsUI|~ za-kw%2~be1Hn;|>cGK`=tzTfcv0WIZ*ptpx+-do~pDYRugPvNY7gD8n;?-9_oobKW zp`{yM>a<#~tbM#bmvUQzGUk5dcemb6Iy+si7wn2^F*LArv{7s>UVi0?0@GYAwBc^u zXaS2vg&|{{@0RXN1Iq}zLn=7|?nW+Gt!`Dfw0!y{wuPyw#k4C~^fYfr9A)%Q-0)N` zMEluB$I6IZ-SuqKw)<_{Zr6gEkWu1#Zo{b?c*(|ASYS74SB@RF%y1sU43*!-KxC#x26lu!|^sxGpwD2f78ZpiM2-Nt9NTfWAP?P@R#NGRlI zz9xD27HO4)9ulxs68$;J4)ox4hc0{ZrPjt-FTD2U`UOijFAa+z>_>hFc9$bZ2raOC zc7ZLbR{b`()^$a<1l_iCXX*B~W%bJ3z0UkXqoGvm$4f^WP3Ihrr>id8TdlJN*wf%E2d&o+((3Oq)*t zyQv?+#*;^mOxi{M%!`r)c7pTD?kDK3%kK=H@1R*!wb%l=E?&YF&eY>z8OcWCV~ws$_0BuNVjPd{l3|eBbFZ3^&-dk;h(vIC5e3hW1$KmN{m|8i4y~uU z?8W2nzj4Q>Z~nIa;LS@nLswjOnIP;_eg_dd1Ab>%1?$Svg)=vnj>@Hbr*52g{pXG$ z6Vhz%9D&${CrYI|^Kdiu^7+D*hrzB3z14jym8Shw6;A%V@1w5H@S*Bmur;Im+XLDj&1-lVPA*0l}zM5@>9f3=c)Lf~uC^iHv5H zKK{qSfgIQkMfjE;+&bg@Pk;Gk<>7k{9|Sy-rtL?5cVP8_6A#>dwngl)lx(43-BhYp z{nF)f>Bj1bd0#kdN|nl@Zgaiu=epgVTe*Du9?%^a#oa} zyhalpjs-1u5&p$nWwodFXq7&s)tYcp+YFLD)oJJ0*`_Qu>V}&H{<1GeAq^^y?ULa= zCqq2xI*g|om2~%`W!R>t>2{CZNJDb#>nqQ$efsVrmppON%JtWyy}WFR*zHGtci^X2 z9(&+{7aEpP1Urn_6z(m#>e14LeChVWiTPT0mP4o(woUES_H)3qvGG3LpXzLr7BuE9 zI312uX#{6OXP*sc>r-PII&NZ6&vl*D@G#Gg@GGi-Hks9?K~}r zD5mI|Lv}NhcB3e4Fa4qiKUzKY4od}#sOv0qs$-Etg@0E1oc_`G}k@Rf|nHQzFA zucQ4BH!qr7R=}e^mu*eSj{-MiaRLW)>4Vf+5%fTFo}T z=^RVsh+mTQbQ`1d_1PxoGMBUHVW~IM2DPbA6T8f$UARj*Wrtv8YqK8sQH`cV)zB-p z)8FwB*g}GU+>N-A6oErX(%#we1XGLvG>Ba%@RKMS+tpKcDY<}mE4(5I)!N_KEsjz= z4(M>PBezR6cj)Om`&T{u=;68NZ@=|{n^%?&LZ-eS`Q3q2-$1W*dsdb$kuKjjUfSLc zm?68`@$E|`k5RgLw3!P%_7>!#w{h&kMJ`r9c;=$4DLL|AvdvUf6@I_}-qcB^YRFN- zgWR4Z)07Z<0->giBA6(0JriwaquTr=mc&9v3IUZABE2KGo3tBM>KOc%yu74=T^#T= z*IEo@KCelG)}Zfs?LI-mC&URr^J?V&<*|`x0xw9!gyEb)x&o`Ik)oaJ*USP%t1>_dVW9hy8|Fc>cgpOO&jP29k8i6y6pRK z)P(iYA*Qii9fpIU49u-6OSYiYh!#Y56B@F+l?nuQb9W7{dhpRJZk~Jf)q74{JU6!v zdYZH!`Q3pJKm73Rw_od)ktC$+WR4apHRvdNmN76r&hB?&9*$Mi?7Zxd(j~WwvwYUA zxMSIZ4&`!G$7f%(7_o!6DE84xM(#9lF)`jr#mY_3 z9GS^9C+#A|&+FLZIcE!NXfrEptKc@3Y@twb5K{WBPJ#;V>87a>Ip8yJeSOD8yczjH zU#mkyuufO=wi;q8fxb^xo*B_cY zxU{lzkYV;CzdMj_LOx@Z3l>WqDdo|+qI8!P1!d4T@x5(-Fd)~Gkhl;nd^drXmw6}s zwZce(@K;q}sgg_{O*64`!zPJuvX&vh^XgUI))~MdMHRG~juX}VuZzXj^OUA|D0rTE8a7aS-?p;zHj~mL2`%$xU<|4>zvuw0zp! zG^#CEHu^>uKw}!Gh~u$cGEA&|L_YluT@&)vMnLlEOpj#m6v^2@ZKS8?ZoTN*kDh$! zv=?t1Y^<*zJalknWrbn(BfmRv)9naQGR1DXP?S|xKrNLFh$`1E5BeK|*6ynxz~5xu z%;lsSHIjS468g2mBs6GCn*fAO=anK@G5*nNgaPc2Au;_4ds4)Di5leq?Qn@9~R{9BKp0h=ZC8$7V4D zFmTMZlh)haF2-zn2B<}b8#+rZW^S~=-!Nuc@EQ(wiCs4?XzEjHXv<5N{mVuKkl5+Tm4t^` ztTkUWo`ml$6(zBe7GoS;#v^c~MF!}ai)*8HX`g1(popx7KG>7Hq8+Ixq=Q6mWfp66 z8zo$fZ8Z43y}e-DO5XI&t{f}TW*)fm1KZo_lE$$N=O!8HV`ylXA$I;|!_HQKY_s8r z(iqhPr!NGIF>xP|gYdlZ!fh{dyC1%A{S8+aCPD7XD`nwK^1JJ(rgJiZZHmD}MQ|KN zp-`yMAzf@I1=m+sa1U&SmtqVTE-ChiMlo6zJ8X)Glz2*0V`fI(>e93s?b@lN*KV>j z?Uj{^+fGD=?3S>!CF;bZ_m$d=#zDj7OFG!$-KbqU|C>#agFg6R5vwJf(zul@=4|Xk ztDrx#?P^&7yNG28rP)SWTC=GDC1PtrsVKe~cv_ia{m?E4!vnc0*r{qp7Ua2qf~))N zrcX8L3x^x8dn|5v2Ved8=T|eEH<0HE$ur6C{Q9zv9EoCoYS2kqA`fgpx2x=m!M5vq z?x|#(*Whd*M#2{qrq~HiiF}3rOr;d zFWpT?`ITyd)iDgw(xf$91-1+fGv&omyVR8XteSC+OlE(7QAj9JQI{~yjIFY^oWv-q ze&1SoZ||v!rKjPDioOyNOK~TGu8Qw=sdLj9iCLqrY-pE-VT@kj)Sl;U;jA#j&2iLC zJ?R%3c+ia`Or8pPxqrOjx~pe7s~I@@B0dA8$Kp{-$CvEy_!kBK*#0XW`?4+<;*O^vdUW`^l_rfQnW4(p@M$@7O~R@O+js(F-_Gn)UBo#VpXX}I>207 zryFbc`>qw?`vW-+!+4Iui$$ldxU9w!yHgQ6!|RXQ1w4Bnl`IrJ19n^b;qJB(GNkdv z1{_Cer=68xyIy^Y*jZF{vdY9G; z(GopyKk+++%ptv}@Ba!XLvr`4Z@&2qZ?6PAIesSj9sR!l4Chp6N5AYimbkqOa924_nY#p9aeFA1QOmCM_U0? zij^r>N!X2|U2boK-ClH@$x%6bEsiQHT}!E##Btxx7)Ixsiw$UKm)Ma?RX39fsTrIW zIA$ztDP5`p*b*Iq4txd7VU7r4^U^oHaY;M)@yBo8e1G!FyRU?b#WTt8sH)9ONI;P^ z><+=8au+`L<@ewI{Ohm3{`~!yAAj8Mw-X-b3f;tbr6Muoa$1c3PZ0s1T@#Is(Q);> zX=dRUz2l78RF8X*>pyK7z`}7Bu_F;X3|?1+(o%I6t6ZCgHr}V8Iqgf*Uu_aUs5 z#hMo1({|3G#W+QuB^5K-YigV^3@MdmSV0^iLEPHR^N1^%Z+9$>t*7 zk=yav@qDM>3!rVA?NPfF+0<85rct+u-Im*Sy)82nGu8JFBc82w>sX=QiSNCltl1L6 zBU_B<-9%U88?MWXR+9xfjcdL_?1py9FmzwZuIBP4DX%H<5Lc?&^y!cu9^`laZIIhx ztYWhdRt@{&b@6zPs@CFD{zLb>r=L zF&B?cEG#slB0r)bI`TUTaZoB%>bQAc%!1v`bDd5x-08Xf{zq?A4Nbvv+vVCs2isq< z*{ZWqvxpsYXrnhBk2;#?Z9{N79xJnrKqwSwkJ_b8o~E41Mw1zYHx(ZSOvJZPCX@YQ zSGlhuR;4g^_=@(_qPS>joc_g{;zS%TQB_#Z<>MHau$;z%9R?r1-JxM5t9VoeI3Cz# zdD>E6_df(aJWl+M=2a+eVo4FPy6TaI7o0cyWaOhpPhUr`5pP)trvk^u^88J zLHw-?7ry)HZ+4#oUe94&-ExFTCDNLL{<|I%q;Vm?Edr^O9hA*htD5idZxrS#!MGr| zqn+hKBd{@*PK!6UssaKp)t_Fi=|IP^Ue_{MO=4cTsVoje-&zzj6Qg$F2+FZ*=9`%; zbxj!%%$u2f)==|X2e=HoeZWa-xXkUn9!=sRuY0(-Eh=KPBpzT?5-gIJap>dXb!cce z1jF~?Mx^)wyF5C`svHGPh`#h+5j+2qtG^x$0do3|jVm_a{ltCzMumT5dJ{;=_u}%I zpKbB8|ip$xA!^N{r3Cs zzyE~W@4x-_>4ghl_l}OX^rWOodVOZ2B!wd4>Z_MscAJe0?flJ!bNmbys$R#_gUo|S zIc^Osx906&y4YH1@$K4bVF9H*qry}xxLl|Qsj9WIP*a*ycrjCtJn|aDPT*f{Bn`=8 zq$FoVl%ecI2#Rw|fTXHpWAqC)V#oD;17o4c7CmRwyeedNJmcu-haFR8)#mHWUfB|D zT5A^N+)nh+r6G!ij*hZ~02{c8e%WGZHw42cQc5h0V~j}gvYO-VQ^z||cyc%=hTElg zO-?`a@Usg~PF}vPJ^m7nR0v)!cqaKBvAcOomcz1O>FtC5mjiYrVn;F4JAa@Xn&X*j z_Rg89joI*g;Z}CyvImLXTwx(yE3o6ysX~H{E478O`P=x$MpQv$Y|XbGohyh_=i;}{ z$o$kDiRGzM?zSmO%B@e?R$;@KFWe>E+EVq}t$DsSZ%EkB@35XSq6o*Ts!VMR=OA{< zeei^;l+UI@7^e}K={v5t>hjBPpFMd0 zmaEROvoCE=P!voTC3UX zm(2wum#zNIE*NhVZWR)t`P%C0>TR=Q*IbNfINO?=Z>`p}*17D1Ev%@x!&xra#nzM% zE@Kf>uCS39FVu)#u7!V9JJ)OqN!>7OQDgpYVj5G_Ik1Zbz)nOX=Tl>; zRLaWdjl)A1gU)y3 z{Fz3^ah$NDSJn*E`l}hvlJq8D` zqpt4ZhbJc=zIx-~$<67_+Z)ys4^Q8&mqCv524Z(6`5mh8J+i$Mj)9!V1dNlI`IEmv z>G$70Ma(=tItm~C;09Bvi(-$?Pu$U}#%87#HZGe(jsN9!-|tYVvQ~I-{apCA)@*$t z^-!y!i2QotW?{J{0~)_jxO2U*A+FEdI=fm}K3B(AV&S%PcNG@OkGAGA8?Bq|3BE^K3&<;U)i75~?i^8$L(|ZdhZ{b-X0-$j?ULc)pAF$$XiBo;%Pz<4)1rDLVz+tu z4Y%C&_+Px8+iSyA9LN8XlB7va(l%+Dv`LdJO=IJ#iSsf=rxC@=l*#Zi^+5%l8b!nx z5f|J`6%>Ra-X9bTG7%I9^~G031Q8vGe}dm%(wmp(e$yst)Be_-Pk+C2ekbWUFFpG3 z12<8GeCg&roYcGJR`<252_8xDHj>|AKJGhTFQkymYy9No{OgYZZ^7;-xEZ4te)dMCoYfkBr(A)Zmj^@r#z&a^2xd?@8+3#bU5~oS`#ipla$NgNuen65+w;@ zIH06^dui8);e&$;)>+{QQ4xG%9Y8?oDhJrD=q>&3(fio`b8n&d>)O+6?({x+;iYSC zx%1X1uUSZGZ6v=#Z{}5JQCKqugYfKwv$L;0s@VPf^It#yRHD25n&-!DudPNkm+`E@^&7qxZy`Ht`p$HkC+)be)C0-w2g*3=r)KPm zrV+@>6;F#{-15?h_q97>zrh%D1Z!~lfs&Bu2b}GWIv#a;io~yqxb(Xq5^!Z1??ka1 z`8_q871O*xy(hfe2fbSK;pcohKG;{igL*!45K$vv21UVQqRyY9Lg@#OhCUw-wl>viL@S4+C!eSP7 z@ibYjguQ>*g|;Ih!nm0#{7DFeUkYaInYEDE6==H@O(|mjC!b6voOyI)2Lhw-1(Ub) zxG!>0)(`U*v0K`WVYt#kf)IqL%2gz3J?>cgO7i?icDOM5-dApT?6FtheB_B6AA9rF zS6_Yjiqq56SM3Ga_?Qc2$z*~ zeKYypJ!q}iNz$qnxn!p&C+D-tSHIzkTe^QeL+~&B$c^K$kCKCHV1o13e9Z2oH9|)k zjV*>udenwD&K& z)(&2e^eYhrU5Fr41RbchP9V$61&AGqeyp_%0o46;@_c?K{aT#QgDh(k z`5oFS=+GSWdWnr9$ipy4;F+CYlD;~hebp~C)G9HH&;-z!&ol$2cx_-Jp4N6mhW+p&y^!Ba_o(H+ zv`dDeBhc#BLdg^yzoS^?<-iz~%gS0w$7@*%sq#DMx>6Vs9YbYfeI0@&VVkqtNPY)$ z#BS8>?PY$EyNHW3;e2*cOsALOSA~Gbfl$MmMJQgTNxdPi_!}P(3s)aD(3k zK{PK4i`3y$bw`K(>3Qj3fO72jthr~k-M+?xrsnuw(6kM+L*voGHmtVE@7C?=lz{;? z5@ksJ%U4^iil=-PpR zwD7x@Z<-_PKkSq}f%Tgn(9K&V2EH6ps`Vu$#P22MN%Y3eTi2mlY3D2}kUik9ET;vMQWk}As?$~Sx zOv{9ZR?N6K^aI1s-2=tYoMt}d7#H#7=Udv1V7Lu*ip*F_!-5WnrhKRXomZ-57-~}O zA~&y2y2A5FCBT&yZIzAWca`6Dx>)8yue59%erH688G{SID-{pu>pl!jmBpZpe?Z_^WL#_yJbkFhS3ncG9O&d zPCA2H%+ZOQRPc>nFO5VFc}yo|M$>jhKjIyZa~h5)0l+(qtD(btEc&mJzc1}ZFr45x z4KZi##h7+Y?sN|q+Q+R_8Q*3LFGZ zE4vjt94lv&|KQ|u(yNb6*Q9S&Gl>=w>xm1e)MvnX;dZBA#E{oXH^8U-N6U0gNG}ihBT8F>%wi?>U zCtiYO<#=r@Nzh-}RUkT!w|#JaIT?BNj@j&%<675ZpY;tfHUy?=XdgR{Fidz`2pq?B zB}1v#M`0dFsjcO_O`~uHer0#gj`R)&8asTi92lFhL4@vTK@NVmhVCi2S-H%0@BPK)!u8|cm_y>@+=cv!&=y>WM()J@Y>X3KRZ19`L~ zt3)|EblvWir>C#ng;_w{!AA1C%J0ll@=An@?u&KB5RjItkdZ37cz}|qisFtt>V(Lv zR^R|F8z6IjYv&hqIruPp&C9p8zJJ%lE9e2Xf0?=9oxMYjo%qtYg?E&NGP~ z52+wbg{mFtzqx>4D~``C$KT3zpTd06rIpmF5OG`xA*SlmRHW!0fgm*nqS(wA6_{C# zsJ)?vkwU|XQq>GS8R%>L@4Ju7z_x4S?}<>X+Vyr=)1JWQw!0^Acf;6gEXU2_)3eXb z;?}2K+#iAND6iA+QybQSoEjx(`8WI_v!5j8sMoW(=rR?RGBADhsd%|Onod@$lanRh zYp%bf?Gxxi4EH9TT-0$~)EUezp>7E|f_q{%q+I!rV-$KY8i7A!RNc;!tWJGg20k?C z*-Z0Hym;T^tgFk4P`h@ zlJkj_UBh6&R5+BiC_Jqg<*i~{y;_|NF1@~D4qnJIPKC#2G8R}HfzF+=bkH4~E9BtA zdJ@V84;MIzy*E{H+@MbQJx!CQv`1F}YZrtn?V~I^wuee5Q=G~WE^h2jv+l5Rwlub4 zv)6TskN_SL=S;YuRk46UMFLxLzH#1e{5y&LDts7r}$45~DInI)#gCzv0%M zEj+JG>$nYdXT}JyN9@=f>&3mB@80}=alC{3BZRuk$dg|-=nV^;`7uJ5C(NB6(_@R z2uYQ$W#^y}J}v`0iWUuB0e%qN1$l55#(0$X=IHI+Tf4dc-5KxhcI+;8NH)b8cQPX9 zXhG(D5x1$hUj+2bh|`8ho(`3mT|Yk_i7VwgSN14tEtq$ywTj~k^EO4;<72yyTm^Y-4JqLYNd>9FMYu=b8SQ&z@L~+o!}HX#!jIt;!QKSze)kFX%PYAJ!QKSz ze)k9>qP3zTa(|uQ@3;yx*^E|HMDBEczvC*5T$rDtB66qm`yE%IHSF1niam2W`rT^t zyD>prUde6P*_-0@yWiL6cdsC$p{Ur{o8-9N^7s4Q8#T#!9`U=?`F?lqY}B3KPa{L8gP_wW+C=b4D!p1ir{oc84XX1+{x+H#?xpx@4XLskrxjm;d_gtRvO#e5# z^#{y`iw8=;Wi?v&B)vbivw zAM@@wtI^FXoI2mf$Pu%V5E?NHRs!+FWhC{4GgwR<5eZQQo1I7sHn&K4$;w+v(JnRI z(4@(e?>u;j=*$^U!@xVQzWUC1!v)j!t;vFCK1lc zD~d(rh{SO`DcEJ~sC4YmLMn`w*0+YbN2gKfRQ;amF8p1$%qJ(gTyDMQk;&bo8`H&` zu*@!vGw)n}$Jpt~X`D26y@fMjdzsfEFaz%EU-5`Ue;y{n1{N7_;UH^Jw z9SipEn$oJ@(esRz#tugsd%az?j*-#noKojTW_K-MM<4Za8R@+^4(`TVcq>Q1#sXk) zOPpuyB#GyYU`Ir^Rl-Z`dArPTTf@5z1%7A>ZU>A5ZT|a9>?Qu*H#`F%14Q`A^y?gd z{3D-(PFBhv+CDnD+1#q$4#Ivq_wSl@=pY>Ed`$DZ-U2*I4(vc|*4_A%I%2{FsfH3m zEU(en1@nqxFY@WCUtL%Mx%COJm|j!Q710%u;ZQf=>g}R1mH;<{>(P0U+1)Iis*ZCm zqtRqF*};zU5u->%f%uFh!@ERIl89{OMHE>PB#}sxBDq8fp824bk5P7Q4U}FR-5}<9 ztxZjM__@ugJ3^W|EAczdcC+5GNpjuDGZv&Vn z9Y1aOg!FbXTAFN5n>qu;ZJV0R8YRhXFDVlwDWk|Tr&uK)r$|In6iMRZ=pZX?Diz7X zq35y59HK$5mXE3IC^NKB9-fD~-ixjNx~hP~0V@LX899Ne#X zK3SASxRj`5BpV}!HZB04Kw!Tr5(iHc6-5pxvcdtL*CHtfD=8&5?S7lej)`Jp>;SL9 z)6nv?)7+SGS?-&h$S!|6?PV7)UXSS>C zRB8;L>T(cehmK)mV()kGJNABeBXg|@)I?LS#m#{l5xEu!I9=j;ZC?Hpk$;Lis zM~NhfzACFMD;bgZ3jA4{D??FdNzTREwE^`T~lptU(^&@ssl+EUfa+66EC1(fq zJi8miC3CzCViF!vivpc>3#sqE&2DdPyN*-G+k^<(2Usv%1c!!xK^}PE2LZFY-pCEx zc2`o{*~#acJ&&_IVBk#az~x-(nLUyb{g{o12?Q$5~Od@z(5Ka&9iT zOzABrK~DpS2HcAgj^1vy*V*Aj)yv(K%Yfo2cY&S3Tz2*Ww@H*xut8A-hfk0t#U}`| zU~3fIqE)h6eHc0Kv)FH?QeUlXA7xLrb9-DVU$o2Eu|Ts;i#D;fK9L+t8)yNADJ;Q0 ze6I5qU}K(l#?$gV;Zsi=BnvMZN$E3h7OQle!u>mqot7LR`9|UfbjZOYpopWs-pG54 z7vBRqz(Yfsj?XLxCN}PU32=v?zRSIk{!)aZQ94#eAw~csW6M8NbVtX>DAkh9o*Zrd zydS+D+dn+~ENz1+uipV=vuxdH8mzVE}G3YIGeYO zvcs0KS$b?v+2XWYM59Hyo15Iu-ufCVMGGf;xm|pgQnbt1F?ugxWAG@y$Njg1Owya@w}>yWfiBXK5@Y6N zuzTYgrPJ3-$qkJpMiV0=iP&J^-kDh}TY2@WAJx8RqfuQRhBfbFxb_rWK7~uF=(e^1 z5A-> zxE5V>V7k!oxm1Ufvpn_=W2fS4e!Y|smkBuvBA-$})+x!;JDvQlT}=dfDY@Qjz3?r# z6xfZAk1vi#MkC`RzPrX_o{IX^b9(xc7@*E_rtE}+xD`5|D@ z+l`l(J>)AfXB?ThqomeQ=fHyKwbc*YZO+J!ipDC$DrqbSEBdeusO%QgcAu>HL`BAV zSvt7&RaW8&Qup0lDcI@qJFtV`cA%#Xnr$9`JRKNBVF;A*A9f0MWA!^UbD(ZFjbb<@ z@H^gOFSYVu_eKkzYG?FLC%T^`mBY@mfE28GooxPf=OueT_!Vag^CWSx0}w?)?sDBj-Gf|qoXEOgGJp?I%J{lTsf+URF)v^ zYc!kEqFCXwmmBS8UA~M3QD+<(1jf@_#KIHeB)pI#IMC;J?M+QDG(jpeRp>k(zYox^%G9Mc0&lJ2|oNDW9FhXsn()NgdMvzPK$%YO!+|ch(b3I9z{CIrT1xHoG1E?mw&OU{ zUwSr+1pNc}k}PXA6d+$gr34JAptAU!(%=2O?ARkMrM*3r&T_z#rl3<&ZprqA!XHk0QTdetR z2ProxTXQu)18O#a2<$-qt{(rX$@|ez+lyvGiH}Q`%3sRw0NTFe9l(RQ2wfYJxxfxa zwMb?gGnq!F?j%;Ye8dbR3rp%J+^azXQ)A@im`H3~2z0RY-HE!`-v6 zvNKkF1;D{PQMvQ?qm@;l2o4A2tXF{@b__G6rS{{9#e=*^$~s@l!NpL%OK-6SrXvBb=lo)hG`CU0FfL0gcthxdYSdL(h zo}R*$)rtMGb^8y0{QcxR1sOn_$-%KHzZBSE8#5VaF&w-}yUz~3vDrbpDoRZ%NGU*_ zRhQyIdM{OZ=DZU)9-DvVYJ41=GHO2)ACc;W9*4cy-tOjF1BvZH5Flyv6}xVbD&bf@ z0CNmf7@?ri0UqeTCrWE^*xWXVsMWUCD0cas>UKxxy?fQ|Kn}h<%GOf3J(6o~qfbIM zaq?m`3{@}eQtZTH^^4~LJ#op2L{2iSe78X_KVX+6(n+OgAQhCRr=5gde{fP6T+fAM zmx!qqP>`4%awPd3yuS!yu~>@3MQIkeYf^YECPcHo;8dtcS!Q+~{k`~I1Eh79{0HBv zw8ad$%8TLuJkC0g;V|B1S3RiOf4|x6pxvQ>R)#2zNwTU=ACLGLTy2X@0Lc9bfEwQ*s( z(E`Vgnaxffmqm6~V}>CD^m8eC1Lz6g4ugQn%%s6JI@w~WJt)}<*r7*~-wjckZMBPL z*Wt~cjSm@ITpSvk|8+5-_1}rbA9kJ$tuHRl_F|J`i~V6si`%%Teul`5#mDC7vuB?? z*?+Ucc`w;re4*LF;?lZf`Av$3Dm&}z#i6;S?P(A6ro$zJIIJ|`gW;bkJeMwAaSuHS zXz|HQT0Cr5qfsKnh^NF&4sOcpr<@hxbmEW3`=N^-^r=lYv$#o#Vgdi| z4RAae-w+xG+ijEMSP(a(*-{fiPCj9X1b95&k>qz%Q&U^3t6PI38@P5AG{zHUuCEXE zuk5U@udc4IZ;h_6%oxV7`JGk!eNmWv9b*lyZfm=hriGCxT(Ph~46VFiBo7VRT{)!1 zZ$zT=F(Nr5R*NX?&fY}Wu!3k$rxFB)6T~oqInQWSt*zl-KcVaZJc=LOlHvvJg5P!B z$W49VZg01>v^z8@%@8WB)0@-1*d2I{4Eoh~@}EE7+-ooIDQ;svu(g9DI<7j+{RDD7 znHe3q%nXZ$yc|vixd{K>V15_;^f4s}eXw#;sF#jIH)s%K*!fR(k|3EV@D=MBK@G+dgJlKfDl---J4SFi@hRcgz9k#o=pfM^< z&0DLaFq+-B`}F(f;oZ5V&7Ga~ot>2(`1|&@HmBzvtvvdg$$JF)^222y^@NYV&R1fg*eSQ0YkKt>`cRD9!onGbqv2PM)~QM zFo>CuJb?E_o;xoP)QUd3hC(O>dLfBNk>7m)fm^S4RpzQ)Xl`R4%Gc0(Pr0Uy_nv(# zmF1u^+15nqkkK`E>{dxvtMlt5mR3FydUM#LlxRI!2$tPgKa<}@^4IIjM%kjE-5E;Y z)mvNhgS|9bVuxB?cJumUAH1(lmYH?ll%J9z(GprL>wHwUWJ>3mr(yuO`&Qr^#Ru>o zwmUT6-RxFrc1Zp3_1WFswcS5`6ObTj$tOHB(hiCl3gMmOEiujfBEH?fA@A_a=JG+fBF1X_r}`Rk3atS%j3t7$F~-qZcbneeL3CN zyBfP+m&!)_K1bYFo02A&VRPQ?$*{q=XyzBh+U9{>((rz~B*{@1v4AYITKFHBFxb`x z?8x4-af!Te`O)8UwMl{C5> zMzkpZw4z3jsxSoA=g7uKS7+_<5wlMgZyEHLoZsAkIHhSq99Xv}Di)8`;fSlg2wBL@ z(d*%%XT$~V*3{J21k~k1g?V|O|L7m+ATu&Q$l;mrF40*L8QE!BlGGGQRPQ95C>9#q z0jNQ+Iu6eOc}j=v-o2D?tRHdQg~yC9!u4)fUYLShC;USQIt1j z&>$rpH3jc~@_xY#ab3h&;I=^lN}jx&9DVYqjp@w;yQE=?9d!+kdDkpTbP9*fHFi1& zlzf5~p6FbfUbrC?7&*XBQW41ncxhrOD&y(GBeRg)X%%!j5&?nC|e1fWt@mbvJmNo^iMZ-*ETj5EWWv?X%jxom#(g?R96Y^&jKZLcxGr^>74e-WI`!)J4sU1GP&U)%u`}iDno_3m_mVGhN29{k({4-)1XIX z!fsRjEhRQLoL~fYx*QbE(q^I1%1&Q4_1Ck9&m^httXSq8zfJI>gMi+9I^_ZCIi{i_*9e{hSy9T85%B0g_rGmB~;> z$dR0%>2l@Dr2IyWvCG{Q6LH?ur_C!!H8!mu4}e7 zG<7I(JCDvXo6V^ftJPxhXnku-=KKmJk}Z)yu9zkn%T{PjnnFiM#Y=W4&n0E2ysaq6 zL)Uf*!n3hQ;0;;Ye4nQhx+~on9hKf_sN2}W+yysPY07#6ai&z}<%v%uG5a|Upz5qES(LR4+|rA5H%|G$)r|vFhn%)de8f!WF4gy)O^Z_K2%>MiaonWjOVJ!4ofs0dOP0d&(%%=RzWL_y zgw|G3+(=;uB+o$aQgL0CyTfgB;7)iiMhK17-459r)B-$qXjKn^whUBTTVp8EbuY|o zC4-0Ue$sUD+{qMe!i`TFfgMzTl^YZJzBL>p3LrGYke64sfIv~d;1j=c{Hd2AX-k7) zP@l-+&_WZqIH2=-mtk)qY;c-RWFO>rz)rAF?>2m+Zr}w-&##+qpR$MZc;Uc~?1s+s zLixDFRbP<;d!3f~Dn`H4&a8!j4m?g^2vPAz^Vp0dIX|<>Adp@|MzAiIgu#DLa9aw^ zoeYMY+|N-IwoKiX-R&bhBXP(FzpX80_AYt*_=XV>_ zA!-GBh}ANP)}&AfgF->&VY}&{!k&dtWrFg<8AuQIw4Mx4|L|Nt-aA-OSwKDt)J^$W z4fh5QxQ}w{6*eS08mef}PY&F0}x>Yev>U`Zy!uNY2kJ$fBYC9|6B&jJrNPKo1U(|fW};m3>7gL0Pn2$gA4DxD^R8s8o}eLinAH{4b~3# z)vJFscR1Vtu4AC(f8NgYDb*^D<9|kP(l!-Rxgu(b=2f^Tiv)<0BC@AQl3>sX8aE`R zkWe#iyyaFFxg?d`%*>%C^NytDl;yNe+f4PUrm3l3_C3$T^0d6Ye&IRda(@^2@cW(f zJL|^NMuS2?FA1Q9Fyw@oc84Aq#++uc);E_r{q`)F+Wj_u@9a)*vXA#{&m4VTNOU+l zGP3)qd=?8jW=R^e}&CT`9@tm_gll?2cPqXuG)H8r>Ps0;w!81 zq{Ej?9w%eT<5j`t=4N4G&Mx*zYYnh#kj=`lOB`SIERKR`7KCKa6CYa+g+`*?eIu{` z^7?0!l+$fc%To-2R?HdC3|vDr#DCbG3t%3r`Lb#L^z7S%Pj|aN`)uoq+;)%I;eEEb zhiv2AY}iFDxU;&uv%Ymwa9epxOLI$)tkKjDtX6@4Ka`Ye? z9HhWZXpPMna9q6kB94naI(Yju6||Uj$`yz4(&E;dsqrqHOjB&vs5Q6)VDATDcP{&9 zsx@}cQp@boVd)*Olqksh2^H`m)ML>gIRkSusC29h@YPpc8&k%|9-1;QM2E``;>muW zXLWafW&e2p_}Jk{Zfd}e&Cb~csn!4vBGK_Msmx)1pc&`zKRm&f)kT+~#}<|+7tGOL zFi!~`$Yxy2k4-)v>VKGO<1vqxBVyX^F^O)f%{SlQW5UFxt+Rp8o{y!j^tzuN3t8vq zhR~wy=}DcQ{(7+JH{e+8&YSaGEehlgEyvrumA1N@7r#6jj-STge(v+Pzx*|8mu9C( z551D;nC%6NQhXLBfe37=3|K!Y#=bWT;*&c|%_ zIh}9rR=Im(Cns<371j7fV7H1(0eV-!?(Vxi+dhZ*#g}h%j?GUkdkr4*WOp=r8ux|A z4M|@xyoUPjw`42@b{Y+rowM8AqiB^V@8VW<$|?)X!48wX{TYIuy_-2I6(Qff%bhvxuv*$k>;17ou&{wu( zHmXSHhKPQ@m;jk}tyYgLao1QRVwyVoG7*c#9ns0*(9m3maW1kEfrnYG#|HpSE!5n2(@6sSalxK^2K!= zo@gF*^&4Gw^X$pVvBF#P$kEY`nvBQ7?{HJN*;VJ1-{FX0x8bQn42)<6P7NX^$Wg)? zU3JLsbP6^r?p<1v#K*(RxGXw}iCQz_F!=Ng|MdIQgSRoM@XJ%!W#o56rd*(=oKKPQyvx42vTDrfnLXjj8(G>W?ZVSt z9`D83ljOr)I70SlW|Llnf^t(ODK9>s^Y51CalG?TnoV_IgK21_vXJf z()Usy(#5J!#QJ!XaVYY*IMjXBZR%wQ>KZ5Ju=BsS;$l=l<%{a6@f1I=Y_a?3^H0y{ zS%zn@%gpZ}8Xbck9B+s;+ghYmWjOl}qp;mPIjc>`SGs~p&CXUl_^@jBXmK{asbaK{ ztGMTq-+AWerw}wl`hvSkTe~zC;DhUd-Q9{6zfE&bGahzCLxV2EYY)d}60zlX-o19u zT7Q2qh=vOBy|v97;dLZrs>|6qyF(_j;@2PK5_V5vmFw}Qqw|B4t@ZZy=)!Vz7-!7w zoPGNA)W*}8Ewo@vl%rJDZ74F-`+yhRq6w-Ho2|3%bTpdP?yFa?{^|F*n&XCK`9N%GgXVV9BLG0}V_rWvIQR|FvP+(Y!t*)*4(QxVSDW$; z`gStxt5%_)<{*-wiB;7Kfpai4c4cH-yuk7-u$Nyds8X-4l9#r+iob&JMWBJyO-(tw zZg)Dn7Kto=r$E%BGCGp`*NnnT7iGoei3BzlpDx}yO-_CCNvN{K7raniaFP-LRJtRW zsY-_3dB4rrIBGE7yFBj?9b#AjU6lUE$_JRC!sE;3?M9!W zw_tI_7FRnp!IKlP(~!TX(FvNG&LzKFqS=*I55B^A7`;#$61VAjrBs)m#2E-UHzlp! z75Q!zt8(B;>Eg5-QI&dO>Ge*a4RUHrePzzBn~GE@)JRy7hFIq-E-kLV_Qi+qJcYB! z+i_xRqPSP=*fC5!?lpGkdaJ8u7ah(X#eamj#kA{m^^f;=iKAW&78z;}kG@2<9l;os z-|t*PwTAXWMB?Q6`8vs%W5Kt)8BRHz6(ymg3urYl$OckRXuiE_DkH&G|WFGS>(ZRWZ@G^9VXzCLFc z131Jq%`X6g;=54sl}^7H|K|IX(o1hn7xOxJjpmhmAGZYE8=Sfq^JFhhnT=RIJb;!|_R&u4EcCpmq;TfiwVl1Hl6Iw|v63U! z`kl)IIO3(MIw2cgQb>rX=#>-GuBt`k7l%B807e(b@6d;OqWOBO9qjD*q`=uDD(jH* zLO@<=+-zeRu%#47cSWKaXE?M2yjxMyz;d4v)4r9mwb}}>oJY8eug|o3WiGab5doKB%lenEVB z+5s{`=xk!xy|ad6Zjm^t0T)-@D;r&x3m-AA2AmcrRl&|v+lv@o{&D}6TUTxr2v*m) z0g+|TW-oD=P}RDR+Y`hv}R2UA@{FZPsp%4ro=2rs&S-I_m}F&0#|f;;f<(JA-MO3 z!HmAqkZo+xtrC0G7~Z(ifnHjZwWr4#*-wTms}o#t>f6(^nO{FYgWX*6K?iGQ`!b7B zq7c7(Gh0Rh6;obwlvwK}}w{lI>IA?=#g%{jKTz?(%ib@7zvk z$~_cWvQ5}EsNc|B!T7U+(O4FsK5K5=*-5NdN;$Dudw&ZJ7EhAtcT-EZUdY)6GZHZa zD3&Z?CARr`Fr+*y1e>7jYChBzsvNolvxZ9l0}7Wv1Qc zXB8xfsR=vREqa4mW61Bk(XI&q9l;j}kS1rqYcQL|4znFyMd1LR=c@JUl=mkLidJ-9 zy!kQl?x&~Uetwd*o0nmi_BEzdsQH_*!WHah2724r4V|n;xz6EWSHyE%I+ zi>npfm_;qQa3P)FUB0gQos$S*b4evaydrrWrkJ8bO95;d&qLeo+0LW0v(wX4%2%$pU(p1{^@Udg0XQAl(Xs^m{o)R-ys^U^ z3?_XZ37W)cID6K9i&qxs6{zxeHooxP%lo65kh35^+4vtX9KQ}md2RbWGg$uuNHcGhA#nWyGXNdPSs zN&!nCutv1)@wn`E^b@T@Xl{ybTwVQbU(ivvIJ^6PUEEvVz1)|vOSAhYIA%AAs@a35 z`lYmYy0@*frmUnr!Q=EMln&pHOlM2Am*EgOc44yL70{$V?tFeazq@>0^E={mm_ZpK zaXJ(7O|*s-rfeV4+$&fkc<=K!y`e9f$D`yu|VqHWiQP{c%MuyFU zF2B9Y=_<*f!-5>VCwn`o{V!fS*=y*W9_Ws4P>E$`-%$l6rd^eWnh8PO)HSyU)B&}u zRucd-N={rBf^IEcsMVXBf>jEj;CNrqR+u>1k(VcYT6P~lYnNuH`d^VBKOD-}AI|iF zo&3@zt}szjQdpH>H5y}ON}Fw_tewr$I#$LpBoUWn{I^y#aRK`3VD7d0q+#K$yr10bPz=&Mp?m(OQO3_mCx&x~Or|yf9FdpONeW z3hW$Q;XD4}>mT=S;oIh&*^Wda8XwcHT0?mp1<6=+m*464Ht74+UGVQTJ^TjPwzmV@ zL;$E>!zc&XskV=O^;P`YMr~Wg%e%Z$Bkbn#yELdYK@BYxgtDa~U%xeQFFzsgy`x4b z!G3b;ad=yer_L&YU5edx&F=&N0GWulQ2`3q5G626 zFTn=8`(Hu}qXmH-v+Jcr_8tR8YTGdzcGj5;J$-9w2HhRz z7IOi$#l8Mw)VBZCx8Je6_U!(ZDw-dj64Nfg5n;x>a~x~;J$Bbb=bZ*iz(e2xALW5y z=Go3@jYzG+V8`)s3Aet!%%P3d%Su?ITG-9ycLL(k3~;pY4Qf_FP5yVWzJbaLC8wxB zC@c|@gWVHN1M<=je7iwmduqj;90xhz3wu!S-Fj;}zq@>0^E>he0i6k@DLq^bb}cPn zM+BlECy}&9Q9I3Dz1lMhcE%pmj?_PlSP3~Rjc)2fNzN|j<=VSw*&&Y|p)(xDUSum< zhnd>0&KUH1pI-3xf~LMk=tzEeEfrR)a_u#UY*53rtES_kAL|{)Zjx37{4Q@>q0Mgx zJ1`>(;47^RLJNLqSqo@Z_M0ksg=MFlM54Wr`%)k5=JGq58Pn&|C2p=#{f@j(D(oBO z*Rm@srByi5eLB%6)$Sc`Z3TCgL7UI80=FXy+claJRyx1Cd|mT9iHMGhqyRSD6#&-) za%tD2;0b!rN-hPv$OP6Mj!4kCOz;u6Bcm(npm~9nvr9Ena~F7T@;g+k0+-FBkudFo93a8%Afwq4@b-_Jo5nn782RxZ_(2K} zL5#ZN0R<2G%=>zsncInKK%#r5oslt-yyA) zR`P}IiFU9nsq8q?AMW{rNqbUkM$Dw>!SOUK&UMT0E;I5w6TxM|4N6B!6u2noOFN!P z@^EPF_Xj0?<|yPbeV@1yVLn1_EtX3M+hFaTRdI% zRp0FGxkDj(rK{5xQP^iR*d-i>UAq11g1dQb4tclOY_MVEekDi+EEfpk*KlwM zB?iCYt~nb=Gr>@t#%e@ab@dMM%PG19@W5cZC6Yf7Z7ssvO% zT~WDMi`Nl1EN|@Zj#Jeg4bKk4*D1dPJ4Q-sBFq2|7l+gMWOy#EYLZFMl?;x<_@p5)8{}AnapO=7Zpevi%AIMcQ0#JciL0q`Bj9pcvWR9DyC)~3uaEY5fzD!Ylj-*oN7R;x zw@7Uk0yymB6(aEkIHLpWV*3S5yNI$_UKt#R20_LZ?hmd&l4QJW`XFSm(26x3Wl>^m#iEbt+D7_>(vgwrgp_p?b_tv=OmN%V z^PwG|+5DL8%oQ9bN5zMcCs@=x-gV3GE;I5wpfgd_vYrNRP_DA0_(kqH(!VuFq3h71S41Ia*rk_LlFv5^C>$oGH%$%&O^f;!xn=0NJmZG=wQGw- zH8O`8eW&g9gofzJ`(JRY%oeb_PbJ1RhCMPg<4aTcCcWQsid(oHnG9&P6>qY*dJR9cxF zLC(wB%~f?>PK(8v;U@_1>8XghZgO{ZsXZOFwHIn`Y1n%zVUVctw?It_wc5U7Y%|*W zE$HbHFzo^opy7@$K?v0xjgID*Mn#b3xWVrbK34{>UAy*jLBq{eedvFCzPG*uc60e1 z4UNezL-5KdD?Z$PZ)G2^djxJ*-6<=pla)Q+#8+PYV5bdR!x)x#PXgPnum{VgDDhaZ zd!5)Z@;ehm*K_bc@{o8OgB;kAD>4_o^Nss~&TZ@?>Njn407Ub zA?WBEoKV$4pLx~bQ4yci*e}!wFBTNvdC4rv<`e`Wen)z==-y$bT|lBlQA0tOUP@td z4>NR(d%D$vNIRdpb}i1!->NMh}= z6%lr5tqAVOzX`P)B`hnZ3rajz&TcMJ;-YU|C^TwT;cX3fRNE9ZCbdV-E(J=R0u8|| zJUi|Xeyt(wn0C|Pg1Bf65{zkQ1h_ORrr(jDAe{Q=&DXw2>H2h~I99r>#LQO0ZZ5y0 zrYUaHPRB&ICNXezJgr}?zAZITw=^+-|C9oQlor^$Sf*qS9LJ)Hb}CElSaHJ z@L2Nemfu}w>57~c7xQ; z5EL=G8l=0ZZ~-A|!KL|m=MBr(en={xFGGV?e`mF=n*+Pd{0{I)K_Od97m$L(FRZC# zRaT6Qj`a5Nv7sE@jjq=pQu>+bJMY>y`q@2zhYy=vc|Q4FMogb$dL2P0LC|z}=40_M z>HFj%J4f3j=XyZ*34zzB%-K~FW=3WXfOQRtLP8j59bS@W73`M%qquC)WK=BVhEFvcXVc*)K+i$Nd|+^lNur zZaADx_#FDb**o8trlL5E|0S&~8?6?VnU zENEy8ZbWNTloe%zf=Cup;*07Dc+S(fVJFpSxs1V4^5*gB!fgA6rJ9*{& z>GM9E?i)ZvD&O;H4WU$e&wAP4RW7|~0Y^I>HlS;YhBCUwh2ZlzoxM98t2+*u!A8f( z+PTfFcMLMh>{w7y26V)ZoS$GvN%T~7w3#?rV)x?~Zmz5HUs{`*E!LeIC4XneoXRqU z+=W92jx{KS+uebL(z+?^^ZBMCCzOBD0p8rFtm?uc&e(t!8r`z3CjRcU$xd?Pl=F>* z0)t|yd)FrhUw~ESEKc}_=xTx-u}kMBZ?390>#a0;l>k&ncj0wftQ|BOg7_GwP6kGj z41bUNU_{kayyba=34_kjQ-b7rD|b2>9-)IT2RlgEJ4TSug)qoTU8_vy)AyOD%AVFe z`7h(PCPiK!0XJyL9At^zAy;JB=e8FL`A+YRmc}!2uxr@g^A|WCcMEgVo>!9xadaQKA{ zP!D8)&SN~ApTReLudz3-w_{aGs@I%c65MEir5-x!3N%OGS+O=@%i%y%>(;(_!gsuD z|NeddwwZ;+Z*$XHjE+3yQgQmIw-){mph)6wCjw58li*!Xg!{sYcs$a+2z$s$!au#I~pJ$nld9pZO525AMP;V{#-RTNEWI}+4B*h%* zYCPM*+RI>>9}=Ti8Ol6YfhUHgIXMI#=*425m(OOf4#f_e zeZxhoU0xr7*gaN(&4o=pst0#k+IDYm+d}N7>hld78v7H8grg_x>0rEMboqskbHC76 z!Lt>?j%fog9QD@1-w_q!CzJc1(8IhIy`1#L`}@MXcC6po9`{{FQ_0pBly8;FUr0<+p%9Z%!mf?&U<>fnBlK;ZMnz0>m;z4VkX#LK$#856|ak87T2NJP3&sm?*uy; z91u~C0*U`cXb>aL6R-nU2^lvC6SNOtK($O&{1kbzuCr=lzkJTc9#0iK6C{sTj=BKRWQ+m5{! zCDKtUy%qJA*=0#8g&pT&w;!=97SORJ!kZ3`7ZmRu=Rr=bf^K&9%cI2w%=~+Cq{kWT z_V$)iG1OaqfA@cOKn8G9+!3}xB(M|W-4qsfP&(>6naWA))baZrL*_vM3WVaqYNm-( zNqEmw7=j4vr?4YGj0bx0HZJ9c<$47;AwowDqa^yT5Q++=syY&-q)c z^L38h^|N2{qF&C90*b(5 z5}A%*enGC6`EpK;2>%HvnENC<3 zxFadHRSLmVZrv_8EaT|Q_#KKIc3@Y)8jOo@=K*2|i>lL`wHE$v^-5WlvKIcXd8MpM zSyg|x1m2a)zpRqKTM932*_T!FcT3@=E&H-c{%$F}v}Ipb)89cWWmU>r_`CX*@;7_u z``We<#_>O6Z{|5^oW}WSIf@%Qshcz%f0K1z02!;PqJgdUM@2f_ULpc`Nf0LhwpRu zAl==6(*B>{z3T1%`Q5AA{>Sg{j@Q~+UQxd-$LzQX`Q7pVx1DXrS#$8sp5q^Q-|9Ham>T)RvNQh!Udp1(qlEn?HumyE%Ds(P zHo|p%RM$@p=&ku^C-GXlJV9HTsETT9WO(%4oIcp;4WH~8s$w~UH-_i#VD7Sf$98G4 zKHsPA*LNHn8dIzUiD%mxO~+=Ua4+*Ho*O*L*@(w2RE;i@=-2}*N1yllBg;jZr(lBg)yW&GDRB)_Tuvz~jQ7vwj>$bXE zF7NJ^pVac2fZY`kLsd7hLJey21Umq90(LvwYuhkM@YXFy;+tQ_{#*0Ta{V)iu-};e zARiseZ)7tX$)$;|w_dxsfxX8^haQg}9v+gzKMxrb#1n$soD+|U7(aQM5A*7syYB_; zE=?V=3+;cG*Jo3cjFGIZWInXsn(W6SOO|Su6lJN%%%V$eD;9T|poHghBuj`OgfVNl zM=+p^lDqhWk~h1lV$AOQyE|1Np?>;S2}T3M+ScI~;oxnZ`8U;MIC zV;G4w{9d`@gWEyv%S|4*0=^nY$PV;i4$w)Aw*U{STbOs|7dCo8&S$p(3;Xp*B$Alj zh$p+ch^7>^VzNJOxu%gBr{usT9fT&6j7gCYgAzs(MD4sT7-RfxtaT(OBZ1x2qW>Lj z`2F3);5jvA0Y^ep)S_C-*p^zd6uYFlC3^sNK#9M$<7zTdw6oMCx=BTRoSrc$a}O9b z=X$8%lyY|W*F`7}+nS+n&i9!Ul2*Oc4MDOYxdN)n>T0=+z*&a$U|%u0wfh{f)pYkH zXj`HUzd#0jjfg4hfSJ@@5I&{G@cZ(%WVnnQm?SrUb?@g*ClK8Nny_ENv1P8Rw{ay- z2%(xq-*R1d$zA{Exi)ixerS!&FvDObB}I$c#BdYF_1hpR_&X2p*4WAStKc`)$j2nR z(e|z+BL+z+gvKwX2<=M6Yv875&B^mx>xCEM@VR^6c<`TF<($b5VN3)?LpHrVN!ZK)WK7G2m{L6wQCqHBf`xOXKqiSq0+LfS$ zxN(b4TCS^&ee=RqTuRL(b0ZNM&th6%!WDw)NiQd=vxJ|0xY%F^a_>wv@-Z2V&n1&8 zC8niXnyW3fYlfxbbXALLbZLapJjkZAf*}eIFyZl0&iUxQPOmyNTJ7=J)UO{0><}}X zliy8*skLOy-tIq` zbWfNW!zC&LB_4D9L>g)nUYwJb>R!w{`@wrYyTyPV&Lh9Rprce#G)k#qY1)Mgigne9 zcg$F{w--I$l4307XH6@u*(7bp6@zk48E0gqO8BS{RgX@60Cw;y$12Uq@1{et3xozv zHFzjE0TArAzKFD*I}Kcb_1Ey+Ny6HOKb5jO(jDl99>ug|hjJu1|6F!Z(Bel2=e9pe z*})hEibDtI2TY7pHW$9Xe;&&`tb?5d81^eDOX*|zHl}H=ORbDWY_Oxzn*;Ww)=}&$ zJ}0J|vKb_45>A*PBav*@J9C1 zcd~c9+bSkgrjk%>mbO*RrNVO=7gViMev+{v?kTu`Pij0_bcChfHkbx3qr!F@~HV*LZ1O|iv=i3^z=Z`K? zqjv+cOv~f+#f9^mptueT`z3_A>9Mq)80n_2YxEh^>~-zP>6^1gubacY)oxJ&1XMea zp_9{*mrwV+p(=mdXZK#f&acDy-E>;0aW!dC*B;5K=8UFTw$VPD&`ziZZST*uyEGL~ zBp9U;nmQp6G}A3nk12CP22al;VRp^Q@4zk~1$2Rd-D-`RJY|Eyr5SDE)xjRGTv;tY zYQKq9{W^QxfLtZSuHpB}`&V}4SVi)K1!EQe^vO9mQ9Dpw5IC3paV2DjKJEgR@E9JN z-vqmLC~q_DcQQiE*oky3-O-DXS+q>r-t^s6s6Z>=fcE35;LH zj|)8=NhPAufzH85TIXI>oP8+ADqx2>pv}qernk0gYTG&*q6F+vtpw!2@N((e?YO>i z^4^m|p?t%B=2ow__K@9LhgXmUeRd7MpZM%>_~Jto;Hw#6L4v6KjxNaQq=6hRM$esk z{BrIp7B-aq-RAkQUo(~(j|}AX?BrxJDW^A(%+0}Z<@Wl!G}B?YCD)ya-?M)#+}Qhe z;O0Hcvh}f-35BFQ@2p(YZxO3ALVAmLW!S9y4cMh~7bla7TQW_X#xJPdDcjCz2^Me9 zWfb#*Mf!W&A9P$RIQHz`Z6fYxkntVpxd<}>vTPtW$ ze0soE8)BkVB)e-*AB+2+RlclzextZQ;9=2u_L@XifE53k-%Vp~ha701}N; z&-SG7wgC@BK@!+dOW`@|^F0YGlR0${mi6QIuMzwrbTGt3G>| zkV9Yij_>a6CIKFp`{a3cpAC15!-0inE12*S7N%EP;0m}txsZx6B*QpvUuwOO`c?Wt z3&%J~9^ZfRJH+l|-tQuroe(X{_|)Lz!?2EW6uF)po~hB&n6Qe1=9cM7P{&C%#7v`1 zX4H%Aa+^3ahh#QGwF;uE2nSBmI8LxEu}@AJ@5bC#5;Bv?vt@_$VK>}z5o#VRauw}> zm6Mbmk3XLHyld~P;{5IDJR-UF}_6lCCiKdLk}8PAZi?Bi+1H`sBvscfbzd;o+$Yv{*>+ zAPf`enrs1E>p3X}7)hrk;YQv#Tp^R*%rT6a3HzNmzkBIu*^#4wX>FDQEmSnERP*e3 zU;6+a%Rg4bG?QNwS-Ah73E*joo{iG%|@EsObI?<(9{GGwT2l<6(yzM|IF{U;whk^0a>V!LowBkVvtl{O8-O=L@KGE!9U?}uIpPhg%OuLY9uJJrZ!aS2fT9@C!g*m9g0WC1% z{g7Wr;jh>-qI>im*tZXM4vmk0^pTX<*lpXkWlJ(LV3bcQimED~(P~rkKrs;H{(Fk8 z5s^?7Ta?81haFa{6Yv?UiNitvT$$NQ*@@}9QwArTSohgxoFp}UXJ^bLl9hmFU4AEm zWfd#rgWz{cj#&vVOfH;X4YnKRIq*9%JFzw>zYBX7s1!9q&LHwR1|95dmn5ogoAkzP zKtq%^m7m_49%sA$Rl>n0b18&T;09%Veg}Rg2*xg%LQxE;4rC@L#>a2j@^nf{?Ed|A zbs1?HY1tBh(+nd%WNU78sN7mrjKN#`%vAxxR*rDi&VWcR(Tkm&Hvxps3zG3BDTT>9;72J=$ z?>c>r9y|i8bUnZg%I^fP6X3y|6AGUdz=4V&oMEqxer-v~yD__b2Y#T|VmtBG*Y@S> zTMr(wVgfrS9O6>}P8FY+3?=@#Evjd?KU=i@VYokbGcPx&s7R&N3T!|yOz1RF_|FOn zi{-M`cG4k;XSe@xHE|F;P4hWU~;SDg>XsTmKI>=1+cutc0(hPNF>P_uIg=d?N%#=&av{4 z+AuubF6Sx_bCt5rpifQ8S5+3R*NWw{ek&Ep^Br=43V2bg~OY)xla>8r*pKn z96Y!+4!L89UD&SJJp@twp-cX>*S~|aBH867l-}e>Rw8yK-Je|yGNq0ZE38TvN9{D^ zhfXlX0=u=pHzsUYOwW$dRu1}*IHLd1P|wuF%|ZLyZ+0&(4}stD=&VpNC(n)wvy0x4 z{Ekltgn6#O55rN>Ab$X;7zrJwCt#D!8w0bjIdNc?!8oj-kztxRj{WL`+G>&}kQV`> zm|YROG<5{-IQGRUzu!MJG&go8aMc51J^8GRAWCuYKLQ9Mm8w38?obfGF$B-#$~y=$ z069ToyXVh4|Ep5EyZ_22SPmzhPAkoEcx!X3w#jVXG(V@!t8`92a?c_?*HY{cB&3fz zG5$0=Xp98Ak^D}`7dKWN%dE=#oos99Us42`}$aAeE^3y4#SHg z`7A_Zr3H4oqBkVJLs1b;Q;@;D%<7jghTddszU|=JIQrZ>jqhqm z2Tfu?naqoh*kx!rdVKE9zR}ryuW`4GzP_A1%Ag5Z8-0KrCwED3r1)hI;c7V zLgn5LwBg0f8oOO0y9=QCg@xdHHFVcsfTi*{Y&fxuv zJL9C!SnA)aC9?f;KBSQ7)2zH`QGO>*5%^~2%uG@<^rw>+`(jPWzs1Mk~v6i2b) z{?`LE#d{sJAeyEeMa>1cnFM~(Q5%xqVVK}*Az^DRCXSm#0Jd|#e${bp)-y{nT$;;lC8x=Ac>sP9kXx&R$wF>k%uVzs}CIi z2zChFd#}8k#hf5g5W_(7xcRKbYvyBS#>V7#2vSHIf}gP9sEy!6G1NpX!9L=bPG6cG zm}N_=>%6q)8>Y!@&%e@Gs{wWxjo77Si|m5mQTC(8vTCI)KT|1_(+my5O>MTSuyYIU zhuhk1Q3i&G2jD6Q??01Vw4zl5`Lym6iLBAK&fyDEZ6Sc)4FjQ#4INp=tgsX5=M2Im1sjv$HoMzk3hI zX~EGDHv%dgp-15!3aku*dM^zCyKD4E<1OW;+qznFZB1>7A0td)092Zk6tTJNaO zvbs&JSClJ_1dT)yM4Qcn?CJ!1I-H=1jHqY&2poJ)2kcA^M`=q!`qr&|IxkXHfmdud zM^I=t(X5=;suZ$Mn;8x9NhWuj`+uGh53wMk-rMq{peFyBl_+h3E%9|9;IbGCap?auO=99+hIRr7C}=sN|6Tvwd~$%of?o%21U&>*aR~ z6@!EA!~V`-a5@MF5_Fw0s{Hmr{wWyVko+zr0))D-G#YpgjgEg}Do}YHxaZoy^*c)g z{eg2kRfcjeSKC-wTYQN&nt*O8&n{vYv4b@*Wm}tCsW26@h@G0I6!IFI2mIUXMQ;lf zI|eh&hKeYZU$GKGqJVy!Yjm#s%U6=!nY9gg>=hzYa-7=7&>qHAeJX3 zZOH#;>NH_yV#$n=$3{X%n6$B47aO=XFuJ_76u2baoML#ZoNJO9t2+{ghYt)3l1L(U zOKCL)E15cH%+k!}*=28Q3LA1$L0+iS6bh zY)HpoSBKo3mkWxj5@iodEU`~TeUMcFTT#_lyiM!7Y;~qr_Uw7D!Cuzr`14P9D*PAc z*P|WJnA#MlDzX}O3tM4_>UTGR zJFCtF=2jM0!dhKfoxJ$nVEfAIDgzIhbC_sv7n&C-+pz{y@Eal-Y2uqP?BYj$J6 z@X8|6sqnsQKYNNEv!%@3`TjlGBkH}1-(H5L=-0l`D_zUg-B+-@mdtmGv+4_TAqGIH zK*!(r@87-k_xBa`_4Sgd4Y_~j)YQ}nOuMG1`8v@V)=1}wAoRF3^`TUWs^|{OdSJlw zY;){7VMxM1XS=#hJEburQzJKT`g}KilC+3jN4b)M4_9SsN-1MytTC`m(P}lJrjNSz zX^5~}mud(|&8u!RCc`G=bLHNus=YlugBRlBE+kBS$7^azY!`(}bj<+oeuee+%i4<* zrsgRBr^=|@+_J2~W3LzHw8?B=$HbUEg}qKqIqb)_@$o0lN*8e3aSM`y}T z=}X>gfWM@oqM`xn3xD7J_NV)I@818Vq9QjVYD4ay$)B9g|IuJy$F7!m$xtA0b#~zQ zUiD?^6VgleP1@uf?dG()&amJ8!`s!rrB#OEKcinFO)-h(fT4}8q2UmSGZA)_2_zrG z0p&S?Q27;No5BiFGA&eao+4S(Y$`T$UAEfP<|?&qX07$TzOJng`}(1}>Uqz>LG`=$ zIed(5w_fKy&+|Uddk*g{W~F&y*y~DMhq{IyrJ|BhwZ+iVq@bsXg2nJN2 z3jC>J%K1Km`1$3&b-e4TXgCS}#QnaiftIZ9TcX`rge9Oo*dG8A*s<~m_V#msAWUyu z_4R95MO(6dCUa&lO+duGKd~?|oyNHgyqTND9t&OmYz?Nx&Ws544*k6s*55lkn%sT+ zY1Yyb>Z=w%{#aa;<^}CSU=V5o!%&00{SZ-RY8rm_=wq?r(eaTG@@Sf#o?uYSVmV6e zD(|@^2w*18C!PWKh~4n}EG#s^wbx8u9D$@rN~#rpZcB1nu%bT;$e4pqtN3S9+0EsU$ytifdBEsl7JQqv|{*8UKG%@ca@46ORZ*) zH8$Xh`A@8WRdQe?IgA!fOJY77776Y$hCLR&rCaO64yVEn!!+fza$o+o^wrtp5^0fO zOV-c4lma?tAlH8}kwS0pn-z^mVNUGBGZ)9l%Eq#`U04I_v~IY}&tClGDg$m_n)g&_ z&;+wzI=S5&DZ*c+@v!L#vSnFFACEWoD?ZR4>A2iHNm_2K;Zc0IWGoQXaE#WLKSA^znR@WMrYxOsVL-a}|f z+K{0@*VJV47Z?mWLN_awbCH}`Pf|m6`P&-p&gfH6 z*#0maCe(X(dfi&Brs@I80=e!cFQc8=OfS1^vngDoQE-3oRy7=mzq~$ES7-^v!pW!# z`iirY{(5C>uHI!JF<)!dK^$wvLA=(NuC1rc{~r z3ani1L_pr4$cverJq!2Y%wL=}o*FyEW^ZE)K3cnY=H0vR-OGcL0gIqPh?AG*oemic z24cE*ng|^V6@VFn47!u_V_e&YMe!Id82J@O0F{Zq$%m~BvW3wDhH2$($p$Z zFv#SkNG%D{;qVB2^W$pM)vxBfqe&{D*yoqfpR1}0r27&)afu@?*<9_@;TJEPX&1G1i(ZL%?Y%YZ)`h46}u z_s#No`2~j_%5TF#{ri2CMukS;Pp|#(=qHQcO>~!q;g6iWEm=QvuH$*Q%Li87?m{b? zdH~5yfMOEHHyYmAM%L%&6T;cYTH>bA5xyxWKJ?K?@9w#G?;e51hDS&ejwpwd<~>JD z8^*_{U!FERUg9X!fpi9W#~iGl=q2PX#^eQAc_lqbPqQE^Yp33*Kt|mvfJUmRA1m6e z3uscvq^u6_LN6>qp%N}BB<7kr9iRC$w0{$yvPCmgiF@P*yqWD0i= zKi>V$R8yw~=Jz39;5?HYgLKDdco|~MmVz}LJD=?AipHY2oLd-=mvFZo_NyGBPOYuq zH#^>YB9FVR-O}Db`7~IS-Dzp0FtU91+8{Bp_}zGNG>J}P&X)9d4WkI1mtDxz!+Xq$ z>?9%3GMABt7x2v<8tPk~3Wdgc24l7ST|0~k!G$9y_w3ob*MUjjaN&@X`peASjY`G6;kdI=llip9b$-KT4J zU6_4BvTyIJ8r%kdL6I6wnwL!zV}OW_v?d4Vqvp}h?pr9iK$aKh@CrIYfewLDi%0se ztn+wcK}*?rmzu)LF?+4NvVruGt*36>n6A9OxSEU}gc^!dv?cxBW$^O=FDBTJMH%}J zbr5Y}Q4Ra%D(w>604u>_Pxf}#w^&ZJ3|6p1hZ4JA-F;-w?!8958RnS!HIOQ}Xu&P$D5@Bw!Sz=-LFygH{Ys(BU>)1 zc=S6xuY<<2)0e=Q)6l$l$)5q>xyEt0~L!MbB z&E&;sKyg(5<;t&s#Y;=q-8@l?8z*5y2)N|y2M@-$4mW6rxj$Q*WQFpx;}&j@=wyhU zi$}0cAl}U81C814nYZqI&2h)~XHGpgaPb9}U<$s+)xD!7lq^s}(j)@`1GiKdjF5N$ zcbZ_R1K=3>_r}JJ>o6ZZ5wqeE4bBm6Nq@)GlFkeu@ze?ipdLKK!^IQ9urPG7cW7w- zv*m2~-c51tPElFT&Ro9_=}g-#@`Y*Mmc~wK&Z3poCPUw3&iqWhzpT<>fsAZ5*}=mZ zK?>?2G@G-t#eCWw6i7f$bAOcWnE!C5VAu0D7;5`7c|i|mTq>XbmC^3fsyp{cEog@T zeY?ce(R*>EM9>2Xi;%omuAl4a_JNqC z!Y%3VJb)?>Z8Dtln@3L&d2}?PV~L(revybJf(gEnQnq+`N}pZCDbCs{+MwW(2# zrSq(;G|e0Gc8)f5I&~Ft6Y>-9t#=d_Z0~^6B1qV6o8C261sYG)c!iS3^-3Tp87S19 z?6LU|ClgsSZ-Xs^Gn3bb(ZI{g7k{O3Y3W7x)IGB)?I0eEG7D!?d_X9cpkrN`=rIWV z{30Hkt!Hx!^DQti(g2b|Sw??H(g-xw+;LaUov(pGQ?2W@cPF?+!03oQpi7IIAsvw%Asf(Oug{q*C6 zanJbzHNFJ~oT%-*}90T;)|M3Kkk&4UBKHM2CEZma*jtMRV zi3C09XCx>9#6yspC6a2cSS%voER*Jodp~3kod>`HqAbmbJ;= zRhHv*lMF!V6mLp99DU}yP;)0}cW2E#wI@ej>8AovwcewDD#5GJ6@<*F6e=f(HV8iy zakPHu>3Hh;%a>o8BR1MrLHhtK71-!97!g}t7(=sv>g0m+bXw05I*v$cAA zQHL7YXE3lf73=$k^z5Mv%K}NoWF~JaV^Y|y->qCq{0ZVtU-F@rhq01PErshmNMBx~!cs7GC@)GE_vVyd`Oxj{js)WBK{T*~V=^H?90=-Qs zh~DBIZIyj}eFUabXo7jAAr_CFom~u-JzH;4NN%D;nC9(>M#1jFYtnh`>}@qI-8JI6EJ&FUcr~w%)i`X>VrXn5AI<-L{cE_LUvYU{sgbtPWKw z~k<{wax!?YL{h6Bf_-%I$x|#6`suS(IZ#`owcU$tf zhuxUmQYUl~Vjho`A9T-&dE!u`MT>7HP2f%Ucff1I$@Y6+uelJv)YjZPOh%x4 z>m0c8*I$2q_r=c#-9fq8ZOl8ckBUSf?rUGCaJcxFK&`FL;p-w*_AquQY2#6v-_5>_{_||giTnI=535NGY21SP}f}-sFXWc zEg-pgif4}TvU|F zfDy-W+WLOsbX2atvAN}ihue)o*Jf-Vsx^8gxy8O{RDvQrgof#@ z>v~$`tVQW;#(7CJI8yfPsAyvb#36s^0FK!%wL!O{d47*=~OLjRzh`uJM6%dN(>Z@`{^J~?P!r4p0Td{B<1 z*@S>cfIu@(mk4))`!=L^Dd4iJ=}7#$B31tZyNxiKvfONUJ39^zt_wS9jXQc~`~3N# z={&c2`au`CLkZHZuCm-+eQ6xIuc~dTb!6Dr+IV7w34uGw2kwer2A!Q5VJda7aPC0?L;%P${%dat43i*wf& z6ikxI?aGT(N{I3rZ901&ef{aDf7~5<=UEW)Dn4L%}Hk>J#6A!NjEUxXCK?4j{fh>JpA(mNtWlM>-!zCf zsQu=zevqY=$8Eg8OY0jR=j^0D<4I%iqn|&WfBs(X-pp)fSLg0X{5v;59PwfXfK2Qn zFX7gF3KYh1mkbLoDio(vD`UfQ{-!H44_pFwI&*!YAJizS1j~aamK5{kkc$SRh(%P&^-zEZL6}nq$0A;MV*Uv?u^l3B{yHeA zPF1i?m^vs$jeNMj=Y&baCBGOVJ-;f$D!G9cl26}%>466>z46BVo)zj4UA1Mogt~Y(aI&wZ zsYRB(NuST*3x`M#W$g9)$K5)0V}b@_?ce=76oET` z@AQPhZ+$5ep(6!Rz11oPXJOBp=1=YX^yTAGSSO16!faF}PY|?DRXytCXSTR&B()*2AG5S(&MEH|sIBo^=*2WtD%JWGw6J5P8S+MSDfN7MwW#1eGsz!mf_N4Uc=no6L;q%o4t zqO?*juXCqjBOXBp-H&EN3`&G)dmp^|0Ttz*3C{&*l`(-*9@)uyobv|<&nh1Dm90MW z!X*maDJ65IB)7B39qiAGev>x{uVZ;%I-{hZtDVRII5A#!gz2W62KmSms!#tE) z#m^BSSWu%^+=h46AYghVFQ~|`G#-h6Cu$f*KsrovjlHt^VXIf%qz2ubYVcn>I6R*s zZQu%f;r8TIb8C~GF4maf&RCX<_Yevp&gX5v;tKC%j-~nnjVF#DbLFC}Zv;1!(=?*8*?a21K%rH=S0OR5gOR$G8?0+HzFmcRFrH5ThWlsCrn})#gUDjmDc) zP5-2<$=pe=`Y5*@ModbszrD}vlLaygt#=U{EoHe_3e&;(PI--u?IJ$0J-LQ&qmQ)m zFiB(#p-~mgkw}+}7cD(}U4+)lD&>-UOfCk%q7e%7%X?PC*)jcADwlahRhXIz23ZyR z@l0~G(`0>4Q&Kf$J^GTPe8a($NC_NV$7V@Lf+NM;xxSAh53N+oi< zV_V<}(*Uo7Xq~>Xh}aH|78JQ)t5nLR$^gT{;_jxr<Vmo}}Lg@gAmogp%DV)(TCRPFiW)`rgGo%hb% zU(19K>dof=%`5SmtmK0VU$=dw0yZA zHOG`o^VeaoG+xtLHQ*ifqz0)}k-M_oHZQ|*j+1Re?M;?8qov8{ogVRd(F@H622{Qb zVv_JBb5rAvC0mI+pi*v$Hv)<-ENv_@d-*XCM|43!uL(T~7i2PlR7y(u{sx;qQlmPr zzHT{R}s2=mGAF4-TI-&2q_rv=p<%BvI2kL@h%|a1*PM z!o7^>=-9e9f-(sHKmEJv?uq((n8vQ+C;74A+v95p(ye615mN0#;mA|{(yhb8Sk&_( zEaz_Sc8UzPjyj#A9qpIb_l<-oy9-zWES8$;?jBvP*T-SiV57I-@QwQzF5npVjytka zcumY2dAI`zhnPexeMudu2K<9`8EEB|a?$CkbIJnr+WOK)d~>S9t@o!p-OdD?i<038 z;@(RI@tSLT(=HH+-?IKX|5ku^fyv?+KuInUtkvA97Y+{mjKWRLCKJ34DtCIrmf4Zm zut>AHY_t%C?fbWfj}2|{{9-;CooPO{sPcftLhkSXq<3|lH3SBO6SFeIi-=kPz-2K5 znNY&?@WM?uYo92esh;R)8aNfC$;Sntn1O4I)otS^^EAe2by$p@-4jK*SKV~|u`b^g zCnYT87l6@878{ndHO2y0CUS>N*$%mKW61RoQ5_(_rq6?@lxt^ML{=YpVhg*N-nM@4 zp5&ZFH7*uH(H!7}Fo+YSu-LxS5zIv=iBx`P03FPFI)*KT1z%yUHo#Qi8 z@1BP7s{!xuZ4OaUelq$`h!sfd zNgC55iU%v@5*(fp72}JT03BOj4o3-HVJ;+saVQ-QYy71-N8b(vL6Vqr1oHD2?BqD< z=|e2Z?Sx=(Z)2rQ<{4&i?Q*~2$rV%vh?r8tFD}P;RKYA6RAG5mKcFMWIwuqIOL#ai zBoaH`#l+HyT%xJA8n^fA%l{YddTJ(mdg{6jhV-hA^lnYe+m~^~JvCUv%LrjD2FpR7;0roWh zUrlr3V=sr&5u1RZ)vh`fgQx*%p ztux-{9qN0^c&z3`)yr2uAjQjw0lNrA=*!z8Y8%O*`jv7I&p|nf*(^@k%!cFuj$o-r z1f(R81*TvIW!~Wj7RDpCU1;S)2X1E0M zSiX=Zdx5@@i+y?g>#8g3@JN`=|Bv2P*L5Gi4)@|L_Ya)XQdx#wpO+^tZ|H0uV3Nr^ z<8a6v7vMIhwAIuJP(_VymX?rI{JEW6IN`28=Rd+*#ilexR* zo^$t{GnpFBooKaiA^mQK5JSijkZM3)3B9ONZuSs3LjDlQ3jD>~+3h$9L~;B{nkvi4 z^ox%ITLdXhP1|%+?M-9dl9hO2G;w3}(ik-w-3ycT!dK8sU%lVB4xhf4~{$ zaOU)!J+uq6DSLTw@!)Bq#Zz*zCZLBi;4z}+5hlZ+DqLY9ROBj7myWt1>W*D8m`*0ba!*qs!YtS}s-5OoLe$4z{#DtUHn4C0k z9RpXkb*VR5;j;5$FJ`F}{ifgm;zrnz2=U=1Q#}-UAnpimp+M2uqP4kS=J>&56nAQO zUp)Rw28{?O27s(4dxeWvh~VOL@=_w{PG+UKoIslTx6h)?l>L#%|K@eD05TxokN0R1* z8phfHwymNM;se#pB^4VGWf6lIM%w_IrecJ|L`oL0G|xdbAjNL1;Q?g_Grw@mUx-fO zT(gPZQLl(v%KVDlRPo6F7)l9|`hK^bYZZk$zNX2}cx=Y#x|!|(G)2~phU5FZ1C#L< z7O-tivl+hMv8L1Mcs#uttDGr-d*&g26-_%i(HM>sB;#Nb?DiDobb=BzhTzzA+J^ab z8fYh)ZBT9meNcjR6`JpB4pD}+)NX9b2v}>lUbnRqPoQ|Hd;~vI^64fMvN?PHaab!=WMi#Jrj!i z#Rw6*XdsL^C?UyfIt^#osik%u*}>8cm;FhCNp<{5>OEW zC^3B7(I}BQs2!!>*pf6keE5xUwKJiLc()VEHNTh8?GaA3O+D|s;2NHICA$%>@vmfo z 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 Eg5PjTRR910 literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/doc/src/geoflickr.qdoc b/examples/positioning/geoflickr/doc/src/geoflickr.qdoc new file mode 100644 index 0000000..de74a9d --- /dev/null +++ b/examples/positioning/geoflickr/doc/src/geoflickr.qdoc @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example geoflickr + \title GeoFlickr (QML) + \ingroup qtpositioning-examples + + \brief The GeoFlickr example shows how to use the user's current position to + fetch local content from a web service. + + This is a small example, illustrating one of the very core parts of the + \l{Qt Positioning} API: the ability to retrieve and use the user's current + geographic position. + + Key QML types shown in this example: + \list + \li \l{QtPositioning::PositionSource}{PositionSource} + \li \l{XmlListModel}{XmlListModel} + \endlist + + \image qml-flickr-1.jpg + + \include examples-run.qdocinc + + \section1 Retrieving the Current Position + + Retrieving the user's current position is achieved using the PositionSource + type. In this example, we instantiate the PositionSource as part of the + GeoTab component (the floating "window" describing current position and + status). + + \snippet geoflickr/flickrmobile/GeoTab.qml possrc + + When the "Locate and update" button is pressed, we first interrogate the + PositionSource to check if it has an available backend for positioning + data. If it does not, we fall back to using a pre-recorded NMEA log + for demonstration. We then instruct the PositionSource to update. + + \snippet geoflickr/flickrmobile/GeoTab.qml locatebutton-top + \snippet geoflickr/flickrmobile/GeoTab.qml locatebutton-clicked + + To share the new position data with the rest of the application, we use + properties that we have created on the GeoTab component: + + \snippet geoflickr/flickrmobile/GeoTab.qml props + + \section1 Using the Current Position + + The longitude and latitude values retrieved here are eventually set on + in properties on the RestModel component. The RestModel is an XmlListModel, + which retrieves XML data from a URL and creates a data model by performing + XPath queries on it. + + In this case, it retrieves data from the Flickr REST API online, based on + our current position + + \snippet geoflickr/flickrcommon/RestModel.qml restmodel + + This model data is then shown in a variety of Qt Quick views to produce + the example application. + +*/ diff --git a/examples/positioning/geoflickr/flickr-90.qml b/examples/positioning/geoflickr/flickr-90.qml new file mode 100644 index 0000000..e335274 --- /dev/null +++ b/examples/positioning/geoflickr/flickr-90.qml @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + width: 480; height: 320 + + Loader { + y: 320; rotation: -90 + transformOrigin: Item.TopLeft + source: "flickr.qml" + } +} diff --git a/examples/positioning/geoflickr/flickr.qml b/examples/positioning/geoflickr/flickr.qml new file mode 100644 index 0000000..45cfb44 --- /dev/null +++ b/examples/positioning/geoflickr/flickr.qml @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 +import "flickrcommon" as Common +import "flickrmobile" as Mobile + +Item { + id: screen; width: 320; height: 480 + property bool inListView : false + + Rectangle { + id: background + anchors.fill: parent; color: "#343434"; + + Image { source: "flickrmobile/images/stripes.png"; fillMode: Image.Tile; anchors.fill: parent; opacity: 0.3 } + + Common.RestModel { + id: restModel + coordinate: geoTab.coordinate + } + + Item { + id: views + x: 2; width: parent.width - 4 + anchors.top: titleBar.bottom; anchors.bottom: toolBar.top + + Text { + text: qsTr("Network error") + font.pixelSize: 48 + fontSizeMode: Text.HorizontalFit + anchors.centerIn: parent + width: parent.width * 0.9 + visible: restModel.status === XmlListModel.Error + + } + + Mobile.GridDelegate { id: gridDelegate } + GridView { + x: (width/4-79)/2; y: x + id: photoGridView; model: restModel; delegate: gridDelegate; cacheBuffer: 100 + cellWidth: (parent.width-2)/4; cellHeight: cellWidth; width: parent.width; height: parent.height - 1; z: 6 + } + Mobile.ListDelegate { id: listDelegate } + ListView { + id: photoListView; model: restModel; delegate: listDelegate; z: 6 + width: parent.width; height: parent.height; x: -(parent.width * 1.5); cacheBuffer: 100; + } + states: State { + name: "ListView"; when: screen.inListView == true + PropertyChanges { target: photoListView; x: 0 } + PropertyChanges { target: photoGridView; x: -(parent.width * 1.5) } + } + + transitions: Transition { + NumberAnimation { properties: "x"; duration: 500; easing.type: Easing.InOutQuad } + } + } + Mobile.ImageDetails { id: imageDetails; width: parent.width; anchors.left: views.right; height: parent.height; z:1 } + Mobile.TitleBar { id: titleBar; z: 5; width: parent.width; height: 40; opacity: 0.9 } + Mobile.GeoTab { + id: geoTab; + x: 15; y:50; + } + Mobile.ToolBar { + id: toolBar; z: 5 + height: 40; anchors.bottom: parent.bottom; width: parent.width; opacity: 0.9 + button1Label: "Update"; button2Label: "View mode" + onButton1Clicked: restModel.reload() + onButton2Clicked: if (screen.inListView == true) screen.inListView = false; else screen.inListView = true + } + Connections { + target: imageDetails + onClosed: { + if (background.state == "DetailedView") { + background.state = ''; + imageDetails.photoUrl = ""; + } + } + } + + states: State { + name: "DetailedView" + PropertyChanges { target: views; x: -parent.width } + PropertyChanges { target: geoTab; x: -parent.width } + PropertyChanges { target: toolBar; button1Label: "More..." } + PropertyChanges { + target: toolBar + onButton1Clicked: if (imageDetails.state=='') imageDetails.state='Back'; else imageDetails.state='' + } + PropertyChanges { target: toolBar; button2Label: "Back" } + PropertyChanges { target: toolBar; onButton2Clicked: imageDetails.closed() } + } + + transitions: Transition { + NumberAnimation { properties: "x"; duration: 500; easing.type: Easing.InOutQuad } + } + } +} diff --git a/examples/positioning/geoflickr/flickr.qrc b/examples/positioning/geoflickr/flickr.qrc new file mode 100644 index 0000000..1019f7d --- /dev/null +++ b/examples/positioning/geoflickr/flickr.qrc @@ -0,0 +1,30 @@ + + + flickr.qml + flickr-90.qml + flickrcommon/Progress.qml + flickrcommon/RestModel.qml + flickrcommon/ScrollBar.qml + flickrcommon/Slider.qml + flickrmobile/Button.qml + flickrmobile/GeoTab.qml + flickrmobile/GridDelegate.qml + flickrmobile/ImageDetails.qml + flickrmobile/ListDelegate.qml + flickrmobile/nmealog.txt + flickrmobile/TitleBar.qml + flickrmobile/ToolBar.qml + flickrmobile/images/gloss.png + flickrmobile/images/lineedit.png + flickrmobile/images/lineedit.sci + flickrmobile/images/moon.png + flickrmobile/images/quit.png + flickrmobile/images/star.png + flickrmobile/images/stripes.png + flickrmobile/images/sun.png + flickrmobile/images/titlebar.png + flickrmobile/images/titlebar.sci + flickrmobile/images/toolbutton.png + flickrmobile/images/toolbutton.sci + + diff --git a/examples/positioning/geoflickr/flickrcommon/Progress.qml b/examples/positioning/geoflickr/flickrcommon/Progress.qml new file mode 100644 index 0000000..f8388b5 --- /dev/null +++ b/examples/positioning/geoflickr/flickrcommon/Progress.qml @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + property variant progress: 0 + + Rectangle { + anchors.fill: parent; smooth: true + border.color: "white"; border.width: 0; radius: height/2 - 2 + gradient: Gradient { + GradientStop { position: 0; color: "#66343434" } + GradientStop { position: 1.0; color: "#66000000" } + } + } + + Rectangle { + y: 2; height: parent.height-4; + x: 2; width: Math.max(parent.width * progress - 4, 0); + opacity: width < 1 ? 0 : 1; smooth: true + gradient: Gradient { + GradientStop { position: 0; color: "lightsteelblue" } + GradientStop { position: 1.0; color: "steelblue" } + } + radius: height/2 - 2 + } + + Text { + text: Math.round(progress * 100) + "%" + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: "white"; font.bold: true + } +} diff --git a/examples/positioning/geoflickr/flickrcommon/RestModel.qml b/examples/positioning/geoflickr/flickrcommon/RestModel.qml new file mode 100644 index 0000000..ad9f4dc --- /dev/null +++ b/examples/positioning/geoflickr/flickrcommon/RestModel.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtQuick.XmlListModel 2.0 + +//! [restmodel] +XmlListModel { + property variant coordinate + + source: "https://api.flickr.com/services/rest/?" + + "min_taken_date=2000-01-01+0:00:00&" + + "extras=date_taken&" + + "method=flickr.photos.search&" + + "per_page=30&" + + "sort=date-taken-desc&" + + "api_key=e36784df8a03fea04c22ed93318b291c&" + + "lat=" + coordinate.latitude + "&lon=" + coordinate.longitude; + query: "/rsp/photos/photo" + + XmlRole { name: "title"; query: "@title/string()" } + XmlRole { name: "datetaken"; query: "@datetaken/string()" } + XmlRole { name: "farm"; query: "@farm/string()" } + XmlRole { name: "server"; query: "@server/string()" } + XmlRole { name: "id"; query: "@id/string()" } + XmlRole { name: "secret"; query: "@secret/string()" } +} +//! [restmodel] diff --git a/examples/positioning/geoflickr/flickrcommon/ScrollBar.qml b/examples/positioning/geoflickr/flickrcommon/ScrollBar.qml new file mode 100644 index 0000000..f961468 --- /dev/null +++ b/examples/positioning/geoflickr/flickrcommon/ScrollBar.qml @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: container + + property variant flickableArea + + Rectangle { + radius: 5 + color: "black" + opacity: 0.3 + border.color: "white" + border.width: 2 + x: 0 + y: flickableArea.visibleArea.yPosition * container.height + width: parent.width + height: flickableArea.visibleArea.heightRatio * container.height + } + states: [ + State { + name: "show" + when: flickableArea.movingVertically + PropertyChanges { + target: container + opacity: 1 + } + } + ] + transitions: [ + Transition { + from: "*" + to: "*" + NumberAnimation { + target: container + properties: "opacity" + duration: 400 + } + } + ] +} diff --git a/examples/positioning/geoflickr/flickrcommon/Slider.qml b/examples/positioning/geoflickr/flickrcommon/Slider.qml new file mode 100644 index 0000000..f323ab2 --- /dev/null +++ b/examples/positioning/geoflickr/flickrcommon/Slider.qml @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: slider; width: 400; height: 16 + + // value is read/write. + property real value + onValueChanged: { handle.x = 2 + (value - minimum) * slider.xMax / (maximum - minimum); } + property real maximum: 1 + property real minimum: 1 + property int xMax: slider.width - handle.width - 4 + + Rectangle { + anchors.fill: parent + border.color: "white"; border.width: 0; radius: 8 + gradient: Gradient { + GradientStop { position: 0.0; color: "#66343434" } + GradientStop { position: 1.0; color: "#66000000" } + } + } + + Rectangle { + id: handle; smooth: true + x: slider.width / 2 - handle.width / 2; y: 2; width: 30; height: slider.height-4; radius: 6 + gradient: Gradient { + GradientStop { position: 0.0; color: "lightgray" } + GradientStop { position: 1.0; color: "gray" } + } + + MouseArea { + anchors.fill: parent; drag.target: parent + drag.axis: Drag.XAxis; drag.minimumX: 2; drag.maximumX: slider.xMax+2 + onPositionChanged: { value = (maximum - minimum) * (handle.x-2) / slider.xMax + minimum; } + } + } +} diff --git a/examples/positioning/geoflickr/flickrmobile/Button.qml b/examples/positioning/geoflickr/flickrmobile/Button.qml new file mode 100644 index 0000000..5f2fcba --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/Button.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: container + + signal clicked + + property string text + + BorderImage { + id: buttonImage + source: "images/toolbutton.sci" + width: container.width; height: container.height + } + BorderImage { + id: pressed + opacity: 0 + source: "images/toolbutton.sci" + width: container.width; height: container.height + } + MouseArea { + id: mouseRegion + anchors.fill: buttonImage + onClicked: { container.clicked(); } + } + Text { + color: "white" + anchors.centerIn: buttonImage; font.bold: true + text: container.text; style: Text.Raised; styleColor: "black" + } + states: [ + State { + name: "Pressed" + when: mouseRegion.pressed == true + PropertyChanges { target: pressed; opacity: 1 } + } + ] +} diff --git a/examples/positioning/geoflickr/flickrmobile/GeoTab.qml b/examples/positioning/geoflickr/flickrmobile/GeoTab.qml new file mode 100644 index 0000000..59dacc2 --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/GeoTab.qml @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtPositioning 5.2 + +Rectangle { + id: container + property int maxX: parent.width; property int maxY: parent.height +//! [props] + property variant coordinate +//! [props] + + Binding { + target: container + property: "coordinate" + value: positionSource.position.coordinate + } + + width: 300; height: 130 + color: "blue" + opacity: 0.7 + border.color: "black" + border.width: 1 + radius: 5 + gradient: Gradient { + GradientStop {position: 0.0; color: "grey"} + GradientStop {position: 1.0; color: "black"} + } + MouseArea { + anchors.fill: parent + drag.target: parent + drag.axis: Drag.XandYAxis + drag.minimumX: -(parent.width * (2/3)); drag.maximumX: parent.maxX - (parent.width/3) + drag.minimumY: -(parent.height/2); drag.maximumY: parent.maxY - (parent.height/2) + } +//! [locatebutton-top] + Button { + id: locateButton + text: "Locate & update" +//! [locatebutton-top] + anchors {left: parent.left; leftMargin: 5} + y: 3; height: 32; width: parent.width - 10 +//! [locatebutton-clicked] + onClicked: { + if (positionSource.supportedPositioningMethods === + PositionSource.NoPositioningMethods) { + positionSource.nmeaSource = "nmealog.txt"; + sourceText.text = "(filesource): " + printableMethod(positionSource.supportedPositioningMethods); + } + positionSource.update(); + } + } +//! [locatebutton-clicked] +//! [possrc] + PositionSource { + id: positionSource + onPositionChanged: { planet.source = "images/sun.png"; } + + onSourceErrorChanged: { + if (sourceError == PositionSource.NoError) + return + + console.log("Source error: " + sourceError) + activityText.fadeOut = true + stop() + } + + onUpdateTimeout: { + activityText.fadeOut = true + } + } +//! [possrc] + function printableMethod(method) { + if (method === PositionSource.SatellitePositioningMethods) + return "Satellite"; + else if (method === PositionSource.NoPositioningMethods) + return "Not available" + else if (method === PositionSource.NonSatellitePositioningMethods) + return "Non-satellite" + else if (method === PositionSource.AllPositioningMethods) + return "Multiple" + return "source error"; + } + + Grid { + id: locationGrid + columns: 2 + anchors {left: parent.left; leftMargin: 5; top: locateButton.bottom; topMargin: 5} + spacing: 5 + Text {color: "white"; font.bold: true + text: "Lat:"; style: Text.Raised; styleColor: "black" + } + Text {id: latitudeValue; color: "white"; font.bold: true + text: positionSource.position.coordinate.latitude; style: Text.Raised; styleColor: "black"; + } + Text {color: "white"; font.bold: true + text: "Lon:"; style: Text.Raised; styleColor: "black" + } + Text {id: longitudeValue; color: "white"; font.bold: true + text: positionSource.position.coordinate.longitude; style: Text.Raised; styleColor: "black" + } + } + Image { + id: planet + anchors {top: locationGrid.bottom; left: parent.left; leftMargin: locationGrid.anchors.leftMargin} + source: "images/moon.png" + width: 30; height: 30 + } + Text {id: sourceText; color: "white"; font.bold: true; + anchors {left: planet.right; leftMargin: 5; verticalCenter: planet.verticalCenter} + text: "Source: " + printableMethod(positionSource.supportedPositioningMethods); style: Text.Raised; styleColor: "black"; + } + + Text { + id: activityText; color: "white"; font.bold: true; + anchors { top: planet.bottom; horizontalCenter: parent.horizontalCenter } + property bool fadeOut: false + + text: { + if (fadeOut) + return qsTr("Timeout occurred!"); + else if (positionSource.active) + return qsTr("Retrieving update...") + else + return "" + } + + Timer { + id: fadeoutTimer; repeat: false; interval: 3000; running: activityText.fadeOut + onTriggered: { activityText.fadeOut = false; } + } + } +} diff --git a/examples/positioning/geoflickr/flickrmobile/GridDelegate.qml b/examples/positioning/geoflickr/flickrmobile/GridDelegate.qml new file mode 100644 index 0000000..5ed0db5 --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/GridDelegate.qml @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + import QtQuick 2.0 + import QtQuick.Window 2.0 + + Component { + id: photoDelegate + Item { + id: wrapper; width: 79; height: 79 + + function photoClicked() { + imageDetails.photoTitle = title; + imageDetails.photoDate = datetaken; + imageDetails.photoUrl = "http://farm" + farm + ".static.flickr.com/" + server + "/" + id + "_" + secret + ".jpg"; + console.log(imageDetails.photoUrl); + scaleMe.state = "Details"; + } + + Item { + anchors.centerIn: parent + scale: 0.0 + Behavior on scale { NumberAnimation { easing.type: Easing.InOutQuad} } + id: scaleMe + + Rectangle { height: 79; width: 79; id: blackRect; anchors.centerIn: parent; color: "black"; smooth: true } + Rectangle { + id: whiteRect; width: 76; height: 76; anchors.centerIn: parent; color: "#dddddd"; smooth: true + Image { id: thumb; + // source: imagePath; + source: imageDetails.photoUrl = "http://farm" + farm + ".static.flickr.com/" + server + "/" + id + "_" + secret + "_t.jpg" + width: parent.width; height: parent.height + x: 1; y: 1; smooth: true} + Image { source: "images/gloss.png" } + } + + Connections { + target: toolBar + onButton2Clicked: if (scaleMe.state == 'Details' ) scaleMe.state = 'Show' + } + + states: [ + State { + name: "Show"; when: thumb.status == Image.Ready + PropertyChanges { target: scaleMe; scale: Math.round(Screen.pixelDensity / 4) } + }, + State { + name: "Details" + PropertyChanges { target: scaleMe; scale: Math.round(Screen.pixelDensity / 4)} + ParentChange { target: wrapper; parent: imageDetails.frontContainer } + PropertyChanges { target: wrapper; x: 20; y: 60; z: 1000 } + PropertyChanges { target: background; state: "DetailedView" } + } + ] + transitions: [ + Transition { + from: "Show"; to: "Details" + ParentAnimation { + NumberAnimation { properties: "x,y"; duration: 500; easing.type: Easing.InOutQuad } + } + }, + Transition { + from: "Details"; to: "Show" + SequentialAnimation { + ParentAnimation { + NumberAnimation { properties: "x,y"; duration: 500; easing.type: Easing.InOutQuad } + } + PropertyAction { targets: wrapper; properties: "z" } + } + } + ] + } + MouseArea { anchors.fill: wrapper; onClicked: { photoClicked() } } + } + } diff --git a/examples/positioning/geoflickr/flickrmobile/ImageDetails.qml b/examples/positioning/geoflickr/flickrmobile/ImageDetails.qml new file mode 100644 index 0000000..58c924b --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/ImageDetails.qml @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "../flickrcommon" as Common + +Flipable { + id: container + + property variant frontContainer: containerFront + property string photoTitle: "" + property string photoDate + property string photoUrl + property variant prevScale: 1.0 + + signal closed + + transform: Rotation { + id: itemRotation + origin.x: container.width / 2; + axis.y: 1; axis.z: 0 + } + + front: Item { + id: containerFront; anchors.fill: container + + Rectangle { + anchors.fill: parent + color: "black"; opacity: 0.4 + } + + Column { + spacing: 10 + anchors { + left: parent.left; leftMargin: 20 + right: parent.right; rightMargin: 20 + top: parent.top; topMargin: 180 + } + Text { font.bold: true; color: "white"; elide: Text.ElideRight; text: container.photoTitle } + Text { color: "white"; elide: Text.ElideRight; text: "Published: " + container.photoDate } + } + } + + back: Item { + anchors.fill: container + + Rectangle { anchors.fill: parent; color: "black"; opacity: 0.4 } + + Common.Progress { + anchors.centerIn: parent; width: 200; height: 18 + progress: bigImage.progress; visible: bigImage.status != Image.Ready + } + + Flickable { + id: flickable; anchors.fill: parent; clip: true + contentWidth: imageContainer.width; contentHeight: imageContainer.height + + Item { + id: imageContainer + width: Math.max(bigImage.width * bigImage.scale, flickable.width); + height: Math.max(bigImage.height * bigImage.scale, flickable.height); + + Image { + id: bigImage; // source: container.photoUrl + source: container.photoUrl + scale: slider.value + anchors.centerIn: parent; smooth: !flickable.movingVertically + onStatusChanged : { + // Default scale shows the entire image. + if (status == Image.Ready && width != 0) { + slider.minimum = Math.min(flickable.width / width, flickable.height / height); + prevScale = Math.min(slider.minimum, 1); + slider.value = prevScale; + } + } + } + } + } + + Text { + text: "Image Unavailable" + visible: bigImage.status == Image.Error + anchors.centerIn: parent; color: "white"; font.bold: true + } + + Common.Slider { + id: slider; visible: { bigImage.status == Image.Ready && maximum > minimum } + anchors { + bottom: parent.bottom; bottomMargin: 65 + left: parent.left; leftMargin: 25 + right: parent.right; rightMargin: 25 + } + onValueChanged: { + if (bigImage.width * value > flickable.width) { + var xoff = (flickable.width/2 + flickable.contentX) * value / prevScale; + flickable.contentX = xoff - flickable.width/2; + } + if (bigImage.height * value > flickable.height) { + var yoff = (flickable.height/2 + flickable.contentY) * value / prevScale; + flickable.contentY = yoff - flickable.height/2; + } + prevScale = value; + } + } + } + + states: State { + name: "Back" + PropertyChanges { target: itemRotation; angle: 180 } + } + + transitions: Transition { + SequentialAnimation { + PropertyAction { target: bigImage; property: "smooth"; value: false } + NumberAnimation { easing.type: Easing.InOutQuad; properties: "angle"; duration: 500 } + PropertyAction { target: bigImage; property: "smooth"; value: !flickable.movingVertically } + } + } +} diff --git a/examples/positioning/geoflickr/flickrmobile/ListDelegate.qml b/examples/positioning/geoflickr/flickrmobile/ListDelegate.qml new file mode 100644 index 0000000..f83f345 --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/ListDelegate.qml @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + Component { + Item { + id: wrapper; width: wrapper.ListView.view.width; height: 86 + Item { + id: moveMe + Rectangle { color: "black"; opacity: index % 2 ? 0.2 : 0.4; height: 84; width: wrapper.width; y: 1 } + Rectangle { + x: 6; y: 4; width: 76; height: 76; color: "white"; smooth: true + + Image { + //source: imagePath; + source: "http://farm" + farm + ".static.flickr.com/" + server + "/" + id + "_" + secret + "_t.jpg" + width: parent.width; height: parent.height + x: 0; y: 0 } + + Image { source: "images/gloss.png" } + } + Column { + x: 92; width: wrapper.ListView.view.width - 95; y: 15; spacing: 2 + Text { text: title; color: "white"; width: parent.width; font.bold: true; elide: Text.ElideRight; style: Text.Raised; styleColor: "black" } + Text { text: datetaken; width: parent.width; elide: Text.ElideRight; color: "#cccccc"; style: Text.Raised; styleColor: "black" } + } + } + } +} diff --git a/examples/positioning/geoflickr/flickrmobile/TitleBar.qml b/examples/positioning/geoflickr/flickrmobile/TitleBar.qml new file mode 100644 index 0000000..883042a --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/TitleBar.qml @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: titleBar + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + + Item { + id: container + width: (parent.width * 2) - 55 ; height: parent.height + + Image { + id: quitButton + anchors.left: parent.left//; anchors.leftMargin: 0 + anchors.verticalCenter: parent.verticalCenter + source: "images/quit.png" + MouseArea { + anchors.fill: parent + onClicked: Qt.quit() + } + } + + Text { + id: categoryText + anchors { + left: quitButton.right; leftMargin: 10; rightMargin: 10 + verticalCenter: parent.verticalCenter + } + elide: Text.ElideLeft + text: "GeoFlickr (QML)" + font.bold: true; color: "White"; style: Text.Raised; styleColor: "Black" + } + } + + transitions: Transition { + NumberAnimation { properties: "x"; easing.type: Easing.InOutQuad } + } +} diff --git a/examples/positioning/geoflickr/flickrmobile/ToolBar.qml b/examples/positioning/geoflickr/flickrmobile/ToolBar.qml new file mode 100644 index 0000000..cb0a0fd --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/ToolBar.qml @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: toolbar + + property alias button1Label: button1.text + property alias button2Label: button2.text + signal button1Clicked + signal button2Clicked + + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } + + Button { + id: button1 + anchors.left: parent.left; anchors.leftMargin: 5; y: 3; width: 140; height: 32 + onClicked: toolbar.button1Clicked() + } + + Button { + id: button2 + anchors.right: parent.right; anchors.rightMargin: 5; y: 3; width: 140; height: 32 + onClicked: toolbar.button2Clicked() + } +} diff --git a/examples/positioning/geoflickr/flickrmobile/images/gloss.png b/examples/positioning/geoflickr/flickrmobile/images/gloss.png new file mode 100644 index 0000000000000000000000000000000000000000..dff2bd34718bce770ed746136f8976332111c23e GIT binary patch literal 889 zcmV-<1BU#GP)eTd}%-tK1b1D4>ySGqo)GjrzLvFHj= z6ours|8KvB5bhFPA3bXSq1ii_tVuw48*3Puc;)XPLEvq@0M!gpbZ1Ll1^l47D7vdi z`@@I+JGf&8-y<7EA2J)e*Kb)8I7LzPu`*^hYk{LE`s7KS8b=@dIEp?8r?nWqwK9sn zvw-muUwc|}_Y@;|?*mciRyY+zi+%{KdB%EZQq!Uz4K>fTb9m*V=%?x|v0k%@aA_1p zKj-OF4Vi~amlplvwVgWKu@Y^RJ$PT8-rb$((ynscUqru!wW1zG=X7b&?-q^SwcDCX zOeTt=KP*L~4qA)W5dG;8mpbY8r*WQ=!^cYXXDcBDq71VmgpZVqh#$%{MJPO zoUKQgEa5i4%F2|6Jt^(AGe>l~A>&}BIQOxZF_i^aEo?ap`Ar(lJM8EZyCMZ+SB29pGh zSg@hzWe6M#$7mL#(BchidKseYIN$B98PBp5u9&iBm>7s)6M`~`R`WoGh%sv>M^Owx zc`ihUiK;@M6*JlaXsvR~ZX;=hHZd`MB)~DVaLr??uW-eXB~zlHa~2a>wgA~7W-9Km+0YRW8uA|UW4I5BTM4~?*i`v?*i`vud&yE=VmBv+|22M P00000NkvXXu0mjfaetp8 literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/lineedit.png b/examples/positioning/geoflickr/flickrmobile/images/lineedit.png new file mode 100644 index 0000000000000000000000000000000000000000..a6afb5121a6f8f02491ce9fa220d9f8b6c0f31c0 GIT binary patch literal 1307 zcmV+$1?2jPP)^_M)OMqYwHdLJ>s}eN&%{;FDDU1^oxa7nPzf zf|n{76#7tuptM>qwY6%kY0}0t>Ez5g`}=&DOiXMwNlBU%`@!bG%sywZNPmU@yB5~XvhKv!26k>*eX&bbw5aXw&G#TbLCA|i}k z8f7#)N6$bH1z+HgV`usG{a=Q?E!s!7KC!vxy=C(31Wd&|ecvMJ3*;m7aD5HC*jIC@bW7P5Ig-Q0^y^q_fw-J0mj6kel zw5houE82_#qG*u0YpWrOL||gWgF7E!_uyWV$r+*^k1tf(Nzyug%&@zEC#Vp+-0V1` zzZa3St15$kvK(nl2r0Q-Ol?bzPe1yE_Wn-16qKwXbQZd~X=oG9`UMY4vl+b0EnCN` zx?~q=^<8j}D4GBshPtXa^+Ya*&qxsw>eJKYeXeO|nvjp$2uVtm%P$GQ)!16A-IodyiE^a&f9@g^NzD zgk`h4nq^mljWq?lp1@rd!^XPn3-G$v@IS#B>(;l1*FD3=7z6=p7QBqiqj*h#&EKk9 zS5?Jwoz)G%Hvs=H0AE)=yzUq_#^Q+=c-?wn*BH}0|MpiYT>j7{ndOr+G4IzOEFes*|GuptD@Dn(UGS#&L;j-B|1sH2&U z*y-tMthY=|O)&E12&puhdPbUwrL2)l&@_WansDUHBbW#pjRtn=iwR<%r@Xbo>xbXq z$gvR+AQ*y%t9FW(r2eak2q6SQ2y>BZ>oaQ{Si`}O4l?}XFokNK^g@Dj-o4kex%6Cl zbA{3AKREpPVMralr3&30T?m4~tjYp3#x%Xv+RLJ=pMU$6*WP`Vw?2HE(vDvGH&!`) z=oDr{dBY=Z1JVB4{)cKwd%}h9#u+~`PH(A~qAgOS0~=d(nRpiqz_Tp7ET(F)tO{e7 z&NJ@D=)I{#d25;Rqi31?qW*%Z4h)(!4G(v0E53Nwle=ndLj^P#M$e4Wn3_Q*QyOG= z?^iU63Yz`vea1jnUk`=u9Qi_#^Iu=!_@Se(6#5Gc0 zr98Q=HtI=(yw9^-MaUwHRW)2O7;H&o=HewTo|>XD7CtH6S$fJB{J3#O{sI^eqK)Qb RrHcRn002ovPDHLkV1hGOfVKbt literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/lineedit.sci b/examples/positioning/geoflickr/flickrmobile/images/lineedit.sci new file mode 100644 index 0000000..054bff7 --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/images/lineedit.sci @@ -0,0 +1,5 @@ +border.left: 10 +border.top: 10 +border.bottom: 10 +border.right: 10 +source: lineedit.png diff --git a/examples/positioning/geoflickr/flickrmobile/images/moon.png b/examples/positioning/geoflickr/flickrmobile/images/moon.png new file mode 100644 index 0000000000000000000000000000000000000000..1583ac83f7cfbfdca3f9c0818a2ff00e7585bef3 GIT binary patch literal 2366 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 literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/quit.png b/examples/positioning/geoflickr/flickrmobile/images/quit.png new file mode 100644 index 0000000000000000000000000000000000000000..aef73853e362dd6787ecffaa057f21adf7d1d456 GIT binary patch literal 1785 zcmV({j3*ryZagbwx(==k`U_8JXxI-RukX6@Fn9*<|uYPJ5Jmv`vw{d;O1w&?VP$K0pa zn8Sg&?gBgEu-kjfOUtykyGIJc)pu!kSJlzc5w7o&!|tFrt8ab^yRx#vkYD6%j^3TV zqXWEn1LHP^W8=KMyiD-fkzk)oX6a&SaY?`&1FBxFsjAheHc+?Iq1`&e+5x-zLs+lZ z8wKQLHk%a|q8v)Df_Cwgh2m{{tHg2FsZEuS_OGCxB$AN~tWyyH=TMxPE$iO8hc&b929q5(agq zg@t}PnSyUBR0U+IP^2PMC=^s-LzGG-YPZ|M!Zw?YeNBT61OhccUZqkg0m&~|#B(K0 zMNPvo7BDA!%E)14!D#HqVcFxOV=807ot+)Z=ko%Te-G7cHU$X4c=;5c&&QCIOeRUe zi}DJHm)jXCPrRw}c(&DSQ56=wE97^uNx&-bBeq07Cu@K?#6p%V%XD~nC}2${6B&(0 zipP_P5DFDxd0FOrvc$1d#`AIx&jYl{&tU0|2IIXTHCUs;(2qx>Vq>v2*q7MvZi=sQ z7(;v|o7G0`RtMHE;(8AMPeZ92_sSw37vVa{P-B6-hSeMT(MW`jx?N#K_Pv`*rvZ~n zQ)91zfWw}^&WVTx=G z#J|H){Ei~-R>Vd!=ndZx`pS<|n4-9lW@cutI0SX!vkdA7c+h{XfX@&4pn%xn>uc*Y zH#<)jN$NgY1&z8?nVp@bwY4>|u{Jk1X%kSsP2T{{Pk#LNd4046pYC>!$YQqA^z_W% zlP_ONj{<46SQMR3M``TlLcTzo-c13eS}-6K1T^YH;k-J&Yx-R>h##Q#PFi zWL0dIaA-?~1zb235`Xi~7uQ&!@5K>h29|Hk-@bEpm|NV66spbyBvBs%?>pI_-{F^7*;> zD`YTH9dY5EjH~w~w1l9J!Ksy#Mi9?nbKV^mR?GqNlG7chJXlrw#ro65_hmc%|4C=`rT0w<1i?VJXt+7_iVoa_}WjQZG z7XP1nFRbLnD4%*J(24dOcEA?OqFoqP^lTl544NNutN%f|(;oPIL+d zgsL_mOWfBJ?zvlUFG-f3)8P_54>w?Z<_{2~Kn{<^#l^10V!7gWMYSL|h8>JqA*a)I zXGjg!B3XO<35OdnZl8HW3>q=r9b$3;K!0*(Q#;ctxm<3tSghmu{)F_|bGzNbC+`ZW z#b%Q!Et$<=e)7Dx>i!T*9kOpGJtF)T9DSGdCYI3cx9J+}=Tw+(|20r_JVQ(&Ie~(WXHxMd(c4()0X~bUEaWAuh*L~4 z{Jfce-bDO+d^GY}tR)x`VN=nnt$k&zd-uES(1G4cbWwT_nKPGanNAY@YMZG|-MUiC zm%xRquF{FA2Xl`mD%*z3u6;v0PTiP$^Wx62D&cRSP^e5idG&1O$VfN2<_Nj5nS0m9 zvV_bJ*wJ_H!mfOUbpzg0+iJ0LBc(x$AGe{`ocpiW^NTY}$0xWvdVkRau+;zn002ov JPDHLkV1nwjbWZ>P literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/stripes.png b/examples/positioning/geoflickr/flickrmobile/images/stripes.png new file mode 100644 index 0000000000000000000000000000000000000000..75d2bf6c66ea20238f29bc6a086a31a5eecc4add GIT binary patch literal 159 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqX`U{QArhD8o;BoRRupM@X#YWC z&AhG~(G?Tk7NzVx{!C}(WsWwToQawCX>C!bqA#&neMnfkRqW1Q<;|IC>;n4?mzErn zP+wi?SU!JJ+@EuLGLzb7+U`79``e!BN>uf^N)M~+&)ps{&z2X9U-Do=3(!UePgg&e IbxsLQ0E~z{qyPW_ literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/sun.png b/examples/positioning/geoflickr/flickrmobile/images/sun.png new file mode 100644 index 0000000000000000000000000000000000000000..c5fd36ed39d42ce1f365f27e9637b3a0e1f3e73f GIT binary patch literal 8110 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 q(yB& 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

;0zh{wA6OyP`PpX?5+&HR6pc9-BAki5H0 zByU68bx5^Jdi<^wtwng<5@1Cse;J?uPDbxFhe;A8w{D#I@c?=4DTV&>DUezUvh%hm z`=n~`%u#jwlJseZipKrWd4HA>n;rECYTSfh#=Aw(^ocbeuZ zc0TC&nh#|m^6IaUly+VMw2DWYuzr>sJ4Y1dd+4P%;g3~<8yV6e5|t@so1Yh(HYg46 zpgnku#z{chc?jA=g4Bdmlg#iEAS#H6QlS7R4X~}iqKl}wp+@yo*v6>%8j$^wRPhMy zEHw$^|AqQ!%k>gx%OfWc7dyE0_Bx*aY!mFNF3)zeNZUq^m=H4j+(Jt9`yb)f?km4d z@NpeVrTKVeEtI_&GbcuvXjzB(d1kaz&nXW^5ZAaw!7+`K~grnCozxONw* z6m~8YuVd8lQZ24@&wri4xP!vXJlXfhgdOckM^E*IncVU^_KH$mzMSj;fEa zqGSCC@yfQD>u=Komjs^%;N7`)QYu_e%=G!SkNUM~R`PZ*`bk63;%8zzyH;O$^Kl{g%Ws~}4r7B^Bua%B| z>-G(>@|QnDXs63a#yhg9&g--${P-11=N=u4qRWI@3CJ~sY6Zp(71hUq%Q2NkRK(0& z4-nZSK?PA5iml`#bd!9%$4Quzw3*nM)w8UkHp}9&dS-Z*0N~&JNg?0;DaJCa{C=aP zPk+bB*PkPN4xlGC0c?JBTf1+mxlZCvd*I*m%v;+xr8cgjGB~Hd*UR{qp*RBR!I7$P z@2ra3r$EC?B=rXbvL+-!k9ow_10!q5Mqo8gi7Ez4s-$Q@6*I=5!ra=tNcqe$tW;L9 z)O!`7`Yc}jm8YFO$j=eevZFeJ<}qpO={~oZYh{e&rL2lYX}_ z!y9^`H==(6U;!3I&5^=Lx7xV`J%l7fz>c7?D_k2RE&6JU2N*^vNs{aS?v`k^fmm`* ze1XEG&^4y$$~&gYb@)fD?)FEh_b;Klai9G4Zyt{C`OAU6HvF*jfFQPw zu>etUrahB$TqAB2dwMYP1<`p)q6t7!IL)PzpseMTJr% zV?j0?0!9ONh<0#@XySe^f{~snp|Tt^1d1dhLu1$2bYSry<8kJ=l=1dTh`A*Ih{lZS z-evBt%zhjXGHbN@Ls}dksKQDFV_jUryAet&kg7+$NP}hNt;;Zi6r2_NYs&bdXJ}Vo zb&a6%OiE-n1P#t|m^8y0P_%CDNHj)zZczh(R=atK`@Qn|smjOcAoFA+hvM53B?MH= zS~taLZ$!WP4WQ94(ANQ^pp7bEg2;Ho-?nS|Sm}|mfV`7Lr+Hl&EaoNt^Ll|t14R|_ z-01adQC$N7H37xhP&YO{K3|Tozj}Gwdt*>=Xvo{A50|U|?L--e*Zx6MUi(K{{`>4* zXZQ|QLcjMU$+e|f9!2fL&t{Ye(BFt-$>ZCEmh3`(s1xRY;~)*7WQgdVpS|_#6_FR- zH@Of2!gjryk5v5*OWfa}sP6$LeQW_K3g~-%9R>i%J6|#%@E+#Ijk`geTG3Br^6Dq$ zLaVFy)+-_auH_#{Mvun`mCD_Rl=ru&(chM@RJm77`P*{+UjTy4T^PfT-T(jq07*qo IM6N<$f(!}_`Tzg` literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/titlebar.png b/examples/positioning/geoflickr/flickrmobile/images/titlebar.png new file mode 100644 index 0000000000000000000000000000000000000000..aa35c9e1b6583bbeb5b0b2ba070f236b36297eda GIT binary patch literal 1327 zcmV+~1000E`Nkl`f!%0rEEPTYqq|G_0m&4Ap+E=_4Pe8zLK6_*upKav37Z`-H{x7y3c#8H{EGoc5R3VI_Um%_ z{cq>z=Wuv<2-aF~j;AU6x)Gg{uVt*>)FaDuXZRLuJ%>YDeXWBeTsNj0yXWU;xV^oF zySuyJ%d)&N0l=RhKK$~>>FFti6Y%)>NNrIRG&F+n-jnGAJEqQ^-vaR(C*{06r?2Nx z+9=}=JN8=UtCWJv|W%Syrb)RaKA~+Y15*bPr6Em7}HGv0pYBVSnvU?<3M-!x;7~}0JfsSJ_GK74>&*pg^Q{TJf*L#xTlqQ|rgpLhP&7>7eVQo(;d}$q zva}p0Dc)ObN(wRt{^aBYa+J(^T?EH#n(!h%u-?4DbUJP5-pvM6?_(xB_2Wf)Dqm18 zm107J$XKR605P@qHJi>i^P|nP@Oj2cFeE<0s`sllP`*;n)_Rml#g*Y&89-oS zA`|t1T?ac;Ule&^M%osX@0|?v^cp|guX^)ErH3$lX(LC_Q#{-K<3ZY!Du1bix^B^h=)vUfe90@eZGAw8y&6-^pE#Wx_d zf@9sc+THHhc=&84V(?9r_bky!N@3v2gW5@-jdiU5c=^{ZlG^ehk)gTOAIV>gbun)NbT6A zcZ#G>CtrV=H@!<4>@#Lx&xW!Bz=F35{^zUB8qm<;RJ1>5_W2rzNWFMjh2qG_QhFcj z5lb*n6_KR&M|zO#rO@@E9X$wL;@`{BDa2S;WZl$v^SfkX?9OYc6A10_dx(6h2-?)L zLKnH@dg%*Ru>Yrwnw)qGa{Ya`uabZ5B;N}HU*E`*xB2qKfULEnkXHU0r06t)k;*P> zEBSIVbaL+nK`+o40IeR?s}sqcq2-k0`=%`GiDssyWk)$voY&W-pK2w|jQ lCTB{<{F8{3tU5o7_z#DajA}HOxLyDN002ovPDHLkV1k~ddi4MR literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/titlebar.sci b/examples/positioning/geoflickr/flickrmobile/images/titlebar.sci new file mode 100644 index 0000000..0418d94 --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/images/titlebar.sci @@ -0,0 +1,5 @@ +border.left: 10 +border.top: 12 +border.bottom: 12 +border.right: 10 +source: titlebar.png diff --git a/examples/positioning/geoflickr/flickrmobile/images/toolbutton.png b/examples/positioning/geoflickr/flickrmobile/images/toolbutton.png new file mode 100644 index 0000000000000000000000000000000000000000..11310013eedc808ca278e3068d7779e708422726 GIT binary patch literal 2550 zcmVe zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00~}6L_t(&-kn;@Ze&RjJrQ}U z%I)@yx>vMhM)Vd??@(_*@_Yb7zz49M58wmf14gXj57@F~Rl)u9qI_Ld^LrWu%xs>-#eH7mxJ+t3r+Iu8>W-a$yaW&t9&L`W+wBlS zfLl#pt~TpGtyZfqA$po-jn!)P$DjWACx19QJHykbPjPf~R4mp`GJ`_tnt(B$NSb8* zzQ_XH0$@3QS~Nt>JtdI!X60wP2}Fdu54Tva*SNgA#6MqugMWPW6^@UOzjlnD5dfT= zoc#X9ix;2&_Se6`#l;1#uCB2Ehi68#3$>GQ&EiUM!f+9^cl;4}c5EU-m|w1^6+#F& zK0e0jCqKliSFiB*FTTLXA3grv1OS`O=JT_&Gn}8F-8ETgyHgg=*R!L*lAJU z{d}G_7_K?rx!~xVyW<`}gnh^5si>^ZGTuyS(`M1OQK- zJi%tO!GCt&#n|)yL5pugLm^OmbDx@NlF)fEk><<7FboZW0t6@k(*1ymcxVtz(*#lg z>~M2)joaHB{OI{7`1arb`fLJ#^?IFS*aF2~Q>LCE71Q5IL$F-wUE-g~UqYuw-8W91xn+bx`P@ZMvZCNMK50N8G~P*pH9 zL^DqZ&ENt)LYaKi!;NQmGju%+0H$cKFZvjcX1 z3lfFb0OuH6e~;a63r9c*Ap$T>6TJ5j5$txmIfa;AP=S|S+yPBAj$oxZdvWW^Iga8uDf^wmPB@BB2s9<3e0NHtnC{zg|fO9SapsENVl)F#7d{_lx z-pvfaKzx?e2qF=uH4{adCkVLNp4_%?~t) zssKzS1E{~dcBd==y(}PTb9-<6EPnMkiUV1u74Wpq1j3YavSEM~xjDL{X3rBWDBq-> z<#R!#$?=6sL)*oQ4jItoebN65fQA9dSR|Tcb;siL5&)1=rR%hnCY8XlW7Txs2S9yq z0>A>#1BT^|;r6*U%nJZ|3@{&s&I^ZKU`=15OjQd~&kxk8G&4!G%Nngd3jkWw5uHpE zTE8-6K)Mh@i0G<9c`0x*wPo3Hi)5_megz#P>d zGxL=RL<$L!9Z94{MURcu-u1mZwpfV5?^^&0uOC9kjvofVUN1CY7d*fO<_XGa!O?dX zf~O3E191T9VoOx!Qh2aqDbFv(5YJLB(Cq5i_6u~3_ zwbCyDnC{Q~z#ImH9Be7Nq`|V2l%*Shs$cX-%JG%P28av*JeS*c9o7gn09Yqz84w3| zb2gJQ?adRETCugS_ytATwLZdBc$unVohIe{UT2{?1jH|FOJI8M;l1ytLGS$x0D06;<&KZf=^ zKY%!<#cH>~WXj~R2rdiYS@o08_Vcb{k)!l zS%IomIEvfYl$2q$^oEvSqYJt8Ub;xAvQZ7f=$f^{T^~S1nApJ)gE(4Rs=Ri5z0j3J zn$D49XHcjugx68ww7}X05jM&teE*hOT_2dK$1M0UOh0GN9i%v`kmmx6Dg0nD7!gpI3_$P_2Y zXj^4Hpb~@jABRyBv#xkv7@Q)ZX^GWhn9R`BTRahgC%42QakQgUs;|?9;zFSmM@zL% zV}$49@j^}607?MEnUtDLmgEhg>NvdsL1X{l?miLb6QL>F?fCeFO zsI4~A0h=mq2wEVgUjNPZ_SA?HcRhSr0ghPFZG_VDS?VlMl^)zw?WNM5^V=(e&bbVP zRz3Tk4%(EbBtv=*8={Q8u2YKVQik-7Z#x$-NEQ+htR;Wx&Yp9`fu<B652VFZWs1RLPz+Io`c{_x1PR z|MypJ{*KUJFE1~D@&5Yy`QyirvDs{LOy>%5{%%hECFmYHFNM)una#u=iB$nYl{xCd z>`+x%o@f%LY03<`ySu}iH*fIv?OUwZ>tEj9e)yIE;PmwL*>=1AaDTV`n{)0{=Nx!) z-Lt<3#d3W%kV<5&zUl_us};i&=sgCA%~?Pyr_&~;w%^s&Ri4EE09*nYhh&1)L;wH) M07*qoM6N<$g3{N*E&u=k literal 0 HcmV?d00001 diff --git a/examples/positioning/geoflickr/flickrmobile/images/toolbutton.sci b/examples/positioning/geoflickr/flickrmobile/images/toolbutton.sci new file mode 100644 index 0000000..9e4f965 --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/images/toolbutton.sci @@ -0,0 +1,5 @@ +border.left: 15 +border.top: 4 +border.bottom: 4 +border.right: 15 +source: toolbutton.png diff --git a/examples/positioning/geoflickr/flickrmobile/nmealog.txt b/examples/positioning/geoflickr/flickrmobile/nmealog.txt new file mode 100644 index 0000000..8c8286d --- /dev/null +++ b/examples/positioning/geoflickr/flickrmobile/nmealog.txt @@ -0,0 +1,1403 @@ +$GPGGA,222437.000,2734.33926,S,15305.44310,E,1,07,1.3,50.6,M,39.2,M,,*72 +$GPGLL,2734.33926,S,15305.44310,E,222437.000,A,A*49 +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222437.000,13.3,7.4,6.6,85.1,6.0,6.8,13.7*56 +$GPGSV,3,1,10,16,49,115,42,25,39,269,36,23,58,176,29,20,72,335,35*75 +$GPGSV,3,2,10,19,02,028,,04,06,241,22,13,30,223,30,27,19,284,35*78 +$GPGSV,3,3,10,11,06,337,30,03,13,055,25*7C +$GPRMC,222437.000,A,2734.33926,S,15305.44310,E,33.9,157.8,030308,11.2,W,A*0F +$GPVTG,157.8,T,169.0,M,33.9,N,62.9,K,A*22 +$GPGGA,222438.000,2734.34821,S,15305.44697,E,1,07,1.2,50.8,M,39.2,M,,*79 +$GPGLL,2734.34821,S,15305.44697,E,222438.000,A,A*4D +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222438.000,12.4,6.4,9.3,16.2,6.1,8.3,16.4*5F +$GPGSV,3,1,10,16,49,115,41,25,39,269,36,23,58,176,28,20,72,335,36*74 +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,28,27,19,284,35*73 +$GPGSV,3,3,10,11,06,337,28,03,13,055,25*75 +$GPRMC,222438.000,A,2734.34821,S,15305.44697,E,33.8,158.3,030308,11.2,W,A*0E +$GPVTG,158.3,T,169.5,M,33.8,N,62.5,K,A*2E +$GPGGA,222439.000,2734.35696,S,15305.45072,E,1,06,1.7,51.2,M,39.2,M,,*78 +$GPGLL,2734.35696,S,15305.45072,E,222439.000,A,A*43 +$GPGSA,A,3,16,25,23,20,13,27,,,,,,,3.3,1.7,2.8*3A +$GPGST,222439.000,10.3,9.1,12.2,44.6,9.8,9.9,25.2*62 +$GPGSV,3,1,10,16,49,115,34,25,39,269,36,23,58,175,29,20,72,335,35*77 +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,27,27,19,284,32*7B +$GPGSV,3,3,10,11,06,337,28,03,14,055,25*72 +$GPRMC,222439.000,A,2734.35696,S,15305.45072,E,33.2,158.7,030308,11.2,W,A*0E +$GPVTG,158.7,T,169.9,M,33.2,N,61.5,K,A*2F +$GPGGA,222440.000,2734.36580,S,15305.45446,E,1,07,1.3,52.0,M,39.2,M,,*76 +$GPGLL,2734.36580,S,15305.45446,E,222440.000,A,A*49 +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222440.000,13.0,8.0,13.4,6.2,7.4,12.2,20.9*64 +$GPGSV,3,1,10,16,49,115,40,25,39,269,38,23,58,175,31,20,72,335,34*72 +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,26,27,19,284,30*78 +$GPGSV,3,3,10,11,06,337,26,03,14,055,25*7C +$GPRMC,222440.000,A,2734.36580,S,15305.45446,E,33.7,159.1,030308,11.2,W,A*06 +$GPVTG,159.1,T,170.3,M,33.7,N,62.4,K,A*2D +$GPGGA,222441.000,2734.37483,S,15305.45825,E,1,07,1.3,52.7,M,39.2,M,,*7A +$GPGLL,2734.37483,S,15305.45825,E,222441.000,A,A*42 +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222441.000,14.0,7.6,14.1,17.6,7.7,12.5,21.0*51 +$GPGSV,3,1,10,16,49,115,41,25,39,269,39,23,58,175,29,20,72,335,35*7A +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,24,27,19,284,30*7A +$GPGSV,3,3,10,11,06,337,28,03,14,055,25*72 +$GPRMC,222441.000,A,2734.37483,S,15305.45825,E,34.6,159.4,030308,11.2,W,A*0E +$GPVTG,159.4,T,170.6,M,34.6,N,64.1,K,A*28 +$GPGGA,222442.000,2734.38407,S,15305.46216,E,1,06,1.3,53.3,M,39.2,M,,*77 +$GPGLL,2734.38407,S,15305.46216,E,222442.000,A,A*4B +$GPGSA,A,3,16,25,20,13,27,11,,,,,,,2.3,1.3,1.9*3C +$GPGST,222442.000,16.6,7.0,14.4,14.6,7.0,12.8,21.6*5A +$GPGSV,3,1,10,16,49,115,40,25,39,269,38,23,58,175,22,20,72,335,35*71 +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,25,27,19,284,29*73 +$GPGSV,3,3,10,11,06,337,27,03,14,055,25*7D +$GPRMC,222442.000,A,2734.38407,S,15305.46216,E,35.5,159.3,030308,11.2,W,A*02 +$GPVTG,159.3,T,170.5,M,35.5,N,65.8,K,A*26 +$GPGGA,222443.000,2734.39347,S,15305.46609,E,1,05,1.8,53.8,M,39.2,M,,*7D +$GPGLL,2734.39347,S,15305.46609,E,222443.000,A,A*42 +$GPGSA,A,3,16,25,20,27,11,,,,,,,,2.8,1.8,2.1*35 +$GPGST,222443.000,11.3,6.5,14.6,14.5,6.6,13.0,18.4*5A +$GPGSV,3,1,10,16,49,115,40,25,39,269,38,23,58,175,22,20,72,335,36*72 +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,26,27,19,284,31*79 +$GPGSV,3,3,10,11,06,337,28,03,14,055,25*72 +$GPRMC,222443.000,A,2734.39347,S,15305.46609,E,36.2,159.4,030308,11.2,W,A*08 +$GPVTG,159.4,T,170.6,M,36.2,N,67.0,K,A*2C +$GPGGA,222444.000,2734.40297,S,15305.47000,E,1,06,1.3,54.1,M,39.2,M,,*70 +$GPGLL,2734.40297,S,15305.47000,E,222444.000,A,A*49 +$GPGSA,A,3,16,25,20,13,27,11,,,,,,,2.3,1.3,1.9*3C +$GPGST,222444.000,17.6,6.3,12.7,14.4,6.3,11.4,16.2*55 +$GPGSV,3,1,10,16,49,115,38,25,39,269,38,23,58,175,22,20,72,335,35*7E +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,25,27,19,284,29*73 +$GPGSV,3,3,10,11,06,337,25,03,14,055,23*79 +$GPRMC,222444.000,A,2734.40297,S,15305.47000,E,36.5,159.5,030308,11.2,W,A*05 +$GPVTG,159.5,T,170.8,M,36.5,N,67.5,K,A*21 +$GPGGA,222445.000,2734.41247,S,15305.47390,E,1,07,1.3,54.2,M,39.2,M,,*75 +$GPGLL,2734.41247,S,15305.47390,E,222445.000,A,A*4E +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222445.000,16.0,7.0,14.4,10.4,6.7,13.0,20.7*52 +$GPGSV,3,1,10,16,49,115,36,25,39,269,36,23,58,175,22,20,72,335,34*7F +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,30,223,26,27,19,284,31*79 +$GPGSV,3,3,10,11,06,337,26,03,14,055,23*7A +$GPRMC,222445.000,A,2734.41247,S,15305.47390,E,36.6,159.7,030308,11.2,W,A*03 +$GPVTG,159.7,T,170.9,M,36.6,N,67.8,K,A*2C +$GPGGA,222446.000,2734.42201,S,15305.47790,E,1,07,1.3,54.4,M,39.2,M,,*75 +$GPGLL,2734.42201,S,15305.47790,E,222446.000,A,A*48 +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222446.000,13.0,7.4,12.3,6.6,6.9,11.2,17.9*60 +$GPGSV,3,1,10,16,49,115,36,25,39,269,37,23,58,175,27,20,72,335,35*7A +$GPGSV,3,2,10,19,02,028,,04,06,241,23,13,30,223,30,27,19,284,32*7E +$GPGSV,3,3,10,11,06,337,27,03,14,055,23*7B +$GPRMC,222446.000,A,2734.42201,S,15305.47790,E,36.6,159.3,030308,11.2,W,A*01 +$GPVTG,159.3,T,170.5,M,36.6,N,67.7,K,A*2B +$GPGGA,222447.000,2734.43157,S,15305.48195,E,1,07,1.3,54.3,M,39.2,M,,*7E +$GPGLL,2734.43157,S,15305.48195,E,222447.000,A,A*44 +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222447.000,10.7,6.7,10.5,6.6,6.2,9.6,15.5*5B +$GPGSV,3,1,11,16,49,115,32,25,39,269,37,23,58,175,28,20,72,335,33*76 +$GPGSV,3,2,11,19,02,028,,04,06,241,23,13,30,223,30,27,19,284,32*7F +$GPGSV,3,3,11,11,06,337,29,01,,,19,03,14,055,23*7D +$GPRMC,222447.000,A,2734.43157,S,15305.48195,E,36.7,159.1,030308,11.2,W,A*0E +$GPVTG,159.1,T,170.3,M,36.7,N,67.9,K,A*20 +$GPGGA,222448.000,2734.44111,S,15305.48610,E,1,08,1.1,54.1,M,39.2,M,,*71 +$GPGLL,2734.44111,S,15305.48610,E,222448.000,A,A*44 +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222448.000,20.4,10.5,8.8,52.5,8.7,9.1,15.2*6C +$GPGSV,3,1,11,16,49,115,27,25,39,269,38,23,58,175,26,20,72,335,31*71 +$GPGSV,3,2,11,19,02,028,,04,06,241,23,13,30,223,37,27,19,284,27*7C +$GPGSV,3,3,11,11,06,337,27,01,,,19,03,14,055,23*73 +$GPRMC,222448.000,A,2734.44111,S,15305.48610,E,36.8,158.7,030308,11.2,W,A*06 +$GPVTG,158.7,T,169.9,M,36.8,N,68.2,K,A*2E +$GPGGA,222449.000,2734.45068,S,15305.49044,E,1,08,1.1,53.6,M,39.2,M,,*78 +$GPGLL,2734.45068,S,15305.49044,E,222449.000,A,A*4D +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222449.000,17.1,8.8,10.2,14.2,8.2,9.3,17.4*6D +$GPGSV,3,1,11,16,49,115,28,25,39,269,37,23,58,175,25,20,72,335,28*7A +$GPGSV,3,2,11,19,02,028,,04,06,241,23,13,30,223,37,27,19,284,32*78 +$GPGSV,3,3,11,11,06,337,27,01,,,19,03,14,055,24*74 +$GPRMC,222449.000,A,2734.45068,S,15305.49044,E,37.2,157.8,030308,11.2,W,A*04 +$GPVTG,157.8,T,169.0,M,37.2,N,68.9,K,A*27 +$GPGGA,222450.000,2734.46041,S,15305.49485,E,1,08,1.1,53.3,M,39.2,M,,*74 +$GPGLL,2734.46041,S,15305.49485,E,222450.000,A,A*44 +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222450.000,17.4,8.9,12.7,10.1,11.5,8.2,16.5*5E +$GPGSV,3,1,11,16,49,115,25,25,39,269,36,23,58,175,30,20,72,336,28*71 +$GPGSV,3,2,11,19,02,028,,04,06,241,21,13,30,223,38,27,19,284,34*73 +$GPGSV,3,3,11,11,06,337,27,01,,,19,03,14,055,22*72 +$GPRMC,222450.000,A,2734.46041,S,15305.49485,E,37.7,157.9,030308,11.2,W,A*09 +$GPVTG,157.9,T,169.1,M,37.7,N,69.8,K,A*22 +$GPGGA,222451.000,2734.47033,S,15305.49924,E,1,08,1.1,53.1,M,39.2,M,,*75 +$GPGLL,2734.47033,S,15305.49924,E,222451.000,A,A*47 +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222451.000,14.1,8.0,10.5,14.3,9.5,7.5,15.0*61 +$GPGSV,3,1,11,16,49,115,27,25,39,269,38,23,58,175,28,20,72,336,25*79 +$GPGSV,3,2,11,19,02,028,,04,06,241,21,13,30,223,38,27,19,284,34*73 +$GPGSV,3,3,11,11,06,337,24,01,,,19,03,14,055,25*76 +$GPRMC,222451.000,A,2734.47033,S,15305.49924,E,38.1,158.1,030308,11.2,W,A*04 +$GPVTG,158.1,T,169.3,M,38.1,N,70.5,K,A*2B +$GPGGA,222452.000,2734.48022,S,15305.50375,E,1,08,1.1,52.5,M,39.2,M,,*7A +$GPGLL,2734.48022,S,15305.50375,E,222452.000,A,A*4D +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222452.000,24.1,13.9,9.7,80.6,12.7,9.0,21.0*54 +$GPGSV,3,1,11,16,49,115,29,25,39,269,38,23,58,175,27,20,72,336,30*7C +$GPGSV,3,2,11,19,02,028,,04,06,241,21,13,30,223,35,27,19,284,34*7E +$GPGSV,3,3,11,11,06,337,22,01,,,19,03,14,055,24*71 +$GPRMC,222452.000,A,2734.48022,S,15305.50375,E,38.3,157.9,030308,11.2,W,A*0B +$GPVTG,157.9,T,169.1,M,38.3,N,70.9,K,A*20 +$GPGGA,222453.000,2734.49019,S,15305.50802,E,1,06,1.7,52.1,M,39.2,M,,*75 +$GPGLL,2734.49019,S,15305.50802,E,222453.000,A,A*4E +$GPGSA,A,3,16,25,23,20,13,27,,,,,,,3.3,1.7,2.8*3A +$GPGST,222453.000,10.4,15.4,9.3,66.3,13.4,9.6,24.6*52 +$GPGSV,3,1,11,16,49,115,31,25,39,269,36,23,58,175,28,20,71,336,25*73 +$GPGSV,3,2,11,19,02,028,,04,06,241,21,13,30,223,33,27,19,284,31*7D +$GPGSV,3,3,11,11,06,337,22,01,,,19,03,14,055,24*71 +$GPRMC,222453.000,A,2734.49019,S,15305.50802,E,38.3,159.1,030308,11.2,W,A*0E +$GPVTG,159.1,T,170.3,M,38.3,N,70.9,K,A*2C +$GPGGA,222454.000,2734.50008,S,15305.51221,E,1,07,1.3,52.1,M,39.2,M,,*75 +$GPGLL,2734.50008,S,15305.51221,E,222454.000,A,A*4B +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222454.000,12.5,11.7,8.3,70.6,10.4,8.0,19.0*5E +$GPGSV,3,1,11,16,49,115,30,25,39,269,36,23,58,175,26,20,71,336,28*71 +$GPGSV,3,2,11,19,02,028,,04,06,241,21,13,30,223,32,27,19,284,30*7D +$GPGSV,3,3,11,11,06,337,24,01,,,18,03,14,055,24*76 +$GPRMC,222454.000,A,2734.50008,S,15305.51221,E,38.1,159.4,030308,11.2,W,A*0C +$GPVTG,159.4,T,170.6,M,38.1,N,70.5,K,A*22 +$GPGGA,222455.000,2734.50992,S,15305.51642,E,1,07,1.3,52.2,M,39.2,M,,*7C +$GPGLL,2734.50992,S,15305.51642,E,222455.000,A,A*41 +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222455.000,11.5,10.8,9.3,83.5,9.9,8.6,23.3*65 +$GPGSV,3,1,11,16,49,115,33,25,39,269,36,23,58,175,32,20,71,336,31*7F +$GPGSV,3,2,11,19,02,028,,04,06,241,21,13,30,223,29,27,19,284,29*7F +$GPGSV,3,3,11,11,06,337,28,01,,,18,03,14,055,24*7A +$GPRMC,222455.000,A,2734.50992,S,15305.51642,E,37.8,159.0,030308,11.2,W,A*04 +$GPVTG,159.0,T,170.2,M,37.8,N,70.0,K,A*21 +$GPGGA,222456.000,2734.51963,S,15305.52059,E,1,07,1.3,52.5,M,39.2,M,,*78 +$GPGLL,2734.51963,S,15305.52059,E,222456.000,A,A*42 +$GPGSA,A,3,16,25,23,20,13,27,11,,,,,,2.3,1.3,1.9*3D +$GPGST,222456.000,11.3,9.0,13.1,11.0,8.4,11.9,20.9*55 +$GPGSV,3,1,11,16,49,115,31,25,39,269,37,23,58,175,27,20,71,336,33*7A +$GPGSV,3,2,11,19,02,028,,04,06,241,19,13,30,223,29,27,19,284,32*7E +$GPGSV,3,3,11,11,06,337,30,01,,,18,03,14,055,24*73 +$GPRMC,222456.000,A,2734.51963,S,15305.52059,E,37.3,158.8,030308,11.2,W,A*05 +$GPVTG,158.8,T,170.0,M,37.3,N,69.1,K,A*28 +$GPGGA,222457.000,2734.52908,S,15305.52467,E,1,06,1.3,53.2,M,39.2,M,,*79 +$GPGLL,2734.52908,S,15305.52467,E,222457.000,A,A*44 +$GPGSA,A,3,16,25,20,13,27,11,,,,,,,2.3,1.3,1.9*3C +$GPGST,222457.000,20.4,7.8,12.0,8.1,7.3,10.9,17.9*63 +$GPGSV,3,1,11,16,49,115,37,25,39,269,37,23,58,175,24,20,71,336,35*79 +$GPGSV,3,2,11,19,02,028,,04,06,241,19,13,30,223,29,27,19,284,32*7E +$GPGSV,3,3,11,11,06,337,28,01,,,18,03,14,055,23*7D +$GPRMC,222457.000,A,2734.52908,S,15305.52467,E,36.2,158.7,030308,11.2,W,A*0C +$GPVTG,158.7,T,169.9,M,36.2,N,67.1,K,A*28 +$GPGGA,222458.000,2734.53845,S,15305.52866,E,1,06,1.6,54.2,M,39.2,M,,*70 +$GPGLL,2734.53845,S,15305.52866,E,222458.000,A,A*4F +$GPGSA,A,3,16,25,23,20,27,11,,,,,,,2.6,1.6,2.1*34 +$GPGST,222458.000,16.7,7.3,13.9,11.0,7.0,12.5,17.9*5D +$GPGSV,3,1,11,16,49,115,36,25,39,269,37,23,58,175,24,20,71,336,35*78 +$GPGSV,3,2,11,19,02,028,19,04,06,241,19,13,30,223,26,27,19,284,31*7A +$GPGSV,3,3,11,11,06,337,23,01,,,18,03,14,055,23*76 +$GPRMC,222458.000,A,2734.53845,S,15305.52866,E,35.9,159.1,030308,11.2,W,A*08 +$GPVTG,159.1,T,170.3,M,35.9,N,66.5,K,A*20 +$GPGGA,222459.000,2734.54772,S,15305.53309,E,1,08,1.1,55.6,M,39.2,M,,*72 +$GPGLL,2734.54772,S,15305.53309,E,222459.000,A,A*41 +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222459.000,17.5,6.5,9.3,15.5,6.1,8.3,15.2*5C +$GPGSV,3,1,11,16,49,115,38,25,39,268,36,23,58,175,35,20,71,336,35*76 +$GPGSV,3,2,11,19,02,028,19,04,06,241,19,13,30,223,33,27,19,284,34*7B +$GPGSV,3,3,11,11,06,337,29,01,,,18,03,14,055,22*7D +$GPRMC,222459.000,A,2734.54772,S,15305.53309,E,35.9,156.8,030308,11.2,W,A*00 +$GPVTG,156.8,T,168.0,M,35.9,N,66.4,K,A*2D +$GPGGA,222500.000,2734.55655,S,15305.53845,E,1,08,1.1,56.1,M,39.2,M,,*7D +$GPGLL,2734.55655,S,15305.53845,E,222500.000,A,A*4A +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222500.000,14.5,9.8,7.9,83.3,7.3,9.0,14.3*5C +$GPGSV,3,1,11,16,49,115,36,25,39,268,31,23,58,175,39,20,71,336,33*75 +$GPGSV,3,2,11,19,02,028,21,04,06,241,19,13,30,223,30,27,19,284,29*7F +$GPGSV,3,3,11,11,06,337,25,01,,,18,03,14,055,28*7B +$GPRMC,222500.000,A,2734.55655,S,15305.53845,E,35.9,151.6,030308,11.2,W,A*02 +$GPVTG,151.6,T,162.8,M,35.9,N,66.5,K,A*27 +$GPGGA,222501.000,2734.56495,S,15305.54489,E,1,08,1.1,57.0,M,39.2,M,,*7A +$GPGLL,2734.56495,S,15305.54489,E,222501.000,A,A*4D +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222501.000,14.3,8.0,10.9,27.0,8.0,9.5,15.6*64 +$GPGSV,3,1,11,16,49,115,41,25,39,268,31,23,58,175,36,20,71,336,31*78 +$GPGSV,3,2,11,19,02,028,21,04,06,241,19,13,30,223,27,27,19,284,29*79 +$GPGSV,3,3,11,11,06,337,22,01,,,18,03,14,055,31*74 +$GPRMC,222501.000,A,2734.56495,S,15305.54489,E,36.5,145.7,030308,11.2,W,A*0E +$GPVTG,145.7,T,156.9,M,36.5,N,67.5,K,A*2B +$GPGGA,222502.000,2734.57337,S,15305.55181,E,1,06,1.5,57.9,M,39.2,M,,*78 +$GPGLL,2734.57337,S,15305.55181,E,222502.000,A,A*4C +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222502.000,14.5,7.0,11.9,20.7,7.1,10.5,15.0*5F +$GPGSV,3,1,11,16,49,115,43,25,39,268,35,23,58,175,37,20,71,336,34*7A +$GPGSV,3,2,11,19,02,028,21,04,06,241,19,13,30,223,23,27,19,284,33*76 +$GPGSV,3,3,11,11,06,337,22,01,,,18,03,14,055,34*71 +$GPRMC,222502.000,A,2734.57337,S,15305.55181,E,37.4,143.8,030308,11.2,W,A*06 +$GPVTG,143.8,T,155.0,M,37.4,N,69.3,K,A*20 +$GPGGA,222503.000,2734.58184,S,15305.55887,E,1,08,1.1,58.5,M,39.2,M,,*7A +$GPGLL,2734.58184,S,15305.55887,E,222503.000,A,A*47 +$GPGSA,A,3,16,25,23,20,13,27,11,03,,,,,2.0,1.1,1.6*30 +$GPGST,222503.000,13.4,6.4,14.1,0.3,5.9,12.9,21.5*60 +$GPGSV,3,1,11,16,49,115,43,25,39,268,35,23,58,175,38,20,71,336,35*74 +$GPGSV,3,2,11,19,02,028,19,04,06,241,19,13,30,223,23,27,19,284,32*7C +$GPGSV,3,3,11,11,06,337,26,01,,,17,03,14,055,34*7A +$GPRMC,222503.000,A,2734.58184,S,15305.55887,E,37.8,143.4,030308,11.2,W,A*0D +$GPVTG,143.4,T,154.6,M,37.8,N,70.0,K,A*2C +$GPGGA,222504.000,2734.59032,S,15305.56580,E,1,07,1.2,58.9,M,39.2,M,,*79 +$GPGLL,2734.59032,S,15305.56580,E,222504.000,A,A*44 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222504.000,24.6,6.2,13.3,3.6,5.7,12.2,20.2*67 +$GPGSV,3,1,11,16,49,115,43,25,39,268,35,23,58,175,39,20,71,336,35*75 +$GPGSV,3,2,11,19,02,028,19,04,06,241,19,13,30,223,22,27,19,284,31*7E +$GPGSV,3,3,11,11,06,337,25,01,,,17,03,14,055,30*7D +$GPRMC,222504.000,A,2734.59032,S,15305.56580,E,37.6,143.6,030308,11.2,W,A*02 +$GPVTG,143.6,T,154.8,M,37.6,N,69.7,K,A*21 +$GPGGA,222505.000,2734.59874,S,15305.57271,E,1,06,1.5,59.4,M,39.2,M,,*70 +$GPGLL,2734.59874,S,15305.57271,E,222505.000,A,A*47 +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222505.000,17.4,5.5,11.8,3.4,5.0,10.8,17.3*61 +$GPGSV,3,1,11,16,49,115,44,25,39,268,35,23,58,175,39,20,71,336,36*71 +$GPGSV,3,2,11,19,02,028,21,04,06,241,19,13,30,223,24,27,19,284,31*73 +$GPGSV,3,3,11,11,06,337,,01,,,17,03,14,055,29*72 +$GPRMC,222505.000,A,2734.59874,S,15305.57271,E,37.3,143.7,030308,11.2,W,A*05 +$GPVTG,143.7,T,154.9,M,37.3,N,69.1,K,A*22 +$GPGGA,222506.000,2734.60703,S,15305.57943,E,1,07,1.2,60.1,M,39.2,M,,*75 +$GPGLL,2734.60703,S,15305.57943,E,222506.000,A,A*4B +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222506.000,16.1,6.2,11.7,15.3,6.1,10.4,17.4*54 +$GPGSV,3,1,11,16,49,115,43,25,39,268,34,23,58,175,37,20,71,336,36*79 +$GPGSV,3,2,11,19,02,028,21,04,06,241,,13,30,223,24,27,19,284,31*7B +$GPGSV,3,3,11,11,06,337,,01,,,17,03,14,055,28*73 +$GPRMC,222506.000,A,2734.60703,S,15305.57943,E,36.5,143.9,030308,11.2,W,A*00 +$GPVTG,143.9,T,155.1,M,36.5,N,67.6,K,A*2B +$GPGGA,222507.000,2734.61507,S,15305.58593,E,1,07,1.2,60.9,M,39.2,M,,*75 +$GPGLL,2734.61507,S,15305.58593,E,222507.000,A,A*43 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222507.000,14.0,6.8,11.8,10.9,6.4,10.7,18.5*54 +$GPGSV,3,1,11,16,49,115,43,25,39,268,34,23,58,175,37,20,71,336,34*7B +$GPGSV,3,2,11,19,02,028,21,04,06,241,,13,30,223,29,27,19,284,34*73 +$GPGSV,3,3,11,11,06,337,,01,,,17,03,14,055,29*72 +$GPRMC,222507.000,A,2734.61507,S,15305.58593,E,35.4,143.9,030308,11.2,W,A*0A +$GPVTG,143.9,T,155.1,M,35.4,N,65.5,K,A*28 +$GPGGA,222508.000,2734.62275,S,15305.59221,E,1,06,1.7,61.8,M,39.2,M,,*70 +$GPGLL,2734.62275,S,15305.59221,E,222508.000,A,A*42 +$GPGSA,A,3,16,25,23,20,13,27,,,,,,,3.3,1.7,2.8*3A +$GPGST,222508.000,12.5,9.3,16.3,19.5,9.4,14.4,27.5*51 +$GPGSV,3,1,11,16,49,115,43,25,39,268,33,23,58,175,38,20,71,336,31*76 +$GPGSV,3,2,11,19,02,028,21,04,06,241,,13,30,223,30,27,19,284,35*7A +$GPGSV,3,3,11,11,06,337,,01,,,17,03,14,055,28*73 +$GPRMC,222508.000,A,2734.62275,S,15305.59221,E,33.9,143.9,030308,11.2,W,A*00 +$GPVTG,143.9,T,155.1,M,33.9,N,62.9,K,A*28 +$GPGGA,222509.000,2734.63006,S,15305.59817,E,1,06,1.7,62.7,M,39.2,M,,*75 +$GPGLL,2734.63006,S,15305.59817,E,222509.000,A,A*4B +$GPGSA,A,3,16,25,23,20,13,27,,,,,,,3.3,1.7,2.8*3A +$GPGST,222509.000,10.4,8.1,14.2,21.9,8.4,12.3,23.7*52 +$GPGSV,3,1,11,16,49,115,44,25,39,268,32,23,58,175,37,20,71,336,29*76 +$GPGSV,3,2,11,19,02,028,21,04,06,241,,13,30,223,28,27,19,284,35*73 +$GPGSV,3,3,11,11,06,337,,01,,,17,03,14,055,30*7A +$GPRMC,222509.000,A,2734.63006,S,15305.59817,E,32.2,143.9,030308,11.2,W,A*03 +$GPVTG,143.9,T,155.1,M,32.2,N,59.7,K,A*24 +$GPGGA,222510.000,2734.63706,S,15305.60376,E,1,06,1.7,63.5,M,39.2,M,,*7F +$GPGLL,2734.63706,S,15305.60376,E,222510.000,A,A*42 +$GPGSA,A,3,16,25,23,20,13,27,,,,,,,3.3,1.7,2.8*3A +$GPGST,222510.000,12.4,8.3,12.9,28.0,8.7,11.0,21.6*57 +$GPGSV,3,1,11,16,48,115,43,25,39,268,32,23,58,175,37,20,71,336,29*70 +$GPGSV,3,2,11,19,02,028,20,04,06,241,,13,30,223,31,27,19,284,35*7A +$GPGSV,3,3,11,11,06,337,,01,,,17,03,14,055,25*7E +$GPRMC,222510.000,A,2734.63706,S,15305.60376,E,30.6,144.3,030308,11.2,W,A*01 +$GPVTG,144.3,T,155.5,M,30.6,N,56.7,K,A*24 +$GPGGA,222511.000,2734.64376,S,15305.60904,E,1,07,1.2,64.4,M,39.2,M,,*77 +$GPGLL,2734.64376,S,15305.60904,E,222511.000,A,A*48 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222511.000,14.6,5.7,10.5,2.7,5.2,9.6,15.0*5C +$GPGSV,3,1,11,16,48,115,44,25,39,268,32,23,58,175,37,20,71,336,28*76 +$GPGSV,3,2,11,19,02,028,20,04,06,241,23,13,30,223,29,27,19,284,36*71 +$GPGSV,3,3,11,11,06,337,,01,,,17,03,14,055,29*72 +$GPRMC,222511.000,A,2734.64376,S,15305.60904,E,29.1,144.2,030308,11.2,W,A*05 +$GPVTG,144.2,T,155.4,M,29.1,N,53.9,K,A*20 +$GPGGA,222512.000,2734.64992,S,15305.61405,E,1,07,1.2,65.4,M,39.2,M,,*78 +$GPGLL,2734.64992,S,15305.61405,E,222512.000,A,A*46 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222512.000,13.4,17.0,5.7,85.6,5.3,15.5,23.6*5A +$GPGSV,3,1,11,16,48,115,45,25,39,268,33,23,58,175,38,20,71,336,28*79 +$GPGSV,3,2,11,19,02,028,20,04,06,241,23,13,30,223,26,27,19,284,33*7B +$GPGSV,3,3,11,11,06,337,24,01,,,17,03,14,055,27*7A +$GPRMC,222512.000,A,2734.64992,S,15305.61405,E,27.2,143.6,030308,11.2,W,A*05 +$GPVTG,143.6,T,154.8,M,27.2,N,50.5,K,A*2C +$GPGGA,222513.000,2734.65572,S,15305.61884,E,1,07,1.2,66.2,M,39.2,M,,*7A +$GPGLL,2734.65572,S,15305.61884,E,222513.000,A,A*41 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222513.000,12.3,7.0,14.8,2.4,6.5,13.5,21.4*6D +$GPGSV,3,1,10,16,48,115,44,25,39,268,35,23,58,175,38,20,71,336,28*7F +$GPGSV,3,2,10,19,02,028,20,04,06,241,23,13,30,223,23,27,19,284,33*7F +$GPGSV,3,3,10,11,06,337,24,03,14,055,28*73 +$GPRMC,222513.000,A,2734.65572,S,15305.61884,E,25.8,143.6,030308,11.2,W,A*0A +$GPVTG,143.6,T,154.8,M,25.8,N,47.9,K,A*2E +$GPGGA,222514.000,2734.66155,S,15305.62364,E,1,06,1.5,67.0,M,39.2,M,,*7C +$GPGLL,2734.66155,S,15305.62364,E,222514.000,A,A*42 +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222514.000,25.5,6.1,14.4,3.1,5.6,13.2,19.2*6A +$GPGSV,3,1,10,16,48,115,44,25,39,268,33,23,58,175,38,20,71,336,26*77 +$GPGSV,3,2,10,19,02,028,18,04,06,241,23,13,30,223,23,27,19,284,34*73 +$GPGSV,3,3,10,11,06,337,28,03,14,055,29*7E +$GPRMC,222514.000,A,2734.66155,S,15305.62364,E,25.9,143.6,030308,11.2,W,A*08 +$GPVTG,143.6,T,154.8,M,25.9,N,48.0,K,A*29 +$GPGGA,222515.000,2734.66761,S,15305.62860,E,1,06,1.5,67.5,M,39.2,M,,*76 +$GPGLL,2734.66761,S,15305.62860,E,222515.000,A,A*4D +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222515.000,20.1,5.5,13.0,3.0,5.1,11.8,17.4*68 +$GPGSV,3,1,10,16,48,115,44,25,39,268,32,23,58,175,38,20,71,336,26*76 +$GPGSV,3,2,10,19,02,028,18,04,06,241,23,13,30,223,24,27,19,284,34*74 +$GPGSV,3,3,10,11,06,337,28,03,14,055,24*73 +$GPRMC,222515.000,A,2734.66761,S,15305.62860,E,26.9,143.7,030308,11.2,W,A*05 +$GPVTG,143.7,T,154.9,M,26.9,N,49.8,K,A*23 +$GPGGA,222516.000,2734.67384,S,15305.63376,E,1,06,1.5,68.2,M,39.2,M,,*7E +$GPGLL,2734.67384,S,15305.63376,E,222516.000,A,A*4D +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222516.000,16.4,5.8,11.5,4.5,5.3,10.5,17.3*6A +$GPGSV,3,1,10,16,48,115,44,25,39,268,32,23,58,175,38,20,71,336,30*71 +$GPGSV,3,2,10,19,02,028,18,04,06,241,23,13,30,223,24,27,19,284,33*73 +$GPGSV,3,3,10,11,06,337,28,03,14,055,28*7F +$GPRMC,222516.000,A,2734.67384,S,15305.63376,E,27.7,143.8,030308,11.2,W,A*05 +$GPVTG,143.8,T,155.0,M,27.7,N,51.4,K,A*2E +$GPGGA,222517.000,2734.68035,S,15305.63901,E,1,06,1.5,68.8,M,39.2,M,,*79 +$GPGLL,2734.68035,S,15305.63901,E,222517.000,A,A*40 +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222517.000,19.1,6.2,12.1,7.2,5.8,11.0,15.3*66 +$GPGSV,3,1,10,16,48,115,44,25,39,268,34,23,58,175,38,20,71,336,32*75 +$GPGSV,3,2,10,19,02,028,20,04,06,241,23,13,30,223,24,27,19,284,34*7F +$GPGSV,3,3,10,11,06,337,24,03,14,055,29*72 +$GPRMC,222517.000,A,2734.68035,S,15305.63901,E,28.6,144.1,030308,11.2,W,A*08 +$GPVTG,144.1,T,155.3,M,28.6,N,53.0,K,A*2B +$GPGGA,222518.000,2734.68718,S,15305.64446,E,1,07,1.4,69.1,M,39.2,M,,*7F +$GPGLL,2734.68718,S,15305.64446,E,222518.000,A,A*4E +$GPGSA,A,3,16,25,23,20,27,11,03,,,,,,2.4,1.4,1.9*3C +$GPGST,222518.000,13.8,5.5,10.6,5.8,5.1,9.6,13.4*54 +$GPGSV,3,1,10,16,48,115,43,25,39,268,33,23,58,175,37,20,71,336,35*7D +$GPGSV,3,2,10,19,02,028,20,04,06,241,18,13,30,223,24,27,19,284,33*70 +$GPGSV,3,3,10,11,06,337,22,03,14,055,33*7F +$GPRMC,222518.000,A,2734.68718,S,15305.64446,E,29.9,144.4,030308,11.2,W,A*0D +$GPVTG,144.4,T,155.6,M,29.9,N,55.3,K,A*20 +$GPGGA,222519.000,2734.69424,S,15305.65010,E,1,07,1.4,69.5,M,39.2,M,,*71 +$GPGLL,2734.69424,S,15305.65010,E,222519.000,A,A*44 +$GPGSA,A,3,16,25,23,20,27,11,03,,,,,,2.4,1.4,1.9*3C +$GPGST,222519.000,14.0,13.4,5.0,89.4,4.6,12.3,18.8*58 +$GPGSV,3,1,10,16,48,115,44,25,39,268,34,23,58,175,37,20,71,336,37*7F +$GPGSV,3,2,10,19,02,028,20,04,06,241,18,13,30,223,23,27,19,284,31*75 +$GPGSV,3,3,10,11,06,337,26,03,14,055,35*7D +$GPRMC,222519.000,A,2734.69424,S,15305.65010,E,31.0,144.4,030308,11.2,W,A*07 +$GPVTG,144.4,T,155.6,M,31.0,N,57.5,K,A*24 +$GPGGA,222520.000,2734.70163,S,15305.65604,E,1,07,1.4,69.6,M,39.2,M,,*75 +$GPGLL,2734.70163,S,15305.65604,E,222520.000,A,A*43 +$GPGSA,A,3,16,25,23,20,27,11,03,,,,,,2.4,1.4,1.9*3C +$GPGST,222520.000,9.8,11.6,4.7,89.0,4.3,10.6,15.9*6A +$GPGSV,3,1,10,16,48,115,44,25,39,268,35,23,58,175,39,20,71,336,36*71 +$GPGSV,3,2,10,19,02,028,20,04,06,241,19,13,30,223,23,27,19,284,31*74 +$GPGSV,3,3,10,11,06,337,31,03,14,055,37*79 +$GPRMC,222520.000,A,2734.70163,S,15305.65604,E,32.5,144.1,030308,11.2,W,A*03 +$GPVTG,144.1,T,155.3,M,32.5,N,60.2,K,A*21 +$GPGGA,222521.000,2734.70923,S,15305.66218,E,1,07,1.4,69.5,M,39.2,M,,*71 +$GPGLL,2734.70923,S,15305.66218,E,222521.000,A,A*44 +$GPGSA,A,3,16,25,23,20,27,11,03,,,,,,2.4,1.4,1.9*3C +$GPGST,222521.000,11.1,6.2,10.8,9.6,5.8,9.8,16.8*53 +$GPGSV,3,1,10,16,48,115,44,25,39,268,36,23,58,175,40,20,71,336,38*72 +$GPGSV,3,2,10,19,02,028,20,04,06,241,19,13,30,223,23,27,19,284,26*72 +$GPGSV,3,3,10,11,06,338,31,03,14,055,37*76 +$GPRMC,222521.000,A,2734.70923,S,15305.66218,E,33.5,144.0,030308,11.2,W,A*04 +$GPVTG,144.0,T,155.3,M,33.5,N,62.0,K,A*21 +$GPGGA,222522.000,2734.71700,S,15305.66845,E,1,06,1.5,69.1,M,39.2,M,,*7A +$GPGLL,2734.71700,S,15305.66845,E,222522.000,A,A*4B +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222522.000,15.0,5.7,9.9,5.5,5.2,9.0,15.4*68 +$GPGSV,3,1,10,16,48,115,43,25,39,268,36,23,58,175,40,20,71,336,37*7A +$GPGSV,3,2,10,19,02,028,20,04,06,241,24,13,30,223,23,27,19,284,26*7C +$GPGSV,3,3,10,11,06,338,25,03,14,055,37*73 +$GPRMC,222522.000,A,2734.71700,S,15305.66845,E,34.2,144.1,030308,11.2,W,A*0A +$GPVTG,144.1,T,155.3,M,34.2,N,63.4,K,A*25 +$GPGGA,222523.000,2734.72487,S,15305.67483,E,1,07,1.4,68.6,M,39.2,M,,*75 +$GPGLL,2734.72487,S,15305.67483,E,222523.000,A,A*42 +$GPGSA,A,3,16,25,23,20,27,11,03,,,,,,2.4,1.4,1.9*3C +$GPGST,222523.000,13.8,5.2,10.1,3.9,4.8,9.2,13.8*5B +$GPGSV,3,1,10,16,48,115,44,25,39,268,37,23,58,175,41,20,71,336,35*7F +$GPGSV,3,2,10,19,02,028,20,04,06,241,24,13,30,223,23,27,19,284,26*7C +$GPGSV,3,3,10,11,06,338,23,03,14,055,31*73 +$GPRMC,222523.000,A,2734.72487,S,15305.67483,E,34.7,144.2,030308,11.2,W,A*05 +$GPVTG,144.2,T,155.4,M,34.7,N,64.3,K,A*24 +$GPGGA,222524.000,2734.73280,S,15305.68126,E,1,07,1.4,68.1,M,39.2,M,,*70 +$GPGLL,2734.73280,S,15305.68126,E,222524.000,A,A*40 +$GPGSA,A,3,16,25,23,20,27,11,03,,,,,,2.4,1.4,1.9*3C +$GPGST,222524.000,10.5,4.9,9.4,3.5,4.5,8.6,12.8*60 +$GPGSV,3,1,10,16,48,115,44,25,39,268,37,23,58,175,41,20,71,336,35*7F +$GPGSV,3,2,10,19,02,028,20,04,06,241,22,13,30,223,23,27,19,284,26*7A +$GPGSV,3,3,10,11,06,338,25,03,14,055,29*7C +$GPRMC,222524.000,A,2734.73280,S,15305.68126,E,35.0,144.2,030308,11.2,W,A*01 +$GPVTG,144.2,T,155.4,M,35.0,N,64.9,K,A*28 +$GPGGA,222525.000,2734.74083,S,15305.68778,E,1,07,1.4,67.7,M,39.2,M,,*73 +$GPGLL,2734.74083,S,15305.68778,E,222525.000,A,A*4A +$GPGSA,A,3,16,25,23,20,27,11,03,,,,,,2.4,1.4,1.9*3C +$GPGST,222525.000,10.3,5.2,13.4,3.6,4.8,12.3,20.4*6B +$GPGSV,3,1,10,16,48,115,43,25,39,268,38,23,58,175,40,20,71,336,36*75 +$GPGSV,3,2,10,19,02,028,20,04,06,241,22,13,30,223,23,27,19,284,23*7F +$GPGSV,3,3,10,11,06,338,27,03,14,055,36*70 +$GPRMC,222525.000,A,2734.74083,S,15305.68778,E,35.6,144.2,030308,11.2,W,A*0D +$GPVTG,144.2,T,155.4,M,35.6,N,65.9,K,A*2F +$GPGGA,222526.000,2734.74894,S,15305.69428,E,1,06,1.5,67.2,M,39.2,M,,*7C +$GPGLL,2734.74894,S,15305.69428,E,222526.000,A,A*40 +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222526.000,8.0,5.7,12.0,2.6,5.2,10.9,17.4*54 +$GPGSV,3,1,10,16,48,115,43,25,39,268,37,23,58,175,40,20,71,336,35*79 +$GPGSV,3,2,10,19,02,028,20,04,06,241,22,13,30,223,23,27,19,284,24*78 +$GPGSV,3,3,10,11,06,338,27,03,14,055,39*7F +$GPRMC,222526.000,A,2734.74894,S,15305.69428,E,35.8,144.5,030308,11.2,W,A*0E +$GPVTG,144.5,T,155.7,M,35.8,N,66.3,K,A*2C +$GPGGA,222527.000,2734.75707,S,15305.70075,E,1,05,1.7,66.8,M,39.2,M,,*77 +$GPGLL,2734.75707,S,15305.70075,E,222527.000,A,A*41 +$GPGSA,A,3,16,25,23,20,03,,,,,,,,2.9,1.7,2.4*39 +$GPGST,222527.000,13.9,6.3,10.9,6.3,5.9,10.0,18.2*60 +$GPGSV,3,1,10,16,48,115,44,25,39,268,38,23,58,175,40,20,71,336,33*77 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,30,223,23,27,19,284,26*7F +$GPGSV,3,3,10,11,06,338,,03,14,055,36*75 +$GPRMC,222527.000,A,2734.75707,S,15305.70075,E,35.8,144.6,030308,11.2,W,A*0C +$GPVTG,144.6,T,155.8,M,35.8,N,66.2,K,A*21 +$GPGGA,222528.000,2734.76518,S,15305.70724,E,1,06,1.5,66.1,M,39.2,M,,*7C +$GPGLL,2734.76518,S,15305.70724,E,222528.000,A,A*42 +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222528.000,11.3,5.6,10.1,4.5,5.2,9.2,16.1*51 +$GPGSV,3,1,10,16,48,115,43,25,39,268,39,23,58,175,39,20,71,336,28*75 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,30,223,23,27,19,284,26*7F +$GPGSV,3,3,10,11,06,338,,03,14,055,36*75 +$GPRMC,222528.000,A,2734.76518,S,15305.70724,E,35.7,144.4,030308,11.2,W,A*02 +$GPVTG,144.4,T,155.6,M,35.7,N,66.2,K,A*22 +$GPGGA,222529.000,2734.77313,S,15305.71385,E,1,06,1.5,66.1,M,39.2,M,,*7F +$GPGLL,2734.77313,S,15305.71385,E,222529.000,A,A*41 +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222529.000,10.9,5.6,12.5,5.4,5.2,11.4,20.9*6F +$GPGSV,3,1,10,16,48,115,41,25,39,268,40,23,58,175,36,20,71,336,28*76 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,30,223,23,27,19,284,24*7D +$GPGSV,3,3,10,11,06,338,27,03,14,055,30*76 +$GPRMC,222529.000,A,2734.77313,S,15305.71385,E,35.6,143.5,030308,11.2,W,A*06 +$GPVTG,143.5,T,154.7,M,35.6,N,65.8,K,A*2C +$GPGGA,222530.000,2734.78106,S,15305.72042,E,1,06,1.5,66.0,M,39.2,M,,*74 +$GPGLL,2734.78106,S,15305.72042,E,222530.000,A,A*4B +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222530.000,9.4,5.4,18.9,2.4,4.9,17.3,30.6*54 +$GPGSV,3,1,10,16,48,115,40,25,39,268,40,23,58,175,36,20,71,336,28*77 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,30,223,23,27,19,284,26*7F +$GPGSV,3,3,10,11,06,338,27,03,14,055,29*7E +$GPRMC,222530.000,A,2734.78106,S,15305.72042,E,35.5,143.7,030308,11.2,W,A*0D +$GPVTG,143.7,T,155.0,M,35.5,N,65.8,K,A*2B +$GPGGA,222531.000,2734.78918,S,15305.72691,E,1,05,4.4,66.0,M,39.2,M,,*7D +$GPGLL,2734.78918,S,15305.72691,E,222531.000,A,A*45 +$GPGSA,A,3,16,25,23,27,11,,,,,,,,9.3,4.4,8.2*36 +$GPGST,222531.000,9.1,8.5,53.9,3.2,8.2,49.2,81.3*56 +$GPGSV,3,1,10,16,48,115,40,25,39,268,39,23,58,175,37,20,71,336,28*78 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,31,223,23,27,19,284,25*7D +$GPGSV,3,3,10,11,06,338,24,03,14,055,22*76 +$GPRMC,222531.000,A,2734.78918,S,15305.72691,E,35.9,144.5,030308,11.2,W,A*0A +$GPVTG,144.5,T,155.7,M,35.9,N,66.4,K,A*2A +$GPGGA,222532.000,2734.79737,S,15305.73347,E,1,06,1.5,66.0,M,39.2,M,,*74 +$GPGLL,2734.79737,S,15305.73347,E,222532.000,A,A*4B +$GPGSA,A,3,16,25,23,20,27,03,,,,,,,2.5,1.5,2.0*36 +$GPGST,222532.000,11.0,6.8,38.7,1.2,6.3,35.3,58.4*69 +$GPGSV,3,1,10,16,48,115,40,25,39,268,39,23,58,175,36,20,71,336,28*79 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,31,223,24,27,19,284,23*7C +$GPGSV,3,3,10,11,06,338,24,03,14,055,30*75 +$GPRMC,222532.000,A,2734.79737,S,15305.73347,E,36.1,144.5,030308,11.2,W,A*0F +$GPVTG,144.5,T,155.7,M,36.1,N,66.9,K,A*2C +$GPGGA,222533.000,2734.80571,S,15305.74004,E,1,06,4.1,66.1,M,39.2,M,,*70 +$GPGLL,2734.80571,S,15305.74004,E,222533.000,A,A*4F +$GPGSA,A,3,16,25,23,27,11,03,,,,,,,8.3,4.1,7.3*3F +$GPGST,222533.000,9.4,6.1,45.5,0.4,5.6,41.6,69.5*50 +$GPGSV,3,1,10,16,48,115,40,25,39,268,39,23,58,175,37,20,71,336,28*78 +$GPGSV,3,2,10,19,02,028,,04,06,241,23,13,31,223,24,27,19,284,25*7C +$GPGSV,3,3,10,11,06,338,22,03,14,055,25*77 +$GPRMC,222533.000,A,2734.80571,S,15305.74004,E,36.6,145.0,030308,11.2,W,A*08 +$GPVTG,145.0,T,156.2,M,36.6,N,67.7,K,A*26 +$GPGGA,222534.000,2734.81441,S,15305.74656,E,1,06,1.8,65.2,M,39.2,M,,*79 +$GPGLL,2734.81441,S,15305.74656,E,222534.000,A,A*4A +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.5,1.8,3.1*3A +$GPGST,222534.000,20.2,5.8,45.7,1.7,5.4,41.7,71.3*6C +$GPGSV,3,1,10,16,48,115,40,25,39,268,38,23,58,175,37,20,71,336,28*79 +$GPGSV,3,2,10,19,02,028,,04,06,241,23,13,31,223,25,27,19,284,25*7D +$GPGSV,3,3,10,11,06,338,22,03,14,055,31*72 +$GPRMC,222534.000,A,2734.81441,S,15305.74656,E,37.6,146.2,030308,11.2,W,A*0D +$GPVTG,146.2,T,157.5,M,37.6,N,69.7,K,A*2E +$GPGGA,222535.000,2734.82349,S,15305.75307,E,1,05,1.9,63.5,M,39.2,M,,*77 +$GPGLL,2734.82349,S,15305.75307,E,222535.000,A,A*47 +$GPGSA,A,3,16,25,23,13,03,,,,,,,,3.6,1.9,3.1*3D +$GPGST,222535.000,27.6,7.0,40.6,3.1,6.7,37.1,68.4*6C +$GPGSV,3,1,10,16,48,115,40,25,39,268,36,23,58,175,37,20,71,336,28*77 +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,31,223,25,27,19,284,23*78 +$GPGSV,3,3,10,11,06,338,22,03,14,055,26*74 +$GPRMC,222535.000,A,2734.82349,S,15305.75307,E,38.7,147.3,030308,11.2,W,A*0E +$GPVTG,147.3,T,158.6,M,38.7,N,71.7,K,A*25 +$GPGGA,222536.000,2734.83215,S,15305.75969,E,1,05,1.7,63.5,M,39.2,M,,*71 +$GPGLL,2734.83215,S,15305.75969,E,222536.000,A,A*4F +$GPGSA,A,3,16,25,23,20,03,,,,,,,,2.9,1.7,2.4*39 +$GPGST,222536.000,7.9,6.3,97.2,3.9,8.3,88.7,161.0*62 +$GPGSV,3,1,10,16,48,115,40,25,39,268,37,23,58,175,36,20,71,336,28*77 +$GPGSV,3,2,10,19,02,028,,04,06,241,20,13,31,223,25,27,19,284,23*78 +$GPGSV,3,3,10,11,06,338,22,03,14,055,26*74 +$GPRMC,222536.000,A,2734.83215,S,15305.75969,E,37.6,145.6,030308,11.2,W,A*0F +$GPVTG,145.6,T,156.8,M,37.6,N,69.7,K,A*25 +$GPGGA,222537.000,2734.84076,S,15305.76655,E,1,05,1.7,63.5,M,39.2,M,,*73 +$GPGLL,2734.84076,S,15305.76655,E,222537.000,A,A*4D +$GPGSA,A,3,16,25,23,20,03,,,,,,,,2.9,1.7,2.4*39 +$GPGST,222537.000,16.0,7.8,110.8,2.1,8.0,101.2,209.1*57 +$GPGSV,3,1,10,16,48,115,39,25,39,268,36,23,58,175,34,20,71,336,28*7A +$GPGSV,3,2,10,19,02,028,,04,06,241,22,13,31,223,25,27,19,284,23*7A +$GPGSV,3,3,10,11,06,338,22,03,14,055,25*77 +$GPRMC,222537.000,A,2734.84076,S,15305.76655,E,37.9,144.6,030308,11.2,W,A*03 +$GPVTG,144.6,T,155.8,M,37.9,N,70.3,K,A*24 +$GPGGA,222538.000,2734.84945,S,15305.77356,E,1,05,1.9,63.5,M,39.2,M,,*7C +$GPGLL,2734.84945,S,15305.77356,E,222538.000,A,A*4C +$GPGSA,A,3,16,25,23,13,03,,,,,,,,3.6,1.9,3.1*3D +$GPGST,222538.000,13.1,8.0,61.7,1.6,7.5,56.4,113.0*51 +$GPGSV,3,1,10,16,48,115,40,25,39,268,37,23,58,175,32,20,71,336,*79 +$GPGSV,3,2,10,19,02,028,,04,06,241,22,13,31,223,25,27,19,284,23*7A +$GPGSV,3,3,10,11,06,338,22,03,14,055,28*7A +$GPRMC,222538.000,A,2734.84945,S,15305.77356,E,38.4,144.2,030308,11.2,W,A*04 +$GPVTG,144.2,T,155.4,M,38.4,N,71.1,K,A*2D +$GPGGA,222539.000,2734.85792,S,15305.78022,E,1,05,1.9,63.5,M,39.2,M,,*77 +$GPGLL,2734.85792,S,15305.78022,E,222539.000,A,A*47 +$GPGSA,A,3,16,25,23,13,03,,,,,,,,3.6,1.9,3.1*3D +$GPGST,222539.000,6.4,7.5,72.7,1.5,7.1,66.5,137.2*68 +$GPGSV,3,1,10,16,48,115,40,25,39,268,38,23,58,175,32,20,71,336,*76 +$GPGSV,3,2,10,19,02,028,,04,06,241,22,13,31,223,25,27,19,284,23*7A +$GPGSV,3,3,10,11,06,338,22,03,14,055,28*7A +$GPRMC,222539.000,A,2734.85792,S,15305.78022,E,37.1,144.9,030308,11.2,W,A*0E +$GPVTG,144.9,T,156.1,M,37.1,N,68.7,K,A*24 +$GPGGA,222540.000,2734.86604,S,15305.78646,E,1,05,4.4,63.5,M,39.2,M,,*78 +$GPGLL,2734.86604,S,15305.78646,E,222540.000,A,A*40 +$GPGSA,A,3,16,25,23,27,11,,,,,,,,9.3,4.4,8.2*36 +$GPGST,222540.000,13.6,8.3,67.7,1.8,7.8,61.9,111.1*55 +$GPGSV,3,1,10,16,48,115,40,25,39,268,38,23,58,175,32,20,71,336,*76 +$GPGSV,3,2,10,19,02,028,,04,06,241,26,13,31,223,22,27,19,284,22*78 +$GPGSV,3,3,10,11,06,338,20,03,14,055,28*78 +$GPRMC,222540.000,A,2734.86604,S,15305.78646,E,35.3,145.6,030308,11.2,W,A*07 +$GPVTG,145.6,T,156.8,M,35.3,N,65.4,K,A*2D +$GPGGA,222541.000,2734.87421,S,15305.79222,E,1,06,1.8,63.5,M,39.2,M,,*70 +$GPGLL,2734.87421,S,15305.79222,E,222541.000,A,A*42 +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.5,1.8,3.1*3A +$GPGST,222541.000,11.7,33.3,8.1,87.4,7.5,30.4,54.6*51 +$GPGSV,3,1,10,16,48,115,39,25,39,268,33,23,58,175,36,20,71,336,*77 +$GPGSV,3,2,10,19,02,028,,04,06,241,26,13,31,223,22,27,19,284,22*78 +$GPGSV,3,3,10,11,06,338,20,03,14,055,26*76 +$GPRMC,222541.000,A,2734.87421,S,15305.79222,E,34.6,147.7,030308,11.2,W,A*02 +$GPVTG,147.7,T,158.9,M,34.6,N,64.1,K,A*21 +$GPGGA,222542.000,2734.88135,S,15305.79765,E,1,06,1.8,63.5,M,39.2,M,,*7A +$GPGLL,2734.88135,S,15305.79765,E,222542.000,A,A*48 +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.5,1.8,3.1*3A +$GPGST,222542.000,23.5,7.9,53.0,0.2,7.2,48.5,78.4*6F +$GPGSV,3,1,10,16,48,115,40,25,39,268,36,23,58,175,35,20,71,336,*7F +$GPGSV,3,2,10,19,02,028,,04,06,241,27,13,31,223,23,27,19,284,25*7F +$GPGSV,3,3,10,11,06,338,20,03,14,055,26*76 +$GPRMC,222542.000,A,2734.88135,S,15305.79765,E,31.0,145.8,030308,11.2,W,A*06 +$GPVTG,145.8,T,157.0,M,31.0,N,57.4,K,A*2C +$GPGGA,222543.000,2734.88798,S,15305.80287,E,1,07,1.2,63.0,M,39.2,M,,*7B +$GPGLL,2734.88798,S,15305.80287,E,222543.000,A,A*47 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222543.000,18.8,6.7,28.4,0.5,6.1,26.0,41.8*62 +$GPGSV,3,1,10,16,48,115,40,25,39,268,39,23,58,175,35,20,71,336,21*73 +$GPGSV,3,2,10,19,02,028,,04,06,241,37,13,31,223,22,27,19,284,24*7E +$GPGSV,3,3,10,11,06,338,20,03,14,055,32*73 +$GPRMC,222543.000,A,2734.88798,S,15305.80287,E,29.0,144.8,030308,11.2,W,A*01 +$GPVTG,144.8,T,156.0,M,29.0,N,53.8,K,A*2D +$GPGGA,222544.000,2734.89328,S,15305.80767,E,1,06,1.8,62.9,M,39.2,M,,*7A +$GPGLL,2734.89328,S,15305.80767,E,222544.000,A,A*45 +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.5,1.8,3.1*3A +$GPGST,222544.000,14.9,35.2,5.8,89.5,5.4,32.2,52.6*52 +$GPGSV,3,1,10,16,48,115,43,25,39,268,40,23,58,175,25,20,71,336,21*7F +$GPGSV,3,2,10,19,02,028,,04,06,241,28,13,31,223,30,27,19,284,29*7E +$GPGSV,3,3,10,11,06,338,20,03,14,055,33*72 +$GPRMC,222544.000,A,2734.89328,S,15305.80767,E,24.5,140.9,030308,11.2,W,A*0E +$GPVTG,140.9,T,152.1,M,24.5,N,45.3,K,A*29 +$GPGGA,222545.000,2734.89633,S,15305.81084,E,1,05,1.9,62.9,M,39.2,M,,*7D +$GPGLL,2734.89633,S,15305.81084,E,222545.000,A,A*40 +$GPGSA,A,3,16,25,23,13,03,,,,,,,,3.6,1.9,3.1*3D +$GPGST,222545.000,19.5,6.0,33.0,1.0,5.5,30.2,52.4*69 +$GPGSV,3,1,10,16,48,115,41,25,39,268,34,23,58,175,29,20,71,336,30*72 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,31,223,29,27,19,284,30*73 +$GPGSV,3,3,10,11,06,338,20,03,14,055,30*71 +$GPRMC,222545.000,A,2734.89633,S,15305.81084,E,14.6,136.6,030308,11.2,W,A*05 +$GPVTG,136.6,T,147.8,M,14.6,N,27.0,K,A*2D +$GPGGA,222546.000,2734.89236,S,15305.81130,E,1,06,1.3,62.6,M,39.2,M,,*77 +$GPGLL,2734.89236,S,15305.81130,E,222546.000,A,A*4C +$GPGSA,A,3,16,25,23,20,13,03,,,,,,,2.3,1.3,1.9*3B +$GPGST,222546.000,11.5,5.6,15.6,0.9,5.2,14.3,26.4*6E +$GPGSV,3,1,10,16,48,115,35,25,39,268,40,23,58,175,41,20,71,336,38*74 +$GPGSV,3,2,10,19,02,028,,04,06,241,26,13,31,223,33,27,19,284,26*7C +$GPGSV,3,3,10,11,06,338,20,03,14,055,28*78 +$GPRMC,222546.000,A,2734.89236,S,15305.81130,E,16.9,2.1,030308,11.2,W,A*05 +$GPVTG,2.1,T,13.3,M,16.9,N,31.2,K,A*1F +$GPGGA,222547.000,2734.89429,S,15305.81239,E,1,05,2.3,62.5,M,39.2,M,,*77 +$GPGLL,2734.89429,S,15305.81239,E,222547.000,A,A*4F +$GPGSA,A,3,16,23,13,27,03,,,,,,,,4.6,2.3,3.9*39 +$GPGST,222547.000,11.4,38.3,9.7,69.8,14.7,33.0,49.2*6B +$GPGSV,3,1,10,16,48,115,32,25,39,268,37,23,58,175,42,20,71,336,37*7F +$GPGSV,3,2,10,19,02,028,,04,06,241,26,13,31,223,36,27,19,284,29*76 +$GPGSV,3,3,10,11,06,338,20,03,14,055,34*75 +$GPRMC,222547.000,A,2734.89429,S,15305.81239,E,8.4,154.5,030308,11.2,W,A*32 +$GPVTG,154.5,T,165.7,M,8.4,N,15.6,K,A*1D +$GPGGA,222548.000,2734.89474,S,15305.81253,E,1,07,1.2,62.0,M,39.2,M,,*79 +$GPGLL,2734.89474,S,15305.81253,E,222548.000,A,A*44 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222548.000,11.0,12.3,7.9,86.0,7.3,11.3,20.6*5F +$GPGSV,3,1,10,16,48,115,34,25,39,268,36,23,58,175,42,20,71,336,38*77 +$GPGSV,3,2,10,19,02,028,,04,06,241,25,13,31,223,36,27,19,284,29*75 +$GPGSV,3,3,10,11,06,338,20,03,14,055,33*72 +$GPRMC,222548.000,A,2734.89474,S,15305.81253,E,0.3,353.0,030308,11.2,W,A*36 +$GPVTG,353.0,T,4.3,M,0.3,N,0.5,K,A*27 +$GPGGA,222549.000,2734.89490,S,15305.81265,E,1,04,2.0,61.8,M,39.2,M,,*7E +$GPGLL,2734.89490,S,15305.81265,E,222549.000,A,A*4A +$GPGSA,A,3,25,23,13,03,,,,,,,,,3.9,2.0,3.3*3D +$GPGST,222549.000,17.0,16.1,8.8,81.3,8.3,14.6,29.3*57 +$GPGSV,3,1,11,16,48,115,33,25,39,268,37,23,58,175,42,20,71,336,36*7E +$GPGSV,3,2,11,19,02,028,,04,06,241,25,13,31,223,35,27,19,284,30*7F +$GPGSV,3,3,11,11,06,338,20,01,,,21,03,14,055,33*71 +$GPRMC,222549.000,A,2734.89490,S,15305.81265,E,0.2,330.7,030308,11.2,W,A*3B +$GPVTG,330.7,T,341.9,M,0.2,N,0.3,K,A*2A +$GPGGA,222550.000,2734.89526,S,15305.81278,E,1,04,2.6,61.7,M,39.2,M,,*7F +$GPGLL,2734.89526,S,15305.81278,E,222550.000,A,A*42 +$GPGSA,A,3,16,25,23,13,,,,,,,,,6.1,2.6,5.6*31 +$GPGST,222550.000,13.4,12.4,27.3,43.1,18.9,19.8,52.9*5A +$GPGSV,3,1,11,16,48,115,30,25,39,268,38,23,58,175,42,20,71,336,37*73 +$GPGSV,3,2,11,19,02,028,,04,06,241,25,13,31,223,36,27,19,284,31*7D +$GPGSV,3,3,11,11,06,338,,01,,,21,03,14,055,32*72 +$GPRMC,222550.000,A,2734.89526,S,15305.81278,E,0.1,350.7,030308,11.2,W,A*36 +$GPVTG,350.7,T,1.9,M,0.1,N,0.2,K,A*29 +$GPGGA,222551.000,2734.89528,S,15305.81279,E,1,05,2.3,61.7,M,39.2,M,,*75 +$GPGLL,2734.89528,S,15305.81279,E,222551.000,A,A*4C +$GPGSA,A,3,16,23,13,27,03,,,,,,,,4.6,2.3,3.9*39 +$GPGST,222551.000,13.7,31.0,11.1,60.5,16.5,25.2,39.3*5F +$GPGSV,3,1,11,16,48,115,30,25,39,268,39,23,58,175,41,20,71,336,35*73 +$GPGSV,3,2,11,19,02,028,,04,06,241,23,13,31,223,36,27,19,284,31*7B +$GPGSV,3,3,11,11,06,338,,01,,,20,03,14,055,30*71 +$GPRMC,222551.000,A,2734.89528,S,15305.81279,E,0.1,345.0,030308,11.2,W,A*3B +$GPVTG,345.0,T,356.2,M,0.1,N,0.2,K,A*20 +$GPGGA,222552.000,2734.89550,S,15305.81288,E,1,05,2.1,61.7,M,39.2,M,,*75 +$GPGLL,2734.89550,S,15305.81288,E,222552.000,A,A*4E +$GPGSA,A,3,16,25,23,13,27,,,,,,,,4.7,2.1,4.2*32 +$GPGST,222552.000,11.7,11.5,20.8,20.0,11.8,18.2,39.1*55 +$GPGSV,3,1,11,16,48,115,31,25,39,268,40,23,58,175,42,20,71,336,35*7F +$GPGSV,3,2,11,19,02,028,,04,06,241,23,13,31,223,38,27,19,284,31*75 +$GPGSV,3,3,11,11,06,338,,01,,,20,03,14,055,30*71 +$GPRMC,222552.000,A,2734.89550,S,15305.81288,E,0.1,11.2,030308,11.2,W,A*09 +$GPVTG,11.2,T,22.5,M,0.1,N,0.2,K,A*27 +$GPGGA,222553.000,2734.89573,S,15305.81294,E,1,05,2.1,61.5,M,39.2,M,,*7A +$GPGLL,2734.89573,S,15305.81294,E,222553.000,A,A*43 +$GPGSA,A,3,16,25,23,13,27,,,,,,,,4.7,2.1,4.2*32 +$GPGST,222553.000,9.8,10.3,17.6,19.1,10.4,15.6,33.7*6C +$GPGSV,3,1,10,16,48,115,29,25,39,268,38,23,58,175,41,20,71,336,37*79 +$GPGSV,3,2,10,19,02,028,,04,06,241,23,13,31,223,39,27,19,284,30*74 +$GPGSV,3,3,10,11,06,338,,03,14,055,29*7B +$GPRMC,222553.000,A,2734.89573,S,15305.81294,E,0.1,351.8,030308,11.2,W,A*39 +$GPVTG,351.8,T,3.0,M,0.1,N,0.2,K,A*2C +$GPGGA,222554.000,2734.89591,S,15305.81298,E,1,04,2.6,61.4,M,39.2,M,,*7A +$GPGLL,2734.89591,S,15305.81298,E,222554.000,A,A*44 +$GPGSA,A,3,16,25,23,13,,,,,,,,,6.1,2.6,5.6*31 +$GPGST,222554.000,16.9,10.4,17.8,12.8,10.0,16.1,37.3*5F +$GPGSV,3,1,10,16,48,115,32,25,39,268,38,23,58,175,41,20,71,336,36*72 +$GPGSV,3,2,10,19,02,028,,04,06,241,23,13,31,223,38,27,19,284,31*74 +$GPGSV,3,3,10,11,06,338,,03,14,055,26*74 +$GPRMC,222554.000,A,2734.89591,S,15305.81298,E,0.1,334.9,030308,11.2,W,A*3C +$GPVTG,334.9,T,346.1,M,0.1,N,0.2,K,A*2D +$GPGGA,222555.000,2734.89607,S,15305.81301,E,1,04,2.6,61.2,M,39.2,M,,*70 +$GPGLL,2734.89607,S,15305.81301,E,222555.000,A,A*48 +$GPGSA,A,3,16,25,23,13,,,,,,,,,6.1,2.6,5.6*31 +$GPGST,222555.000,14.4,11.9,23.3,40.8,16.2,17.6,44.5*56 +$GPGSV,3,1,11,16,48,115,32,25,39,268,37,23,58,175,40,20,71,336,38*73 +$GPGSV,3,2,11,19,02,028,,04,06,241,23,13,31,223,37,27,19,284,30*7B +$GPGSV,3,3,11,11,06,338,,01,,,20,03,14,055,25*75 +$GPRMC,222555.000,A,2734.89607,S,15305.81301,E,0.1,6.2,030308,11.2,W,A*39 +$GPVTG,6.2,T,17.4,M,0.1,N,0.2,K,A*16 +$GPGGA,222556.000,2734.89612,S,15305.81301,E,1,04,2.6,61.1,M,39.2,M,,*74 +$GPGLL,2734.89612,S,15305.81301,E,222556.000,A,A*4F +$GPGSA,A,3,16,25,23,13,,,,,,,,,6.1,2.6,5.6*31 +$GPGST,222556.000,12.4,15.8,24.1,4.1,14.5,22.0,39.9*69 +$GPGSV,3,1,11,16,48,115,33,25,39,268,38,23,58,174,41,20,71,336,38*7D +$GPGSV,3,2,11,19,02,028,,04,06,242,23,13,31,223,38,27,19,284,32*75 +$GPGSV,3,3,11,11,06,338,,01,,,20,03,14,055,28*78 +$GPRMC,222556.000,A,2734.89612,S,15305.81301,E,0.1,21.8,030308,11.2,W,A*01 +$GPVTG,21.8,T,33.0,M,0.1,N,0.2,K,A*2B +$GPGGA,222557.000,2734.89635,S,15305.81337,E,1,05,2.1,61.0,M,39.2,M,,*72 +$GPGLL,2734.89635,S,15305.81337,E,222557.000,A,A*4E +$GPGSA,A,3,16,25,23,13,27,,,,,,,,4.7,2.1,4.2*32 +$GPGST,222557.000,10.3,13.0,15.0,5.2,11.9,13.7,32.7*6B +$GPGSV,3,1,10,16,48,115,33,25,39,268,38,23,58,174,41,20,71,336,36*72 +$GPGSV,3,2,10,19,02,028,,04,06,242,23,13,31,223,39,27,19,284,28*7E +$GPGSV,3,3,10,11,06,338,,03,14,055,31*72 +$GPRMC,222557.000,A,2734.89635,S,15305.81337,E,1.2,96.1,030308,11.2,W,A*07 +$GPVTG,96.1,T,107.3,M,1.2,N,2.2,K,A*1B +$GPGGA,222558.000,2734.89638,S,15305.81517,E,1,06,1.8,60.6,M,39.2,M,,*7A +$GPGLL,2734.89638,S,15305.81517,E,222558.000,A,A*48 +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.5,1.8,3.1*3A +$GPGST,222558.000,18.6,12.9,7.5,72.4,7.5,11.4,21.9*57 +$GPGSV,3,1,10,16,48,115,39,25,39,268,38,23,58,174,37,20,71,336,26*78 +$GPGSV,3,2,10,19,02,028,,04,06,242,23,13,31,223,34,27,19,284,29*72 +$GPGSV,3,3,10,11,06,338,,03,14,055,30*73 +$GPRMC,222558.000,A,2734.89638,S,15305.81517,E,5.6,82.0,030308,11.2,W,A*05 +$GPVTG,82.0,T,93.2,M,5.6,N,10.3,K,A*10 +$GPGGA,222559.000,2734.89631,S,15305.81911,E,1,05,2.2,60.5,M,39.2,M,,*71 +$GPGLL,2734.89631,S,15305.81911,E,222559.000,A,A*4A +$GPGSA,A,3,16,25,23,20,27,,,,,,,,3.6,2.2,2.9*3A +$GPGST,222559.000,17.0,15.1,33.1,15.1,15.5,29.4,59.0*59 +$GPGSV,3,1,10,16,48,115,34,25,39,268,35,23,58,174,30,20,71,336,29*70 +$GPGSV,3,2,10,19,02,028,,04,06,242,23,13,31,223,35,27,19,284,28*72 +$GPGSV,3,3,10,11,06,338,,03,14,055,27*75 +$GPRMC,222559.000,A,2734.89631,S,15305.81911,E,12.5,87.6,030308,11.2,W,A*31 +$GPVTG,87.6,T,98.8,M,12.5,N,23.2,K,A*26 +$GPGGA,222600.000,2734.89436,S,15305.82359,E,1,06,1.8,61.2,M,39.2,M,,*72 +$GPGLL,2734.89436,S,15305.82359,E,222600.000,A,A*45 +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.5,1.8,3.1*3A +$GPGST,222600.000,14.4,14.9,6.9,79.1,6.7,13.4,28.4*57 +$GPGSV,3,1,10,16,48,115,37,25,39,268,36,23,58,174,29,20,71,336,33*73 +$GPGSV,3,2,10,19,02,028,,04,06,242,23,13,31,223,39,27,19,284,28*7E +$GPGSV,3,3,10,11,06,338,,03,14,055,28*7A +$GPRMC,222600.000,A,2734.89436,S,15305.82359,E,15.9,62.5,030308,11.2,W,A*3D +$GPVTG,62.5,T,73.7,M,15.9,N,29.5,K,A*22 +$GPGGA,222601.000,2734.89190,S,15305.82850,E,1,06,1.8,60.5,M,39.2,M,,*7E +$GPGLL,2734.89190,S,15305.82850,E,222601.000,A,A*4F +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.6,1.8,3.1*39 +$GPGST,222601.000,11.5,13.4,6.1,78.5,6.0,12.0,24.9*56 +$GPGSV,3,1,10,16,48,115,40,25,39,268,27,23,58,174,35,20,71,336,28*74 +$GPGSV,3,2,10,19,02,028,,04,06,242,,13,31,223,39,27,19,284,27*70 +$GPGSV,3,3,10,11,06,338,,03,14,055,23*71 +$GPRMC,222601.000,A,2734.89190,S,15305.82850,E,18.0,58.9,030308,11.2,W,A*36 +$GPVTG,58.9,T,70.1,M,18.0,N,33.4,K,A*2C +$GPGGA,222602.000,2734.88904,S,15305.83361,E,1,05,1.6,60.0,M,39.2,M,,*79 +$GPGLL,2734.88904,S,15305.83361,E,222602.000,A,A*40 +$GPGSA,A,3,16,23,20,13,03,,,,,,,,2.5,1.6,1.9*3F +$GPGST,222602.000,11.3,17.1,6.5,54.9,10.2,13.2,18.6*62 +$GPGSV,3,1,10,16,48,115,27,25,39,268,29,23,58,174,33,20,71,336,33*77 +$GPGSV,3,2,10,19,02,028,,04,06,242,,13,31,223,35,27,19,284,32*78 +$GPGSV,3,3,10,11,06,338,,03,14,055,24*76 +$GPRMC,222602.000,A,2734.88904,S,15305.83361,E,19.8,55.1,030308,11.2,W,A*35 +$GPVTG,55.1,T,66.3,M,19.8,N,36.7,K,A*23 +$GPGGA,222603.000,2734.88556,S,15305.83944,E,1,04,3.8,58.8,M,39.2,M,,*70 +$GPGLL,2734.88556,S,15305.83944,E,222603.000,A,A*47 +$GPGSA,A,3,25,23,20,27,,,,,,,,,6.5,3.8,5.2*3C +$GPGST,222603.000,12.6,48.6,15.6,66.9,41.3,21.9,62.5*59 +$GPGSV,3,1,10,16,48,115,23,25,39,268,29,23,58,174,31,20,71,337,37*74 +$GPGSV,3,2,10,19,02,028,,04,06,242,,13,31,223,34,27,19,284,26*7C +$GPGSV,3,3,10,11,06,338,,03,14,055,28*7A +$GPRMC,222603.000,A,2734.88556,S,15305.83944,E,22.7,54.4,030308,11.2,W,A*31 +$GPVTG,54.4,T,65.6,M,22.7,N,42.1,K,A*23 +$GPGGA,222604.000,2734.88238,S,15305.84546,E,1,04,4.4,57.8,M,39.2,M,,*75 +$GPGLL,2734.88238,S,15305.84546,E,222604.000,A,A*46 +$GPGSA,A,3,25,20,13,27,,,,,,,,,7.2,4.4,5.7*37 +$GPGST,222604.000,10.4,46.8,10.9,81.3,42.4,11.8,57.5*57 +$GPGSV,3,1,10,16,48,115,27,25,39,268,32,23,58,174,32,20,71,337,37*79 +$GPGSV,3,2,10,19,02,028,,04,06,242,,13,31,223,34,27,19,284,34*7F +$GPGSV,3,3,10,11,06,338,,03,14,055,29*7B +$GPRMC,222604.000,A,2734.88238,S,15305.84546,E,23.3,54.5,030308,11.2,W,A*34 +$GPVTG,54.5,T,65.7,M,23.3,N,43.2,K,A*24 +$GPGGA,222605.000,2734.87917,S,15305.85182,E,1,04,4.4,56.6,M,39.2,M,,*7F +$GPGLL,2734.87917,S,15305.85182,E,222605.000,A,A*43 +$GPGSA,A,3,25,20,13,27,,,,,,,,,7.2,4.4,5.7*37 +$GPGST,222605.000,14.3,40.3,10.1,81.5,36.5,10.7,49.8*58 +$GPGSV,3,1,10,16,48,115,24,25,39,268,32,23,58,174,33,20,71,337,37*7B +$GPGSV,3,2,10,19,02,028,,04,06,242,20,13,31,223,35,27,19,284,32*7A +$GPGSV,3,3,10,11,06,338,,03,14,055,26*74 +$GPRMC,222605.000,A,2734.87917,S,15305.85182,E,24.5,55.0,030308,11.2,W,A*34 +$GPVTG,55.0,T,66.2,M,24.5,N,45.3,K,A*20 +$GPGGA,222606.000,2734.87588,S,15305.85846,E,1,05,3.6,55.8,M,39.2,M,,*7E +$GPGLL,2734.87588,S,15305.85846,E,222606.000,A,A*4B +$GPGSA,A,3,25,23,20,13,27,,,,,,,,6.3,3.6,5.2*36 +$GPGST,222606.000,11.7,30.4,8.7,77.7,27.2,9.8,40.6*59 +$GPGSV,3,1,10,16,48,115,24,25,39,268,31,23,58,174,32,20,71,337,34*7A +$GPGSV,3,2,10,19,02,029,,04,06,242,20,13,31,223,35,27,19,284,34*7D +$GPGSV,3,3,10,11,06,338,,03,14,055,24*76 +$GPRMC,222606.000,A,2734.87588,S,15305.85846,E,25.3,55.9,030308,11.2,W,A*32 +$GPVTG,55.9,T,67.1,M,25.3,N,46.9,K,A*25 +$GPGGA,222607.000,2734.87218,S,15305.86543,E,1,06,1.8,55.5,M,39.2,M,,*78 +$GPGLL,2734.87218,S,15305.86543,E,222607.000,A,A*4F +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.6,1.8,3.1*39 +$GPGST,222607.000,11.2,13.8,8.5,86.1,7.8,12.6,27.3*52 +$GPGSV,3,1,10,16,48,115,29,25,39,268,26,23,58,174,34,20,71,337,32*71 +$GPGSV,3,2,10,19,02,029,,04,06,242,27,13,31,223,36,27,19,284,30*7D +$GPGSV,3,3,10,11,06,338,,03,14,055,24*76 +$GPRMC,222607.000,A,2734.87218,S,15305.86543,E,26.0,57.6,030308,11.2,W,A*3B +$GPVTG,57.6,T,68.8,M,26.0,N,48.1,K,A*28 +$GPGGA,222608.000,2734.86867,S,15305.87251,E,1,06,1.8,55.1,M,39.2,M,,*75 +$GPGLL,2734.86867,S,15305.87251,E,222608.000,A,A*46 +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.6,1.8,3.1*39 +$GPGST,222608.000,15.1,15.8,7.1,68.8,8.0,13.6,36.7*5C +$GPGSV,3,1,10,16,48,115,39,25,39,268,28,23,58,174,35,20,71,337,30*7D +$GPGSV,3,2,10,19,02,029,,04,06,242,27,13,31,223,40,27,19,284,28*75 +$GPGSV,3,3,10,11,06,338,,03,14,055,28*7A +$GPRMC,222608.000,A,2734.86867,S,15305.87251,E,25.9,60.7,030308,11.2,W,A*3D +$GPVTG,60.7,T,71.9,M,25.9,N,47.9,K,A*29 +$GPGGA,222609.000,2734.86555,S,15305.87973,E,1,06,1.8,54.7,M,39.2,M,,*74 +$GPGLL,2734.86555,S,15305.87973,E,222609.000,A,A*40 +$GPGSA,A,3,16,25,23,13,27,03,,,,,,,3.6,1.8,3.1*39 +$GPGST,222609.000,10.9,10.9,5.1,85.4,4.7,10.0,22.0*55 +$GPGSV,3,1,10,16,48,116,34,25,39,268,39,23,58,174,29,20,71,337,33*7D +$GPGSV,3,2,10,19,02,029,,04,06,242,24,13,31,223,42,27,19,284,23*7F +$GPGSV,3,3,10,11,06,338,,03,14,055,26*74 +$GPRMC,222609.000,A,2734.86555,S,15305.87973,E,25.7,64.3,030308,11.2,W,A*35 +$GPVTG,64.3,T,75.6,M,25.7,N,47.5,K,A*20 +$GPGGA,222610.000,2734.86253,S,15305.88695,E,1,05,1.9,54.1,M,39.2,M,,*71 +$GPGLL,2734.86253,S,15305.88695,E,222610.000,A,A*41 +$GPGSA,A,3,16,25,23,13,03,,,,,,,,3.7,1.9,3.1*3C +$GPGST,222610.000,11.1,14.6,7.4,64.7,8.3,12.4,28.7*57 +$GPGSV,3,1,10,16,48,116,29,25,39,268,42,23,58,174,28,20,71,337,32*7D +$GPGSV,3,2,10,19,02,029,,04,06,242,24,13,31,223,42,27,19,284,23*7F +$GPGSV,3,3,10,11,06,338,,03,14,055,24*76 +$GPRMC,222610.000,A,2734.86253,S,15305.88695,E,25.3,65.0,030308,11.2,W,A*32 +$GPVTG,65.0,T,76.3,M,25.3,N,46.8,K,A*2C +$GPGGA,222611.000,2734.85924,S,15305.89387,E,1,05,2.2,53.4,M,39.2,M,,*75 +$GPGLL,2734.85924,S,15305.89387,E,222611.000,A,A*4F +$GPGSA,A,3,16,25,23,20,13,,,,,,,,5.3,2.2,4.8*39 +$GPGST,222611.000,17.7,8.4,21.7,17.4,9.4,19.0,57.8*57 +$GPGSV,3,1,10,16,48,116,39,25,39,268,32,23,58,174,35,20,71,337,26*72 +$GPGSV,3,2,10,19,02,029,,04,06,242,31,13,31,223,39,27,19,284,24*70 +$GPGSV,3,3,10,11,06,338,,03,14,055,26*74 +$GPRMC,222611.000,A,2734.85924,S,15305.89387,E,24.9,61.4,030308,11.2,W,A*37 +$GPVTG,61.4,T,72.6,M,24.9,N,46.1,K,A*2F +$GPGGA,222612.000,2734.85516,S,15305.90007,E,1,07,1.2,53.3,M,39.2,M,,*7E +$GPGLL,2734.85516,S,15305.90007,E,222612.000,A,A*42 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222612.000,15.5,16.3,8.2,77.8,8.0,14.7,27.8*50 +$GPGSV,3,1,10,16,48,116,29,25,39,268,29,23,58,174,30,20,71,337,34*7F +$GPGSV,3,2,10,19,02,029,,04,06,242,27,13,31,223,35,27,19,284,28*77 +$GPGSV,3,3,10,11,06,338,,03,14,055,25*77 +$GPRMC,222612.000,A,2734.85516,S,15305.90007,E,24.5,53.1,030308,11.2,W,A*32 +$GPVTG,53.1,T,64.3,M,24.5,N,45.4,K,A*23 +$GPGGA,222613.000,2734.85043,S,15305.90565,E,1,05,1.9,53.2,M,39.2,M,,*73 +$GPGLL,2734.85043,S,15305.90565,E,222613.000,A,A*47 +$GPGSA,A,3,16,25,20,13,27,,,,,,,,3.7,1.9,3.2*3A +$GPGST,222613.000,21.1,11.0,20.9,21.8,11.7,18.1,48.5*55 +$GPGSV,3,1,10,16,48,116,30,25,39,268,32,23,58,174,24,20,71,337,36*7A +$GPGSV,3,2,10,19,02,029,,04,06,242,22,13,31,223,35,27,19,284,29*73 +$GPGSV,3,3,10,11,06,338,,03,14,055,29*7B +$GPRMC,222613.000,A,2734.85043,S,15305.90565,E,24.9,45.4,030308,11.2,W,A*39 +$GPVTG,45.4,T,56.6,M,24.9,N,46.1,K,A*2F +$GPGGA,222614.000,2734.84523,S,15305.91064,E,1,05,1.9,53.2,M,39.2,M,,*73 +$GPGLL,2734.84523,S,15305.91064,E,222614.000,A,A*47 +$GPGSA,A,3,16,25,20,13,27,,,,,,,,3.7,1.9,3.2*3A +$GPGST,222614.000,18.8,16.8,28.3,28.1,18.2,24.0,43.4*54 +$GPGSV,3,1,10,16,48,116,27,25,39,268,30,23,58,174,32,20,71,337,35*7A +$GPGSV,3,2,10,19,02,029,,04,06,242,28,13,31,223,26,27,19,284,32*71 +$GPGSV,3,3,10,11,06,338,,03,14,055,30*73 +$GPRMC,222614.000,A,2734.84523,S,15305.91064,E,24.7,40.2,030308,11.2,W,A*34 +$GPVTG,40.2,T,51.4,M,24.7,N,45.7,K,A*22 +$GPGGA,222615.000,2734.84002,S,15305.91547,E,1,07,1.2,53.0,M,39.2,M,,*7B +$GPGLL,2734.84002,S,15305.91547,E,222615.000,A,A*44 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222615.000,19.7,18.9,7.9,71.0,8.8,16.5,18.9*52 +$GPGSV,3,1,10,16,48,116,25,25,39,268,26,23,58,174,35,20,71,337,34*79 +$GPGSV,3,2,10,19,02,029,,04,06,242,24,13,31,223,25,27,19,284,30*7C +$GPGSV,3,3,10,11,06,338,,03,14,055,29*7B +$GPRMC,222615.000,A,2734.84002,S,15305.91547,E,24.1,38.7,030308,11.2,W,A*3B +$GPVTG,38.7,T,49.9,M,24.1,N,44.7,K,A*2B +$GPGGA,222616.000,2734.83488,S,15305.92024,E,1,07,1.2,53.1,M,39.2,M,,*7B +$GPGLL,2734.83488,S,15305.92024,E,222616.000,A,A*45 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222616.000,18.1,15.8,11.2,70.8,10.8,14.1,19.9*51 +$GPGSV,3,1,10,16,48,116,27,25,39,268,29,23,58,174,34,20,71,337,30*71 +$GPGSV,3,2,10,19,02,029,,04,06,242,27,13,31,223,24,27,19,284,28*77 +$GPGSV,3,3,10,11,06,338,,03,14,055,32*71 +$GPRMC,222616.000,A,2734.83488,S,15305.92024,E,24.0,39.3,030308,11.2,W,A*3E +$GPVTG,39.3,T,50.5,M,24.0,N,44.5,K,A*29 +$GPGGA,222617.000,2734.82955,S,15305.92505,E,1,07,1.2,53.2,M,39.2,M,,*73 +$GPGLL,2734.82955,S,15305.92505,E,222617.000,A,A*4E +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222617.000,14.7,13.3,9.2,70.7,8.9,11.8,16.8*5B +$GPGSV,3,1,10,16,48,116,23,25,39,268,30,23,58,174,35,20,71,337,33*7F +$GPGSV,3,2,10,19,02,029,,04,06,242,25,13,31,223,25,27,19,284,26*7A +$GPGSV,3,3,10,11,06,338,,03,14,055,32*71 +$GPRMC,222617.000,A,2734.82955,S,15305.92505,E,24.7,38.8,030308,11.2,W,A*38 +$GPVTG,38.8,T,50.0,M,24.7,N,45.7,K,A*22 +$GPGGA,222618.000,2734.82405,S,15305.92991,E,1,07,1.2,53.5,M,39.2,M,,*72 +$GPGLL,2734.82405,S,15305.92991,E,222618.000,A,A*48 +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222618.000,13.7,14.7,7.8,77.2,7.6,13.2,24.3*54 +$GPGSV,3,1,10,16,48,116,27,25,39,268,30,23,58,174,36,20,71,337,32*79 +$GPGSV,3,2,10,19,02,029,,04,06,242,31,13,31,223,25,27,19,283,23*7D +$GPGSV,3,3,10,11,06,338,,03,14,056,32*72 +$GPRMC,222618.000,A,2734.82405,S,15305.92991,E,25.3,38.3,030308,11.2,W,A*30 +$GPVTG,38.3,T,49.5,M,25.3,N,46.8,K,A*2D +$GPGGA,222619.000,2734.81867,S,15305.93485,E,1,07,1.2,53.8,M,39.2,M,,*7C +$GPGLL,2734.81867,S,15305.93485,E,222619.000,A,A*4B +$GPGSA,A,3,16,25,23,20,13,27,03,,,,,,2.1,1.2,1.7*33 +$GPGST,222619.000,20.5,18.3,9.9,85.5,9.1,16.7,27.9*5A +$GPGSV,3,1,10,16,48,116,27,25,39,268,30,23,58,174,35,20,71,337,33*7B +$GPGSV,3,2,10,19,02,029,,04,06,242,29,13,31,223,23,27,19,283,23*72 +$GPGSV,3,3,10,11,06,338,,03,14,056,32*72 +$GPRMC,222619.000,A,2734.81867,S,15305.93485,E,25.3,39.2,030308,11.2,W,A*33 +$GPVTG,39.2,T,50.4,M,25.3,N,46.8,K,A*24 +$GPGGA,222620.000,2734.81344,S,15305.93979,E,1,05,1.6,53.8,M,39.2,M,,*74 +$GPGLL,2734.81344,S,15305.93979,E,222620.000,A,A*45 +$GPGSA,A,3,16,23,20,13,03,,,,,,,,2.5,1.6,1.9*3F +$GPGST,222620.000,18.2,14.7,21.1,9.8,19.1,13.7,29.8*64 +$GPGSV,3,1,10,16,48,116,26,25,39,268,30,23,58,174,33,20,71,337,34*7B +$GPGSV,3,2,10,19,02,029,,04,06,242,24,13,31,223,25,27,19,283,29*73 +$GPGSV,3,3,10,11,06,338,,03,14,056,33*73 +$GPRMC,222620.000,A,2734.81344,S,15305.93979,E,24.7,39.5,030308,11.2,W,A*3F +$GPVTG,39.5,T,50.7,M,24.7,N,45.8,K,A*26 +$GPGGA,222621.000,2734.80838,S,15305.94469,E,1,06,1.3,53.5,M,39.2,M,,*74 +$GPGLL,2734.80838,S,15305.94469,E,222621.000,A,A*4E +$GPGSA,A,3,16,25,23,20,13,03,,,,,,,2.3,1.3,1.9*3B +$GPGST,222621.000,14.6,11.8,13.4,39.2,11.7,11.4,21.8*5D +$GPGSV,3,1,10,16,48,116,25,25,39,268,31,23,58,174,33,20,71,337,37*7A +$GPGSV,3,2,10,19,03,029,,04,06,242,24,13,31,223,24,27,19,283,36*7D +$GPGSV,3,3,10,11,06,338,,03,14,056,32*72 +$GPRMC,222621.000,A,2734.80838,S,15305.94469,E,24.1,40.1,030308,11.2,W,A*38 +$GPVTG,40.1,T,51.3,M,24.1,N,44.7,K,A*21 +$GPGGA,222622.000,2734.80354,S,15305.94956,E,1,06,1.7,53.3,M,39.2,M,,*75 +$GPGLL,2734.80354,S,15305.94956,E,222622.000,A,A*4D +$GPGSA,A,3,16,25,23,20,13,27,,,,,,,3.3,1.7,2.9*3B +$GPGST,222622.000,13.2,10.2,17.1,41.6,12.5,13.2,30.8*59 +$GPGSV,3,1,10,16,48,116,26,25,39,268,31,23,58,174,26,20,71,337,39*73 +$GPGSV,3,2,10,19,03,029,,04,06,242,24,13,31,223,28,27,19,283,36*71 +$GPGSV,3,3,10,11,06,338,,03,14,056,35*75 +$GPRMC,222622.000,A,2734.80354,S,15305.94956,E,23.6,41.3,030308,11.2,W,A*38 +$GPVTG,41.3,T,52.5,M,23.6,N,43.7,K,A*20 +$GPGGA,222623.000,2734.79878,S,15305.95470,E,1,05,1.9,52.8,M,39.2,M,,*78 +$GPGLL,2734.79878,S,15305.95470,E,222623.000,A,A*47 +$GPGSA,A,3,16,25,20,13,27,,,,,,,,3.7,1.9,3.2*3A +$GPGST,222623.000,14.7,24.8,16.6,50.3,19.9,18.6,39.3*5E +$GPGSV,3,1,11,16,48,116,24,25,39,268,32,23,58,174,24,20,71,337,38*70 +$GPGSV,3,2,11,19,03,029,,04,06,242,24,13,31,223,32,27,19,283,34*79 +$GPGSV,3,3,11,11,06,338,,01,,,18,03,14,056,34*7D +$GPRMC,222623.000,A,2734.79878,S,15305.95470,E,24.0,43.4,030308,11.2,W,A*36 +$GPVTG,43.4,T,54.6,M,24.0,N,44.4,K,A*25 +$GPGGA,222624.000,2734.79400,S,15305.96019,E,1,05,1.9,52.3,M,39.2,M,,*7F +$GPGLL,2734.79400,S,15305.96019,E,222624.000,A,A*4B +$GPGSA,A,3,16,25,20,27,03,,,,,,,,2.7,1.9,2.0*39 +$GPGST,222624.000,12.3,24.0,7.9,80.2,8.0,21.7,15.3*5C +$GPGSV,3,1,11,16,48,116,23,25,39,268,32,23,58,174,26,20,71,337,37*7A +$GPGSV,3,2,11,19,03,029,,04,06,242,24,13,31,223,35,27,19,283,25*7E +$GPGSV,3,3,11,11,06,338,,01,,,18,03,14,056,31*78 +$GPRMC,222624.000,A,2734.79400,S,15305.96019,E,24.7,45.3,030308,11.2,W,A*3C +$GPVTG,45.3,T,56.6,M,24.7,N,45.7,K,A*23 +$GPGGA,222625.000,2734.78953,S,15305.96595,E,1,05,1.9,51.8,M,39.2,M,,*7D +$GPGLL,2734.78953,S,15305.96595,E,222625.000,A,A*41 +$GPGSA,A,3,16,25,20,13,27,,,,,,,,3.7,1.9,3.2*3A +$GPGST,222625.000,24.0,12.2,19.9,37.5,14.2,16.0,36.1*57 +$GPGSV,3,1,11,16,48,116,30,25,39,268,32,23,58,174,23,20,71,337,37*7D +$GPGSV,3,2,11,19,03,029,,04,06,242,25,13,31,223,36,27,19,283,25*7C +$GPGSV,3,3,11,11,06,338,,01,,,18,03,14,056,24*7C +$GPRMC,222625.000,A,2734.78953,S,15305.96595,E,24.8,47.9,030308,11.2,W,A*31 +$GPVTG,47.9,T,59.1,M,24.8,N,46.0,K,A*28 +$GPGGA,222626.000,2734.78516,S,15305.97172,E,1,05,2.5,51.1,M,39.2,M,,*79 +$GPGLL,2734.78516,S,15305.97172,E,222626.000,A,A*43 +$GPGSA,A,3,16,25,20,04,13,,,,,,,,4.2,2.5,3.3*37 +$GPGST,222626.000,20.2,12.6,23.1,19.8,13.0,20.2,38.4*5F +$GPGSV,3,1,11,16,48,116,31,25,39,268,32,23,58,174,25,20,71,337,38*75 +$GPGSV,3,2,11,19,03,029,,04,06,242,25,13,31,223,36,27,19,283,23*7A +$GPGSV,3,3,11,11,06,338,,01,,,18,03,14,056,24*7C +$GPRMC,222626.000,A,2734.78516,S,15305.97172,E,24.7,48.3,030308,11.2,W,A*39 +$GPVTG,48.3,T,59.5,M,24.7,N,45.7,K,A*22 +$GPGGA,222627.000,2734.78079,S,15305.97737,E,1,04,2.5,50.5,M,39.2,M,,*77 +$GPGLL,2734.78079,S,15305.97737,E,222627.000,A,A*49 +$GPGSA,A,3,16,23,20,13,,,,,,,,,5.8,2.5,5.2*39 +$GPGST,222627.000,16.3,12.4,42.2,11.3,13.5,37.9,90.0*51 +$GPGSV,3,1,11,16,48,116,28,25,39,268,32,23,58,174,25,20,71,337,37*72 +$GPGSV,3,2,11,19,03,029,,04,06,242,29,13,31,223,37,27,19,283,30*75 +$GPGSV,3,3,11,11,06,338,,01,,,18,03,14,056,24*7C +$GPRMC,222627.000,A,2734.78079,S,15305.97737,E,24.3,48.1,030308,11.2,W,A*35 +$GPVTG,48.1,T,59.3,M,24.3,N,45.1,K,A*24 +$GPGGA,222628.000,2734.77637,S,15305.98293,E,1,07,1.3,50.2,M,39.2,M,,*7E +$GPGLL,2734.77637,S,15305.98293,E,222628.000,A,A*41 +$GPGSA,A,3,16,25,23,04,13,27,03,,,,,,2.2,1.3,1.8*38 +$GPGST,222628.000,13.2,14.5,8.8,76.9,8.4,13.0,22.0*5F +$GPGSV,3,1,11,16,48,116,30,25,39,268,32,23,58,174,27,20,71,337,37*79 +$GPGSV,3,2,11,19,03,029,,04,06,242,32,13,31,223,37,27,19,283,28*76 +$GPGSV,3,3,11,11,06,338,,01,,,18,03,14,056,25*7D +$GPRMC,222628.000,A,2734.77637,S,15305.98293,E,23.9,48.5,030308,11.2,W,A*34 +$GPVTG,48.5,T,59.7,M,23.9,N,44.3,K,A*2A +$GPGGA,222629.000,2734.77226,S,15305.98850,E,1,08,1.1,49.5,M,39.2,M,,*7C +$GPGLL,2734.77226,S,15305.98850,E,222629.000,A,A*41 +$GPGSA,A,3,16,25,23,20,04,13,27,03,,,,,1.7,1.1,1.3*35 +$GPGST,222629.000,11.0,9.5,7.3,89.6,6.7,8.6,14.3*5E +$GPGSV,3,1,11,16,48,116,31,25,39,268,32,23,58,174,25,20,71,337,36*7B +$GPGSV,3,2,11,19,03,029,,04,06,242,30,13,31,223,37,27,19,283,28*74 +$GPGSV,3,3,11,11,06,338,,01,,,18,03,14,056,27*7F +$GPRMC,222629.000,A,2734.77226,S,15305.98850,E,23.5,49.4,030308,11.2,W,A*38 +$GPVTG,49.4,T,60.6,M,23.5,N,43.5,K,A*2C +$GPGGA,222630.000,2734.76859,S,15305.99361,E,1,04,3.5,49.4,M,39.2,M,,*74 +$GPGLL,2734.76859,S,15305.99361,E,222630.000,A,A*42 +$GPGSA,A,3,16,25,20,27,,,,,,,,,4.9,3.5,3.4*39 +$GPGST,222630.000,13.4,7.9,50.0,19.9,17.0,43.1,69.5*66 +$GPGSV,3,1,11,16,48,116,31,25,39,268,30,23,58,174,22,20,71,337,36*7E +$GPGSV,3,2,11,19,03,029,,04,06,242,32,13,31,223,35,27,19,283,24*78 +$GPGSV,3,3,11,11,06,338,18,01,,,18,03,14,056,29*78 +$GPRMC,222630.000,A,2734.76859,S,15305.99361,E,21.1,50.3,030308,11.2,W,A*32 +$GPVTG,50.3,T,61.5,M,21.1,N,39.1,K,A*2E +$GPGGA,222631.000,2734.76498,S,15305.99809,E,1,08,1.1,48.2,M,39.2,M,,*7C +$GPGLL,2734.76498,S,15305.99809,E,222631.000,A,A*47 +$GPGSA,A,3,16,25,23,20,04,13,27,03,,,,,1.7,1.1,1.3*35 +$GPGST,222631.000,18.4,10.8,5.8,77.6,5.6,9.7,13.2*63 +$GPGSV,3,1,11,16,48,116,32,25,39,268,31,23,58,174,22,20,71,337,35*7F +$GPGSV,3,2,11,19,03,029,,04,06,242,30,13,31,223,36,27,19,283,25*78 +$GPGSV,3,3,11,11,06,338,18,01,,,18,03,14,056,33*73 +$GPRMC,222631.000,A,2734.76498,S,15305.99809,E,19.4,47.4,030308,11.2,W,A*38 +$GPVTG,47.4,T,58.6,M,19.4,N,35.9,K,A*2C +$GPGGA,222632.000,2734.76359,S,15306.00162,E,1,08,1.1,49.6,M,39.2,M,,*77 +$GPGLL,2734.76359,S,15306.00162,E,222632.000,A,A*49 +$GPGSA,A,3,16,25,23,20,04,13,27,03,,,,,1.7,1.1,1.3*35 +$GPGST,222632.000,18.1,14.9,6.7,53.4,9.5,11.5,16.1*5A +$GPGSV,3,1,11,16,48,116,30,25,39,268,31,23,58,174,31,20,71,337,31*7B +$GPGSV,3,2,11,19,03,029,,04,06,242,22,13,31,223,37,27,19,283,29*76 +$GPGSV,3,3,11,11,06,338,24,01,,,18,03,14,056,25*7B +$GPRMC,222632.000,A,2734.76359,S,15306.00162,E,12.4,65.5,030308,11.2,W,A*3C +$GPVTG,65.5,T,76.7,M,12.4,N,22.9,K,A*2D +$GPGGA,222633.000,2734.76254,S,15306.00553,E,1,08,1.1,49.0,M,39.2,M,,*7A +$GPGLL,2734.76254,S,15306.00553,E,222633.000,A,A*42 +$GPGSA,A,3,16,25,23,20,04,13,27,03,,,,,1.7,1.1,1.3*35 +$GPGST,222633.000,13.9,12.3,5.8,55.3,7.8,9.7,13.7*62 +$GPGSV,3,1,10,16,48,116,35,25,39,268,35,23,58,174,30,20,71,337,35*7E +$GPGSV,3,2,10,19,03,029,,04,06,242,26,13,31,223,39,27,19,283,26*72 +$GPGSV,3,3,10,11,06,338,24,03,14,056,30*76 +$GPRMC,222633.000,A,2734.76254,S,15306.00553,E,12.9,72.5,030308,11.2,W,A*3C +$GPVTG,72.5,T,83.7,M,12.9,N,23.9,K,A*2D +$GPGGA,222634.000,2734.76305,S,15306.00850,E,1,08,1.1,48.4,M,39.2,M,,*73 +$GPGLL,2734.76305,S,15306.00850,E,222634.000,A,A*4E +$GPGSA,A,3,16,25,23,20,04,13,27,03,,,,,1.7,1.1,1.3*35 +$GPGST,222634.000,11.3,10.3,5.3,58.0,6.5,8.4,12.0*62 +$GPGSV,3,1,10,16,48,116,33,25,39,268,38,23,58,174,30,20,71,337,30*70 +$GPGSV,3,2,10,19,03,029,,04,06,242,29,13,31,223,34,27,19,283,30*77 +$GPGSV,3,3,10,11,06,338,23,03,14,056,25*75 +$GPRMC,222634.000,A,2734.76305,S,15306.00850,E,9.4,99.3,030308,11.2,W,A*04 +$GPVTG,99.3,T,110.5,M,9.4,N,17.3,K,A*2D +$GPGGA,222635.000,2734.76418,S,15306.01089,E,1,08,1.1,47.9,M,39.2,M,,*76 +$GPGLL,2734.76418,S,15306.01089,E,222635.000,A,A*49 +$GPGSA,A,3,16,25,23,20,04,13,27,03,,,,,1.7,1.1,1.3*35 +$GPGST,222635.000,13.4,9.3,7.7,66.3,7.3,8.3,14.0*50 +$GPGSV,3,1,10,16,48,116,36,25,39,268,31,23,58,174,37,20,71,337,36*7D +$GPGSV,3,2,10,19,03,029,,04,06,242,31,13,31,223,34,27,19,283,31*7F +$GPGSV,3,3,10,11,06,338,23,03,14,056,23*73 +$GPRMC,222635.000,A,2734.76418,S,15306.01089,E,8.3,116.7,030308,11.2,W,A*37 +$GPVTG,116.7,T,127.9,M,8.3,N,15.4,K,A*14 +$GPGGA,222636.000,2734.76577,S,15306.01258,E,1,07,1.7,47.9,M,39.2,M,,*7A +$GPGLL,2734.76577,S,15306.01258,E,222636.000,A,A*4C +$GPGSA,A,3,16,25,23,20,04,13,27,,,,,,2.8,1.7,2.3*3F +$GPGST,222636.000,10.6,11.9,8.1,45.4,9.3,9.3,18.8*65 +$GPGSV,3,1,10,16,48,116,41,25,39,268,35,23,58,174,40,20,71,337,33*7C +$GPGSV,3,2,10,19,03,029,,04,06,242,32,13,31,223,28,27,19,283,34*74 +$GPGSV,3,3,10,11,06,338,25,03,14,056,23*75 +$GPRMC,222636.000,A,2734.76577,S,15306.01258,E,7.6,135.7,030308,11.2,W,A*39 +$GPVTG,135.7,T,146.9,M,7.6,N,14.1,K,A*1C +$GPGGA,222637.000,2734.76759,S,15306.01422,E,1,07,1.7,47.8,M,39.2,M,,*7F +$GPGLL,2734.76759,S,15306.01422,E,222637.000,A,A*48 +$GPGSA,A,3,16,25,23,20,04,13,27,,,,,,2.8,1.7,2.3*3F +$GPGST,222637.000,8.9,7.2,10.9,44.4,8.4,8.5,17.1*59 +$GPGSV,3,1,10,16,48,116,44,25,39,268,36,23,58,174,33,20,71,337,33*7E +$GPGSV,3,2,10,19,03,029,,04,06,242,24,13,31,223,29,27,19,283,36*70 +$GPGSV,3,3,10,11,06,338,25,03,14,056,23*75 +$GPRMC,222637.000,A,2734.76759,S,15306.01422,E,8.0,140.6,030308,11.2,W,A*37 +$GPVTG,140.6,T,151.8,M,8.0,N,14.8,K,A*18 +$GPGGA,222638.000,2734.76957,S,15306.01592,E,1,06,2.0,47.6,M,39.2,M,,*71 +$GPGLL,2734.76957,S,15306.01592,E,222638.000,A,A*4D +$GPGSA,A,3,16,25,23,20,04,13,,,,,,,3.2,2.0,2.6*30 +$GPGST,222638.000,10.4,8.9,16.3,14.1,8.7,14.6,19.9*51 +$GPGSV,3,1,10,16,48,116,38,25,39,268,30,23,58,174,29,20,71,337,29*73 +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,29*7B +$GPGSV,3,3,10,11,06,338,21,03,14,056,23*71 +$GPRMC,222638.000,A,2734.76957,S,15306.01592,E,8.8,142.8,030308,11.2,W,A*36 +$GPVTG,142.8,T,154.0,M,8.8,N,16.2,K,A*19 +$GPGGA,222639.000,2734.77109,S,15306.01748,E,1,07,1.7,47.8,M,39.2,M,,*7C +$GPGLL,2734.77109,S,15306.01748,E,222639.000,A,A*4B +$GPGSA,A,3,16,25,23,20,04,13,27,,,,,,2.8,1.7,2.3*3F +$GPGST,222639.000,9.5,9.6,14.7,43.8,11.3,11.5,19.8*51 +$GPGSV,3,1,10,16,48,116,37,25,39,268,32,23,58,174,37,20,71,337,29*71 +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,27*75 +$GPGSV,3,3,10,11,06,338,21,03,14,056,23*71 +$GPRMC,222639.000,A,2734.77109,S,15306.01748,E,7.1,137.2,030308,11.2,W,A*3E +$GPVTG,137.2,T,148.4,M,7.1,N,13.1,K,A*18 +$GPGGA,222640.000,2734.77216,S,15306.02018,E,1,04,2.6,47.8,M,39.2,M,,*7F +$GPGLL,2734.77216,S,15306.02018,E,222640.000,A,A*49 +$GPGSA,A,3,16,25,23,13,,,,,,,,,6.2,2.6,5.6*32 +$GPGST,222640.000,20.0,11.7,22.3,7.0,10.9,20.3,46.8*65 +$GPGSV,3,1,10,16,48,116,32,25,39,268,29,23,58,174,24,20,71,337,25*70 +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,25*77 +$GPGSV,3,3,10,11,06,338,21,03,14,056,23*71 +$GPRMC,222640.000,A,2734.77216,S,15306.02018,E,9.4,114.2,030308,11.2,W,A*36 +$GPVTG,114.2,T,125.4,M,9.4,N,17.4,K,A*18 +$GPGGA,222641.000,2734.77543,S,15306.02149,E,1,04,2.6,47.7,M,39.2,M,,*73 +$GPGLL,2734.77543,S,15306.02149,E,222641.000,A,A*4A +$GPGSA,A,3,16,25,23,13,,,,,,,,,6.2,2.6,5.6*32 +$GPGST,222641.000,18.3,40.3,13.2,79.5,13.7,36.3,59.1*5E +$GPGSV,3,1,10,16,48,116,34,25,39,268,23,23,58,174,26,20,71,337,27*7C +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*71 +$GPGSV,3,3,10,11,06,338,21,03,14,056,23*71 +$GPRMC,222641.000,A,2734.77543,S,15306.02149,E,12.5,160.9,030308,11.2,W,A*06 +$GPVTG,160.9,T,172.1,M,12.5,N,23.1,K,A*2E +$GPGGA,222642.000,2734.77709,S,15306.02300,E,1,04,2.6,47.7,M,39.2,M,,*73 +$GPGLL,2734.77709,S,15306.02300,E,222642.000,A,A*4A +$GPGSA,A,3,16,20,04,13,,,,,,,,,4.3,2.6,3.4*35 +$GPGST,222642.000,15.8,14.5,71.8,20.7,26.3,61.6,97.2*58 +$GPGSV,3,1,10,16,48,116,41,25,40,268,23,23,58,174,27,20,71,337,34*73 +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*71 +$GPGSV,3,3,10,11,06,338,21,03,14,056,23*71 +$GPRMC,222642.000,A,2734.77709,S,15306.02300,E,7.5,140.7,030308,11.2,W,A*3E +$GPVTG,140.7,T,151.9,M,7.5,N,13.9,K,A*14 +$GPGGA,222643.000,2734.77831,S,15306.02427,E,1,04,2.6,47.5,M,39.2,M,,*76 +$GPGLL,2734.77831,S,15306.02427,E,222643.000,A,A*4D +$GPGSA,A,3,16,20,04,13,,,,,,,,,4.3,2.6,3.4*35 +$GPGST,222643.000,12.9,15.5,112.8,21.4,39.9,96.1,154.3*5C +$GPGSV,3,1,10,16,48,116,41,25,40,268,23,23,58,174,26,20,71,337,35*73 +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*71 +$GPGSV,3,3,10,11,06,338,21,03,14,056,23*71 +$GPRMC,222643.000,A,2734.77831,S,15306.02427,E,5.6,136.2,030308,11.2,W,A*3C +$GPVTG,136.2,T,147.4,M,5.6,N,10.4,K,A*15 +$GPGGA,222644.000,2734.77900,S,15306.02522,E,1,04,2.5,47.4,M,39.2,M,,*74 +$GPGLL,2734.77900,S,15306.02522,E,222644.000,A,A*4D +$GPGSA,A,3,16,23,20,13,,,,,,,,,5.8,2.5,5.2*39 +$GPGST,222644.000,11.9,32.3,39.9,23.1,35.5,30.7,165.7*69 +$GPGSV,3,1,10,16,48,116,38,25,40,268,23,23,58,174,25,20,71,337,37*7C +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*71 +$GPGSV,3,3,10,11,06,338,21,03,14,056,26*74 +$GPRMC,222644.000,A,2734.77900,S,15306.02522,E,3.7,127.8,030308,11.2,W,A*31 +$GPVTG,127.8,T,139.0,M,3.7,N,6.9,K,A*2F +$GPGGA,222645.000,2734.77991,S,15306.02594,E,1,04,3.4,47.4,M,39.2,M,,*70 +$GPGLL,2734.77991,S,15306.02594,E,222645.000,A,A*49 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.4,5.0*30 +$GPGST,222645.000,15.3,40.1,15.0,66.6,34.1,19.2,69.1*50 +$GPGSV,3,1,10,16,48,116,33,25,40,268,23,23,58,174,23,20,71,337,35*73 +$GPGSV,3,2,10,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*71 +$GPGSV,3,3,10,11,06,338,21,03,14,056,30*73 +$GPRMC,222645.000,A,2734.77991,S,15306.02594,E,3.9,145.4,030308,11.2,W,A*33 +$GPVTG,145.4,T,156.6,M,3.9,N,7.2,K,A*2C +$GPGGA,222646.000,2734.78074,S,15306.02652,E,1,04,3.4,47.4,M,39.2,M,,*77 +$GPGLL,2734.78074,S,15306.02652,E,222646.000,A,A*4E +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.4,5.0*30 +$GPGST,222646.000,14.8,53.5,15.6,78.9,48.1,16.9,76.9*50 +$GPGSV,3,1,11,16,48,116,32,25,40,268,23,23,58,174,26,20,71,337,32*71 +$GPGSV,3,2,11,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*70 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,34*4D +$GPRMC,222646.000,A,2734.78074,S,15306.02652,E,3.5,149.5,030308,11.2,W,A*35 +$GPVTG,149.5,T,160.7,M,3.5,N,6.6,K,A*2C +$GPGGA,222647.000,2734.78140,S,15306.02704,E,1,04,3.4,47.3,M,39.2,M,,*75 +$GPGLL,2734.78140,S,15306.02704,E,222647.000,A,A*4B +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.4,5.0*30 +$GPGST,222647.000,11.7,42.4,13.1,77.7,38.0,14.3,60.9*53 +$GPGSV,3,1,11,16,48,116,37,25,40,268,23,23,58,174,31,20,71,337,36*76 +$GPGSV,3,2,11,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*70 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,34*4D +$GPRMC,222647.000,A,2734.78140,S,15306.02704,E,2.8,145.4,030308,11.2,W,A*31 +$GPVTG,145.4,T,156.6,M,2.8,N,5.1,K,A*2D +$GPGGA,222648.000,2734.78183,S,15306.02753,E,1,04,3.5,47.2,M,39.2,M,,*77 +$GPGLL,2734.78183,S,15306.02753,E,222648.000,A,A*49 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222648.000,11.3,35.2,16.9,76.3,31.5,16.9,53.8*53 +$GPGSV,3,1,11,16,48,116,35,25,40,267,23,23,58,174,37,20,71,337,35*7E +$GPGSV,3,2,11,19,03,029,,04,06,242,28,13,31,223,31,27,19,283,23*70 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,35*4C +$GPRMC,222648.000,A,2734.78183,S,15306.02753,E,2.1,135.8,030308,11.2,W,A*31 +$GPVTG,135.8,T,147.1,M,2.1,N,3.9,K,A*26 +$GPGGA,222649.000,2734.78197,S,15306.02773,E,1,04,3.5,46.8,M,39.2,M,,*7A +$GPGLL,2734.78197,S,15306.02773,E,222649.000,A,A*4F +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222649.000,13.9,60.8,14.8,84.0,55.3,14.7,79.7*52 +$GPGSV,3,1,11,16,48,116,31,25,40,267,23,23,58,174,36,20,71,337,31*7F +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,19,283,23*78 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,29*41 +$GPRMC,222649.000,A,2734.78197,S,15306.02773,E,0.6,148.6,030308,11.2,W,A*36 +$GPVTG,148.6,T,159.8,M,0.6,N,1.2,K,A*28 +$GPGGA,222650.000,2734.78195,S,15306.02785,E,1,04,3.5,46.4,M,39.2,M,,*75 +$GPGLL,2734.78195,S,15306.02785,E,222650.000,A,A*4C +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222650.000,11.4,48.0,12.9,83.3,43.6,12.8,63.8*5B +$GPGSV,3,1,11,16,48,116,30,25,40,267,23,23,58,174,33,20,71,337,30*7A +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,19,283,23*78 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,26*4E +$GPRMC,222650.000,A,2734.78195,S,15306.02785,E,0.2,6.4,030308,11.2,W,A*38 +$GPVTG,6.4,T,17.6,M,0.2,N,0.3,K,A*10 +$GPGGA,222651.000,2734.78191,S,15306.02798,E,1,04,3.5,45.9,M,39.2,M,,*72 +$GPGLL,2734.78191,S,15306.02798,E,222651.000,A,A*45 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222651.000,9.8,40.0,12.0,82.4,36.3,11.9,53.8*6E +$GPGSV,3,1,11,16,48,116,28,25,40,267,,23,58,174,35,20,71,337,26*73 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,19,283,*79 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,28*40 +$GPRMC,222651.000,A,2734.78191,S,15306.02798,E,0.1,352.6,030308,11.2,W,A*32 +$GPVTG,352.6,T,3.8,M,0.1,N,0.3,K,A*28 +$GPGGA,222652.000,2734.78198,S,15306.02797,E,1,04,3.5,45.9,M,39.2,M,,*77 +$GPGLL,2734.78198,S,15306.02797,E,222652.000,A,A*40 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222652.000,21.7,41.2,15.0,83.8,37.5,14.2,91.6*58 +$GPGSV,3,1,11,16,48,116,24,25,40,267,,23,58,174,23,20,71,337,26*78 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,19,283,*79 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,24*4C +$GPRMC,222652.000,A,2734.78198,S,15306.02797,E,0.2,263.5,030308,11.2,W,A*34 +$GPVTG,263.5,T,274.7,M,0.2,N,0.3,K,A*26 +$GPGGA,222653.000,2734.78226,S,15306.02793,E,1,04,3.5,45.9,M,39.2,M,,*74 +$GPGLL,2734.78226,S,15306.02793,E,222653.000,A,A*43 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222653.000,18.8,36.4,20.7,81.4,33.0,19.3,78.6*5F +$GPGSV,3,1,11,16,48,116,24,25,40,267,,23,58,174,25,20,71,337,25*7D +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,24*4C +$GPRMC,222653.000,A,2734.78226,S,15306.02793,E,0.8,188.1,030308,11.2,W,A*3F +$GPVTG,188.1,T,199.3,M,0.8,N,1.5,K,A*2D +$GPGGA,222654.000,2734.78231,S,15306.02789,E,1,04,3.5,45.9,M,39.2,M,,*7E +$GPGLL,2734.78231,S,15306.02789,E,222654.000,A,A*49 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222654.000,14.4,23.3,16.5,69.8,20.7,16.0,57.9*5D +$GPGSV,3,1,11,16,48,116,34,25,40,267,,23,58,174,34,20,71,337,31*79 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,31*48 +$GPRMC,222654.000,A,2734.78231,S,15306.02789,E,0.1,145.6,030308,11.2,W,A*3A +$GPVTG,145.6,T,156.8,M,0.1,N,0.2,K,A*2C +$GPGGA,222655.000,2734.78251,S,15306.02806,E,1,04,3.5,45.8,M,39.2,M,,*70 +$GPGLL,2734.78251,S,15306.02806,E,222655.000,A,A*46 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222655.000,12.0,28.1,14.0,78.7,25.3,13.6,52.5*54 +$GPGSV,3,1,11,16,48,116,40,25,40,267,,23,58,174,38,20,71,337,35*72 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,36*4F +$GPRMC,222655.000,A,2734.78251,S,15306.02806,E,0.7,135.5,030308,11.2,W,A*37 +$GPVTG,135.5,T,146.8,M,0.7,N,1.2,K,A*2E +$GPGGA,222656.000,2734.78273,S,15306.02823,E,1,04,3.5,45.7,M,39.2,M,,*7B +$GPGLL,2734.78273,S,15306.02823,E,222656.000,A,A*42 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222656.000,9.9,24.8,12.2,76.6,22.2,12.0,44.0*69 +$GPGSV,3,1,11,16,48,116,38,25,40,267,,23,58,174,36,20,71,337,30*76 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,35*4C +$GPRMC,222656.000,A,2734.78273,S,15306.02823,E,0.5,135.5,030308,11.2,W,A*31 +$GPVTG,135.5,T,146.7,M,0.5,N,1.0,K,A*21 +$GPGGA,222657.000,2734.78312,S,15306.02858,E,1,04,3.5,45.5,M,39.2,M,,*72 +$GPGLL,2734.78312,S,15306.02858,E,222657.000,A,A*49 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222657.000,8.4,22.4,10.9,74.9,19.9,11.0,38.1*60 +$GPGSV,3,1,11,16,48,116,37,25,40,267,,23,58,174,36,20,71,337,30*79 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,21,08,00,298,,03,14,056,34*4D +$GPRMC,222657.000,A,2734.78312,S,15306.02858,E,1.3,137.6,030308,11.2,W,A*3C +$GPVTG,137.6,T,148.8,M,1.3,N,2.4,K,A*21 +$GPGGA,222658.000,2734.78370,S,15306.02910,E,1,04,3.5,45.4,M,39.2,M,,*75 +$GPGLL,2734.78370,S,15306.02910,E,222658.000,A,A*4F +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222658.000,11.5,25.4,19.7,45.8,20.9,20.7,46.1*53 +$GPGSV,3,1,11,16,48,116,34,25,40,267,18,23,58,174,36,20,71,337,32*71 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,32*48 +$GPRMC,222658.000,A,2734.78370,S,15306.02910,E,2.7,141.8,030308,11.2,W,A*32 +$GPVTG,141.8,T,153.0,M,2.7,N,5.0,K,A*28 +$GPGGA,222659.000,2734.78463,S,15306.02956,E,1,04,3.5,45.4,M,39.2,M,,*73 +$GPGLL,2734.78463,S,15306.02956,E,222659.000,A,A*49 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222659.000,10.9,34.8,17.8,79.2,31.4,17.1,52.2*5E +$GPGSV,3,1,11,16,48,116,32,25,40,267,18,23,58,174,37,20,71,337,32*76 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,26*4D +$GPRMC,222659.000,A,2734.78463,S,15306.02956,E,3.6,157.2,030308,11.2,W,A*39 +$GPVTG,157.2,T,168.4,M,3.6,N,6.7,K,A*2D +$GPGGA,222700.000,2734.78553,S,15306.02974,E,1,04,3.5,45.3,M,39.2,M,,*7B +$GPGLL,2734.78553,S,15306.02974,E,222700.000,A,A*46 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222700.000,13.4,30.5,15.4,80.9,27.6,14.6,58.2*5C +$GPGSV,3,1,11,16,48,116,38,25,40,267,18,23,58,174,42,20,71,337,34*78 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,30*4A +$GPRMC,222700.000,A,2734.78553,S,15306.02974,E,3.1,171.1,030308,11.2,W,A*36 +$GPVTG,171.1,T,182.3,M,3.1,N,5.7,K,A*2D +$GPGGA,222701.000,2734.78678,S,15306.02887,E,1,05,1.6,45.4,M,39.2,M,,*7A +$GPGLL,2734.78678,S,15306.02887,E,222701.000,A,A*40 +$GPGSA,A,3,16,23,20,13,03,,,,,,,,2.5,1.6,1.9*3F +$GPGST,222701.000,18.6,22.3,33.4,25.2,29.0,22.6,46.2*53 +$GPGSV,3,1,11,16,48,116,30,25,40,267,18,23,58,174,33,20,71,337,27*74 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,20,27,20,283,*71 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,27*4C +$GPRMC,222701.000,A,2734.78678,S,15306.02887,E,5.1,212.6,030308,11.2,W,A*37 +$GPVTG,212.6,T,223.8,M,5.1,N,9.5,K,A*27 +$GPGGA,222702.000,2734.78726,S,15306.02769,E,1,04,3.5,45.3,M,39.2,M,,*7B +$GPGLL,2734.78726,S,15306.02769,E,222702.000,A,A*46 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.0*31 +$GPGST,222702.000,14.7,49.5,20.8,84.3,45.1,19.5,72.8*5F +$GPGSV,3,1,11,16,48,116,36,25,40,267,18,23,58,174,40,20,71,337,31*71 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,20,27,20,283,*71 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,28*43 +$GPRMC,222702.000,A,2734.78726,S,15306.02769,E,4.2,246.4,030308,11.2,W,A*30 +$GPVTG,246.4,T,257.6,M,4.2,N,7.7,K,A*27 +$GPGGA,222703.000,2734.78745,S,15306.02617,E,1,05,1.6,45.3,M,39.2,M,,*77 +$GPGLL,2734.78745,S,15306.02617,E,222703.000,A,A*4A +$GPGSA,A,3,16,23,20,13,03,,,,,,,,2.5,1.6,1.9*3F +$GPGST,222703.000,12.3,16.0,23.0,21.5,20.3,15.6,33.1*53 +$GPGSV,3,1,11,16,48,116,31,25,40,267,18,23,58,174,29,20,70,337,23*7B +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,22,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,27*4C +$GPRMC,222703.000,A,2734.78745,S,15306.02617,E,4.9,265.5,030308,11.2,W,A*37 +$GPVTG,265.5,T,276.7,M,4.9,N,9.1,K,A*26 +$GPGGA,222704.000,2734.78730,S,15306.02555,E,1,04,3.5,45.3,M,39.2,M,,*77 +$GPGLL,2734.78730,S,15306.02555,E,222704.000,A,A*4A +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.1*30 +$GPGST,222704.000,9.5,34.0,22.1,80.5,30.8,20.6,55.7*69 +$GPGSV,3,1,11,16,48,116,31,25,40,267,18,23,58,174,29,20,70,337,23*7B +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,22,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,31*4B +$GPRMC,222704.000,A,2734.78730,S,15306.02555,E,2.1,286.5,030308,11.2,W,A*34 +$GPVTG,286.5,T,297.7,M,2.1,N,3.9,K,A*28 +$GPGGA,222705.000,2734.78698,S,15306.02452,E,1,04,3.5,45.3,M,39.2,M,,*73 +$GPGLL,2734.78698,S,15306.02452,E,222705.000,A,A*4E +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.1*30 +$GPGST,222705.000,93.8,65.1,31.9,84.0,59.3,29.7,94.1*5F +$GPGSV,3,1,11,16,48,116,29,25,40,267,18,23,58,174,29,20,70,337,23*72 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,22,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,30*4A +$GPRMC,222705.000,A,2734.78698,S,15306.02452,E,3.6,290.3,030308,11.2,W,A*37 +$GPVTG,290.3,T,301.5,M,3.6,N,6.8,K,A*27 +$GPGGA,222706.000,2734.78686,S,15306.02339,E,1,04,3.5,45.3,M,39.2,M,,*75 +$GPGLL,2734.78686,S,15306.02339,E,222706.000,A,A*48 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.1*30 +$GPGST,222706.000,114.3,62.0,48.3,70.3,55.4,45.7,116.3*5B +$GPGSV,3,1,11,16,48,116,24,25,40,267,18,23,58,174,29,20,70,337,23*7F +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,22,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,24*4F +$GPRMC,222706.000,A,2734.78686,S,15306.02339,E,3.4,277.7,030308,11.2,W,A*3E +$GPVTG,277.7,T,288.9,M,3.4,N,6.3,K,A*2F +$GPGGA,222707.000,2734.78679,S,15306.02251,E,1,04,3.5,45.3,M,39.2,M,,*7B +$GPGLL,2734.78679,S,15306.02251,E,222707.000,A,A*46 +$GPGSA,A,3,16,23,20,03,,,,,,,,,6.1,3.5,5.1*30 +$GPGST,222707.000,128.0,90.0,67.0,53.0,75.4,69.6,158.6*55 +$GPGSV,3,1,11,16,48,116,24,25,40,267,18,23,58,174,29,20,70,337,23*7F +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,223,22,27,20,283,*73 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,24*4F +$GPRMC,222707.000,A,2734.78679,S,15306.02251,E,,,030308,,,A*79 +$GPVTG,,T,,M,,N,,K,N*2C +$GPGGA,222708.000,2734.78673,S,15306.02181,E,1,05,1.6,45.3,M,39.2,M,,*70 +$GPGLL,2734.78673,S,15306.02181,E,222708.000,A,A*4D +$GPGSA,A,3,16,23,20,13,03,,,,,,,,2.5,1.6,1.9*3F +$GPGST,222708.000,66.8,93.0,70.5,62.6,69.3,81.1,141.7*6F +$GPGSV,3,1,11,16,48,116,24,25,40,267,,23,58,174,29,20,70,337,23*76 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,222,22,27,20,283,*72 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,24*4F +$GPRMC,222708.000,A,2734.78673,S,15306.02181,E,,,030308,,,A*72 +$GPVTG,,T,,M,,N,,K,N*2C +$GPGGA,222709.000,2734.78668,S,15306.02124,E,1,05,1.6,45.3,M,39.2,M,,*74 +$GPGLL,2734.78668,S,15306.02124,E,222709.000,A,A*49 +$GPGSA,A,2,16,23,20,13,03,,,,,,,,1.8,1.6,0.9*31 +$GPGST,222709.000,89.5,61.2,117.7,37.9,91.7,79.5,4.3*54 +$GPGSV,3,1,11,16,48,116,24,25,40,267,,23,58,174,29,20,70,337,23*76 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,222,22,27,20,283,*72 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,24*4F +$GPRMC,222709.000,A,2734.78668,S,15306.02124,E,,,030308,,,A*76 +$GPVTG,,T,,M,,N,,K,N*2C +$GPGGA,222710.000,2734.78664,S,15306.02080,E,1,05,1.6,45.3,M,39.2,M,,*7F +$GPGLL,2734.78664,S,15306.02080,E,222710.000,A,A*42 +$GPGSA,A,2,16,23,20,13,03,,,,,,,,1.8,1.6,0.9*31 +$GPGST,222710.000,117.0,82.3,157.6,39.0,121.7,107.9,3.9*62 +$GPGSV,3,1,11,16,48,116,24,25,40,267,,23,58,174,29,20,70,337,23*76 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,222,22,27,20,283,*72 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,24*4F +$GPRMC,222710.000,A,2734.78664,S,15306.02080,E,,,030308,,,A*7D +$GPVTG,,T,,M,,N,,K,N*2C +$GPGGA,222711.000,2734.78660,S,15306.02044,E,1,05,1.6,45.3,M,39.2,M,,*72 +$GPGLL,2734.78660,S,15306.02044,E,222711.000,A,A*4F +$GPGSA,A,2,16,23,20,13,03,,,,,,,,1.8,1.6,0.9*31 +$GPGST,222711.000,128.0,107.6,205.2,40.2,156.7,142.6,3.5*58 +$GPGSV,3,1,11,16,48,116,24,25,40,267,,23,58,174,29,20,70,337,23*76 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,222,22,27,20,283,*72 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,24*4F +$GPRMC,222711.000,A,2734.78660,S,15306.02044,E,,,030308,,,A*70 +$GPVTG,,T,,M,,N,,K,N*2C +$GPGGA,222712.000,2734.78658,S,15306.02015,E,1,05,1.6,45.3,M,39.2,M,,*7E +$GPGLL,2734.78658,S,15306.02015,E,222712.000,A,A*43 +$GPGSA,A,2,16,23,20,13,03,,,,,,,,1.8,1.6,0.9*31 +$GPGST,222712.000,128.0,137.2,261.1,41.4,197.4,183.9,3.2*51 +$GPGSV,3,1,11,16,48,116,24,25,40,267,,23,58,173,29,20,70,337,23*71 +$GPGSV,3,2,11,19,03,029,,04,06,242,,13,31,222,22,27,20,283,*72 +$GPGSV,3,3,11,11,06,338,,08,00,298,,03,14,056,24*4F +$GPRMC,222712.000,A,2734.78658,S,15306.02015,E,,,030308,,,A*7C \ No newline at end of file diff --git a/examples/positioning/geoflickr/geoflickr.pro b/examples/positioning/geoflickr/geoflickr.pro new file mode 100644 index 0000000..4bbb795 --- /dev/null +++ b/examples/positioning/geoflickr/geoflickr.pro @@ -0,0 +1,15 @@ +TEMPLATE = app +TARGET = geoflickr + +QT += qml quick network positioning +SOURCES += qmllocationflickr.cpp + +RESOURCES += \ + flickr.qrc + +OTHER_FILES += flickr.qml \ + flickrcommon/* \ + flickrmobile/* + +target.path = $$[QT_INSTALL_EXAMPLES]/positioning/geoflickr +INSTALLS += target diff --git a/examples/positioning/geoflickr/geoflickr.qmlproject b/examples/positioning/geoflickr/geoflickr.qmlproject new file mode 100644 index 0000000..d4909f8 --- /dev/null +++ b/examples/positioning/geoflickr/geoflickr.qmlproject @@ -0,0 +1,16 @@ +import QmlProject 1.0 + +Project { + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + JavaScriptFiles { + directory: "." + } + ImageFiles { + directory: "." + } + /* List of plugin directories passed to QML runtime */ + // importPaths: [ " ../exampleplugin " ] +} diff --git a/examples/positioning/geoflickr/qmllocationflickr.cpp b/examples/positioning/geoflickr/qmllocationflickr.cpp new file mode 100644 index 0000000..61176cb --- /dev/null +++ b/examples/positioning/geoflickr/qmllocationflickr.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication application(argc, argv); + const QString mainQmlApp = QStringLiteral("qrc:///flickr.qml"); + QQuickView view; + view.setSource(QUrl(mainQmlApp)); + view.setResizeMode(QQuickView::SizeRootObjectToView); + // Qt.quit() called in embedded .qml by default only emits + // quit() signal, so do this (optionally use Qt.exit()). + QObject::connect(view.engine(), SIGNAL(quit()), qApp, SLOT(quit())); + view.setGeometry(QRect(100, 100, 360, 640)); + view.show(); + return application.exec(); +} diff --git a/examples/positioning/logfilepositionsource/clientapplication.cpp b/examples/positioning/logfilepositionsource/clientapplication.cpp new file mode 100644 index 0000000..1dca07a --- /dev/null +++ b/examples/positioning/logfilepositionsource/clientapplication.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include + +#include "logfilepositionsource.h" +#include "clientapplication.h" + +ClientApplication::ClientApplication(QWidget *parent) + : QMainWindow(parent) +{ + textEdit = new QTextEdit; + setCentralWidget(textEdit); + + LogFilePositionSource *source = new LogFilePositionSource(this); + connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdated(QGeoPositionInfo))); + + source->startUpdates(); +} + +void ClientApplication::positionUpdated(const QGeoPositionInfo &info) +{ + textEdit->append(QString("Position updated: Date/time = %1, Coordinate = %2").arg(info.timestamp().toString()).arg(info.coordinate().toString())); +} diff --git a/examples/positioning/logfilepositionsource/clientapplication.h b/examples/positioning/logfilepositionsource/clientapplication.h new file mode 100644 index 0000000..4ffd548 --- /dev/null +++ b/examples/positioning/logfilepositionsource/clientapplication.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef CLIENTAPPLICATION_H +#define CLIENTAPPLICATION_H + +#include + +QT_BEGIN_NAMESPACE +class QGeoPositionInfo; +class QTextEdit; +QT_END_NAMESPACE + +class ClientApplication : public QMainWindow +{ + Q_OBJECT +public: + ClientApplication(QWidget *parent = 0); + +private slots: + void positionUpdated(const QGeoPositionInfo &info); + +private: + QTextEdit *textEdit; +}; + + +#endif diff --git a/examples/positioning/logfilepositionsource/doc/src/logfilepositionsource.qdoc b/examples/positioning/logfilepositionsource/doc/src/logfilepositionsource.qdoc new file mode 100644 index 0000000..27ad7e3 --- /dev/null +++ b/examples/positioning/logfilepositionsource/doc/src/logfilepositionsource.qdoc @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\example logfilepositionsource +\title Log File Position Source (C++) +\ingroup qtpositioning-examples + +\brief The Logfile Position Source shows how to create and work with a custom NMEA position source, + for platforms without GPS. + +The data is read from a file which has positional data in NMEA format. The resulting time and +position information is then displayed to the screen as simple text in date/time and +latitude/longitude format. + +This example class reads position data from a text file, \e log.txt. The file specifies position +data using a simple text format: it contains one position update per line, where each line contains +a date/time, a latitude and a longitude, separated by spaces. The date/time is in ISO 8601 format +and the latitude and longitude are in degrees decimal format. Here is an excerpt from \e log.txt: + +\code +2009-08-24T22:25:01 -27.576082 153.092415 +2009-08-24T22:25:02 -27.576223 153.092530 +2009-08-24T22:25:03 -27.576364 153.092648 +\endcode + +The class reads this data and distributes it via the +\l{QGeoPositionInfoSource::positionUpdated()}{positionUpdated()} signal. + +Here is the definition of the \c LogFilePositionSource class: + +\quotefromfile logfilepositionsource/logfilepositionsource.h +\skipto class LogFilePositionSource +\printuntil }; + +The main methods overrided by the subclass are: + +\list + \li \l{QGeoPositionInfoSource::startUpdates()}{startUpdates()}: called by client applications + to start regular position updates. + \li \l{QGeoPositionInfoSource::stopUpdates()}{stopUpdates()}: called by client applications to + stop regular position updates. + \li \l{QGeoPositionInfoSource::requestUpdate()}{requestUpdate()}: called by client applications + to request a single update, with a specified timeout. +\endlist + +When a position update is available, the subclass emits the +\l{QGeoPositionInfoSource::positionUpdated()}{positionUpdated()} signal. + +Here are the key methods in the class implementation: + +\quotefromfile logfilepositionsource/logfilepositionsource.cpp +\skipto LogFilePositionSource::LogFilePositionSource +\printuntil /^\}/ +\skipto LogFilePositionSource::startUpdates +\printuntil /^\}/ +\skipto LogFilePositionSource::stopUpdates +\printuntil /^\}/ +\skipto LogFilePositionSource::requestUpdate +\printuntil /^\}/ +\printuntil LogFilePositionSource::readNextPosition +\printuntil /^\}/ +*/ diff --git a/examples/positioning/logfilepositionsource/logfile.qrc b/examples/positioning/logfilepositionsource/logfile.qrc new file mode 100644 index 0000000..6121394 --- /dev/null +++ b/examples/positioning/logfilepositionsource/logfile.qrc @@ -0,0 +1,5 @@ + + + simplelog.txt + + diff --git a/examples/positioning/logfilepositionsource/logfilepositionsource.cpp b/examples/positioning/logfilepositionsource/logfilepositionsource.cpp new file mode 100644 index 0000000..9c414c6 --- /dev/null +++ b/examples/positioning/logfilepositionsource/logfilepositionsource.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +#include "logfilepositionsource.h" + +LogFilePositionSource::LogFilePositionSource(QObject *parent) + : QGeoPositionInfoSource(parent), + logFile(new QFile(this)), + timer(new QTimer(this)) +{ + connect(timer, SIGNAL(timeout()), this, SLOT(readNextPosition())); + + logFile->setFileName(":/simplelog.txt"); + if (!logFile->open(QIODevice::ReadOnly)) + qWarning() << "Error: cannot open source file" << logFile->fileName(); +} + +QGeoPositionInfo LogFilePositionSource::lastKnownPosition(bool /*fromSatellitePositioningMethodsOnly*/) const +{ + return lastPosition; +} + +LogFilePositionSource::PositioningMethods LogFilePositionSource::supportedPositioningMethods() const +{ + return AllPositioningMethods; +} + +int LogFilePositionSource::minimumUpdateInterval() const +{ + return 500; +} + +void LogFilePositionSource::startUpdates() +{ + int interval = updateInterval(); + if (interval < minimumUpdateInterval()) + interval = minimumUpdateInterval(); + + timer->start(interval); +} + +void LogFilePositionSource::stopUpdates() +{ + timer->stop(); +} + +void LogFilePositionSource::requestUpdate(int /*timeout*/) +{ + // For simplicity, ignore timeout - assume that if data is not available + // now, no data will be added to the file later + if (logFile->canReadLine()) + readNextPosition(); + else + emit updateTimeout(); +} + +void LogFilePositionSource::readNextPosition() +{ + QByteArray line = logFile->readLine().trimmed(); + if (!line.isEmpty()) { + QList data = line.split(' '); + double latitude; + double longitude; + bool hasLatitude = false; + bool hasLongitude = false; + QDateTime timestamp = QDateTime::fromString(QString(data.value(0)), Qt::ISODate); + latitude = data.value(1).toDouble(&hasLatitude); + longitude = data.value(2).toDouble(&hasLongitude); + + if (hasLatitude && hasLongitude && timestamp.isValid()) { + QGeoCoordinate coordinate(latitude, longitude); + QGeoPositionInfo info(coordinate, timestamp); + if (info.isValid()) { + lastPosition = info; + emit positionUpdated(info); + } + } + } +} + +QGeoPositionInfoSource::Error LogFilePositionSource::error() const +{ + return QGeoPositionInfoSource::NoError; +} diff --git a/examples/positioning/logfilepositionsource/logfilepositionsource.h b/examples/positioning/logfilepositionsource/logfilepositionsource.h new file mode 100644 index 0000000..843516c --- /dev/null +++ b/examples/positioning/logfilepositionsource/logfilepositionsource.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef LOGFILEPOSITIONSOURCE_H +#define LOGFILEPOSITIONSOURCE_H + +#include + +QT_BEGIN_NAMESPACE +class QFile; +class QTimer; +QT_END_NAMESPACE + +class LogFilePositionSource : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + LogFilePositionSource(QObject *parent = 0); + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + + PositioningMethods supportedPositioningMethods() const; + int minimumUpdateInterval() const; + Error error() const; + +public slots: + virtual void startUpdates(); + virtual void stopUpdates(); + + virtual void requestUpdate(int timeout = 5000); + +private slots: + void readNextPosition(); + +private: + QFile *logFile; + QTimer *timer; + QGeoPositionInfo lastPosition; +}; + +#endif diff --git a/examples/positioning/logfilepositionsource/logfilepositionsource.pro b/examples/positioning/logfilepositionsource/logfilepositionsource.pro new file mode 100644 index 0000000..cb9e30a --- /dev/null +++ b/examples/positioning/logfilepositionsource/logfilepositionsource.pro @@ -0,0 +1,16 @@ +TEMPLATE = app +TARGET = logfilepositionsource +QT = positioning core widgets + + +HEADERS = logfilepositionsource.h \ + clientapplication.h +SOURCES = logfilepositionsource.cpp \ + clientapplication.cpp \ + main.cpp + +RESOURCES += \ + logfile.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/positioning/logfilepositionsource +INSTALLS += target diff --git a/examples/positioning/logfilepositionsource/main.cpp b/examples/positioning/logfilepositionsource/main.cpp new file mode 100644 index 0000000..dcc05a1 --- /dev/null +++ b/examples/positioning/logfilepositionsource/main.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +#include "clientapplication.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + ClientApplication client; + client.show(); + + return app.exec(); +} diff --git a/examples/positioning/logfilepositionsource/simplelog.txt b/examples/positioning/logfilepositionsource/simplelog.txt new file mode 100644 index 0000000..d016b2a --- /dev/null +++ b/examples/positioning/logfilepositionsource/simplelog.txt @@ -0,0 +1,156 @@ +2009-08-24T22:24:37 -27.572321 153.090718 +2009-08-24T22:24:38 -27.572470 153.090783 +2009-08-24T22:24:39 -27.572616 153.090845 +2009-08-24T22:24:40 -27.572763 153.090908 +2009-08-24T22:24:41 -27.572914 153.090971 +2009-08-24T22:24:42 -27.573068 153.091036 +2009-08-24T22:24:43 -27.573224 153.091102 +2009-08-24T22:24:44 -27.573383 153.091167 +2009-08-24T22:24:45 -27.573541 153.091232 +2009-08-24T22:24:46 -27.573700 153.091298 +2009-08-24T22:24:47 -27.573860 153.091366 +2009-08-24T22:24:48 -27.574019 153.091435 +2009-08-24T22:24:49 -27.574178 153.091507 +2009-08-24T22:24:50 -27.574340 153.091581 +2009-08-24T22:24:51 -27.574506 153.091654 +2009-08-24T22:24:52 -27.574670 153.091729 +2009-08-24T22:24:53 -27.574836 153.091800 +2009-08-24T22:24:54 -27.575001 153.091870 +2009-08-24T22:24:55 -27.575165 153.091940 +2009-08-24T22:24:56 -27.575327 153.092010 +2009-08-24T22:24:57 -27.575485 153.092078 +2009-08-24T22:24:58 -27.575641 153.092144 +2009-08-24T22:24:59 -27.575795 153.092218 +2009-08-24T22:25:00 -27.575942 153.092308 +2009-08-24T22:25:01 -27.576082 153.092415 +2009-08-24T22:25:02 -27.576223 153.092530 +2009-08-24T22:25:03 -27.576364 153.092648 +2009-08-24T22:25:04 -27.576505 153.092763 +2009-08-24T22:25:05 -27.576646 153.092879 +2009-08-24T22:25:06 -27.576784 153.092990 +2009-08-24T22:25:07 -27.576918 153.093099 +2009-08-24T22:25:08 -27.577046 153.093204 +2009-08-24T22:25:09 -27.577168 153.093303 +2009-08-24T22:25:10 -27.577284 153.093396 +2009-08-24T22:25:11 -27.577396 153.093484 +2009-08-24T22:25:12 -27.577499 153.093568 +2009-08-24T22:25:13 -27.577595 153.093647 +2009-08-24T22:25:14 -27.577692 153.093727 +2009-08-24T22:25:15 -27.577793 153.093810 +2009-08-24T22:25:16 -27.577897 153.093896 +2009-08-24T22:25:17 -27.578006 153.093984 +2009-08-24T22:25:18 -27.578120 153.094074 +2009-08-24T22:25:19 -27.578237 153.094168 +2009-08-24T22:25:20 -27.578360 153.094267 +2009-08-24T22:25:21 -27.578487 153.094370 +2009-08-24T22:25:22 -27.578617 153.094474 +2009-08-24T22:25:23 -27.578748 153.094581 +2009-08-24T22:25:24 -27.578880 153.094688 +2009-08-24T22:25:25 -27.579014 153.094796 +2009-08-24T22:25:26 -27.579149 153.094905 +2009-08-24T22:25:27 -27.579285 153.095012 +2009-08-24T22:25:28 -27.579420 153.095121 +2009-08-24T22:25:29 -27.579552 153.095231 +2009-08-24T22:25:30 -27.579684 153.095340 +2009-08-24T22:25:31 -27.579820 153.095449 +2009-08-24T22:25:32 -27.579956 153.095558 +2009-08-24T22:25:33 -27.580095 153.095667 +2009-08-24T22:25:34 -27.580240 153.095776 +2009-08-24T22:25:35 -27.580392 153.095885 +2009-08-24T22:25:36 -27.580536 153.095995 +2009-08-24T22:25:37 -27.580679 153.096109 +2009-08-24T22:25:38 -27.580824 153.096226 +2009-08-24T22:25:39 -27.580965 153.096337 +2009-08-24T22:25:40 -27.581101 153.096441 +2009-08-24T22:25:41 -27.581237 153.096537 +2009-08-24T22:25:42 -27.581356 153.096628 +2009-08-24T22:25:43 -27.581466 153.096714 +2009-08-24T22:25:44 -27.581555 153.096795 +2009-08-24T22:25:45 -27.581606 153.096847 +2009-08-24T22:25:46 -27.581539 153.096855 +2009-08-24T22:25:47 -27.581572 153.096873 +2009-08-24T22:25:48 -27.581579 153.096875 +2009-08-24T22:25:49 -27.581582 153.096878 +2009-08-24T22:25:50 -27.581588 153.096880 +2009-08-24T22:25:51 -27.581588 153.096880 +2009-08-24T22:25:52 -27.581592 153.096881 +2009-08-24T22:25:53 -27.581596 153.096882 +2009-08-24T22:25:54 -27.581599 153.096883 +2009-08-24T22:25:55 -27.581601 153.096883 +2009-08-24T22:25:56 -27.581602 153.096883 +2009-08-24T22:25:57 -27.581606 153.096890 +2009-08-24T22:25:58 -27.581606 153.096919 +2009-08-24T22:25:59 -27.581605 153.096985 +2009-08-24T22:26:00 -27.581573 153.097060 +2009-08-24T22:26:01 -27.581532 153.097142 +2009-08-24T22:26:02 -27.581484 153.097227 +2009-08-24T22:26:03 -27.581426 153.097324 +2009-08-24T22:26:04 -27.581373 153.097424 +2009-08-24T22:26:05 -27.581320 153.097530 +2009-08-24T22:26:06 -27.581265 153.097641 +2009-08-24T22:26:07 -27.581203 153.097757 +2009-08-24T22:26:08 -27.581144 153.097875 +2009-08-24T22:26:09 -27.581093 153.097995 +2009-08-24T22:26:10 -27.581042 153.098116 +2009-08-24T22:26:11 -27.580987 153.098231 +2009-08-24T22:26:12 -27.580919 153.098334 +2009-08-24T22:26:13 -27.580841 153.098428 +2009-08-24T22:26:14 -27.580754 153.098511 +2009-08-24T22:26:15 -27.580667 153.098591 +2009-08-24T22:26:16 -27.580581 153.098671 +2009-08-24T22:26:17 -27.580492 153.098751 +2009-08-24T22:26:18 -27.580401 153.098832 +2009-08-24T22:26:19 -27.580311 153.098914 +2009-08-24T22:26:20 -27.580224 153.098996 +2009-08-24T22:26:21 -27.580140 153.099078 +2009-08-24T22:26:22 -27.580059 153.099159 +2009-08-24T22:26:23 -27.579980 153.099245 +2009-08-24T22:26:24 -27.579900 153.099336 +2009-08-24T22:26:25 -27.579826 153.099433 +2009-08-24T22:26:26 -27.579753 153.099529 +2009-08-24T22:26:27 -27.579680 153.099623 +2009-08-24T22:26:28 -27.579606 153.099716 +2009-08-24T22:26:29 -27.579538 153.099808 +2009-08-24T22:26:30 -27.579477 153.099893 +2009-08-24T22:26:31 -27.579416 153.099968 +2009-08-24T22:26:32 -27.579393 153.100027 +2009-08-24T22:26:33 -27.579376 153.100092 +2009-08-24T22:26:34 -27.579384 153.100142 +2009-08-24T22:26:35 -27.579403 153.100181 +2009-08-24T22:26:36 -27.579429 153.100210 +2009-08-24T22:26:37 -27.579460 153.100237 +2009-08-24T22:26:38 -27.579493 153.100265 +2009-08-24T22:26:39 -27.579518 153.100291 +2009-08-24T22:26:40 -27.579536 153.100336 +2009-08-24T22:26:41 -27.579591 153.100358 +2009-08-24T22:26:42 -27.579618 153.100383 +2009-08-24T22:26:43 -27.579639 153.100404 +2009-08-24T22:26:44 -27.579650 153.100420 +2009-08-24T22:26:45 -27.579665 153.100432 +2009-08-24T22:26:46 -27.579679 153.100442 +2009-08-24T22:26:47 -27.579690 153.100451 +2009-08-24T22:26:48 -27.579697 153.100459 +2009-08-24T22:26:49 -27.579700 153.100462 +2009-08-24T22:26:50 -27.579699 153.100464 +2009-08-24T22:26:51 -27.579699 153.100466 +2009-08-24T22:26:52 -27.579700 153.100466 +2009-08-24T22:26:53 -27.579704 153.100466 +2009-08-24T22:26:54 -27.579705 153.100465 +2009-08-24T22:26:55 -27.579708 153.100468 +2009-08-24T22:26:56 -27.579712 153.100471 +2009-08-24T22:26:57 -27.579719 153.100476 +2009-08-24T22:26:58 -27.579728 153.100485 +2009-08-24T22:26:59 -27.579744 153.100493 +2009-08-24T22:27:00 -27.579759 153.100496 +2009-08-24T22:27:01 -27.579780 153.100481 +2009-08-24T22:27:02 -27.579788 153.100462 +2009-08-24T22:27:03 -27.579791 153.100436 +2009-08-24T22:27:04 -27.579788 153.100426 +2009-08-24T22:27:05 -27.579783 153.100409 +2009-08-24T22:27:06 -27.579781 153.100390 +2009-08-24T22:27:07 -27.579780 153.100375 +2009-08-24T22:27:08 -27.579779 153.100364 +2009-08-24T22:27:09 -27.579778 153.100354 +2009-08-24T22:27:10 -27.579777 153.100347 +2009-08-24T22:27:11 -27.579777 153.100341 +2009-08-24T22:27:12 -27.579776 153.100336 diff --git a/examples/positioning/positioning.pro b/examples/positioning/positioning.pro new file mode 100644 index 0000000..bbdc1a4 --- /dev/null +++ b/examples/positioning/positioning.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +qtHaveModule(widgets): SUBDIRS += logfilepositionsource +qtHaveModule(quick) { + SUBDIRS += geoflickr satelliteinfo + qtHaveModule(network): SUBDIRS += weatherinfo +} diff --git a/examples/positioning/satelliteinfo/doc/images/example-satelliteinfo.png b/examples/positioning/satelliteinfo/doc/images/example-satelliteinfo.png new file mode 100644 index 0000000000000000000000000000000000000000..aa9a217c232773407fe0bcec4665723d3ed4565d GIT binary patch literal 27371 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 k63DQRexEtLIU8q)dJ~KYEcqim+ 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 KWlN>sef&T0c$BCB literal 0 HcmV?d00001 diff --git a/examples/positioning/satelliteinfo/doc/src/satelliteinfo.qdoc b/examples/positioning/satelliteinfo/doc/src/satelliteinfo.qdoc new file mode 100644 index 0000000..54c3020 --- /dev/null +++ b/examples/positioning/satelliteinfo/doc/src/satelliteinfo.qdoc @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example satelliteinfo + \title SatelliteInfo (C++/QML) + + \brief The SatelliteInfo example shows how the available satellites + at the user's current position and marks the satellites + currently contributing to the GPS fix as pink. + + \ingroup qtpositioning-examples + + Key \l{Qt Positioning} classes used in this example: + + \list + \li \l{QGeoSatelliteInfo} + \li \l{QGeoSatelliteInfoSource} + \endlist + + \image ../images/example-satelliteinfo.png + + The example displays the signal strength of all satellites in view. Any satellite + that is currently used to calculate the GPS fix has been marked pink. The number at + the bottom of each signal bar is the individual satellite identifier. + + The application operates in three different modes: + + \table + \header + \li Application mode + \li Description + \row + \li running + \li The application continuously queries the system for satellite updates. When new data + is available it will be displayed. + \row + \li stopped + \li The application stops updating the satellite information. + \row + \li single + \li The application makes a single update request with a timeout of 10s. The display + remains empty until the request was answered by the system. + \endtable + + If the platform does not provide satellite information the application automatically + switches into a demo mode whereby it continuously switches between predefined + sets of satellite data. + + \include examples-run.qdocinc +*/ diff --git a/examples/positioning/satelliteinfo/main.cpp b/examples/positioning/satelliteinfo/main.cpp new file mode 100644 index 0000000..feeb12a --- /dev/null +++ b/examples/positioning/satelliteinfo/main.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include "satellitemodel.h" + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + qmlRegisterType("Local", 1, 0, "SatelliteModel"); + + QQuickView view; + view.setSource(QStringLiteral("qrc:///satelliteinfo.qml")); + view.setResizeMode(QQuickView::SizeRootObjectToView); + + QObject::connect(view.engine(), SIGNAL(quit()), qApp, SLOT(quit())); + view.show(); + + return app.exec(); +} + + diff --git a/examples/positioning/satelliteinfo/satelliteinfo.pro b/examples/positioning/satelliteinfo/satelliteinfo.pro new file mode 100644 index 0000000..4aef9d0 --- /dev/null +++ b/examples/positioning/satelliteinfo/satelliteinfo.pro @@ -0,0 +1,22 @@ +TEMPLATE = app +TARGET = satelliteinfo + +QT += quick positioning + +SOURCES += main.cpp \ + satellitemodel.cpp + +HEADERS += \ + satellitemodel.h + +OTHER_FILES += \ + satelliteinfo.qml + +RESOURCES += \ + satelliteinfo.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/positioning/satelliteinfo +INSTALLS += target + + + diff --git a/examples/positioning/satelliteinfo/satelliteinfo.qml b/examples/positioning/satelliteinfo/satelliteinfo.qml new file mode 100644 index 0000000..a0269e2 --- /dev/null +++ b/examples/positioning/satelliteinfo/satelliteinfo.qml @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import Local 1.0 + +Rectangle { + id: page + width: 360 + height: 360 + + SatelliteModel { + id: satelliteModel + running: true + onErrorFound: errorLabel.text = qsTr("Last Error: %1", "%1=error number").arg(code) + } + + Item { + id: header + height: column.height + 30 + width: parent.width + state: "running" + + anchors.top: parent.top + + function toggle() + { + switch (header.state) { + case "single": header.state = "running"; break; + default: + case "running": header.state = "stopped"; break; + case "stopped": header.state = "single"; break; + } + } + + function enterSingle() + { + satelliteModel.singleRequestMode = true; + satelliteModel.running = true; + } + + function enterRunning() + { + satelliteModel.running = false; + satelliteModel.singleRequestMode = false; + satelliteModel.running = true; + } + + states: [ + State { + name: "stopped" + PropertyChanges { target: startStop; bText: qsTr("Single") } + PropertyChanges { + target: modeLabel; text: qsTr("Current Mode: stopped") + } + PropertyChanges { target: satelliteModel; running: false; } + }, + State { + name: "single" + PropertyChanges { target: startStop; bText: qsTr("Start") } + PropertyChanges { + target: modeLabel; text: qsTr("Current Mode: single") + } + StateChangeScript { script: header.enterSingle(); } + }, + State { + name: "running" + PropertyChanges { target: startStop; bText: qsTr("Stop") } + PropertyChanges { + target: modeLabel; text: qsTr("Current Mode: running") + } + StateChangeScript { script: header.enterRunning(); } + } + ] + + Column { + id: column + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.margins: 7 + Text { + id: overview + text: satelliteModel.satelliteInfoAvailable + ? qsTr("Known Satellites") + : qsTr("Known Satellites (Demo Mode)") + font.pointSize: 12 + } + + Text { + id: modeLabel + font.pointSize: 12 + } + + Text { + id: errorLabel + text: qsTr("Last Error: None") + font.pointSize: 12 + } + } + Rectangle { + id: startStop + border.color: "black" + border.width: 3 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.margins: 7 + radius: 10 + height: maxField.height*1.4 + width: maxField.width*1.4 + + property string bText: qsTr("Stop"); + + Text { //need this for sizing + id: maxField + text: qsTr("Single") + font.pointSize: 13 + opacity: 0 + } + + Text { + id: buttonText + anchors.centerIn: parent + text: startStop.bText + font.pointSize: 13 + } + + MouseArea { + anchors.fill: parent + onPressed: { startStop.color = "lightGray" } + onClicked: { header.toggle() } + onReleased: { startStop.color = "white" } + } + } + } + + Rectangle { + anchors.top: header.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: rect.myMargin + border.width: 3 + radius: 10 + border.color: "black" + + Item { + id: rect + anchors.fill: parent + anchors.margins: myMargin + property int myMargin: 7 + + Row { + id: view + property int rows: repeater.model.entryCount + property int singleWidth: ((rect.width - scale.width)/rows )-rect.myMargin + spacing: rect.myMargin + + Rectangle { + id: scale + width: strengthLabel.width+10 + height: rect.height + color: "#32cd32" + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: lawngreenRect.top + font.pointSize: 11 + text: "50" + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + font.pointSize: 11 + text: "100" + } + + Rectangle { + id: redRect + width: parent.width + color: "red" + height: parent.height*10/100 + anchors.bottom: parent.bottom + Text { + id: strengthLabel + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + font.pointSize: 11 + text: "00" + } + } + Rectangle { + id: orangeRect + height: parent.height*10/100 + anchors.bottom: redRect.top + width: parent.width + color: "#ffa500" + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + font.pointSize: 11 + text: "10" + } + } + Rectangle { + id: goldRect + height: parent.height*10/100 + anchors.bottom: orangeRect.top + width: parent.width + color: "#ffd700" + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + font.pointSize: 11 + text: "20" + } + } + Rectangle { + id: yellowRect + height: parent.height*10/100 + anchors.bottom: goldRect.top + width: parent.width + color: "yellow" + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + font.pointSize: 11 + text: "30" + } + } + Rectangle { + id: lawngreenRect + height: parent.height*10/100 + anchors.bottom: yellowRect.top + width: parent.width + color: "#7cFc00" + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + font.pointSize: 11 + text: "40" + } + } + } + + Repeater { + id: repeater + model: satelliteModel + delegate: Rectangle { + height: rect.height + width: view.singleWidth + Rectangle { + anchors.bottom: parent.bottom + width: parent.width + height: parent.height*signalStrength/100 + color: isInUse ? "#7FFF0000" : "#7F0000FF" + } + Text { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + text: satelliteIdentifier + } + } + } + } + } + } +} + diff --git a/examples/positioning/satelliteinfo/satelliteinfo.qrc b/examples/positioning/satelliteinfo/satelliteinfo.qrc new file mode 100644 index 0000000..8745f87 --- /dev/null +++ b/examples/positioning/satelliteinfo/satelliteinfo.qrc @@ -0,0 +1,5 @@ + + + satelliteinfo.qml + + diff --git a/examples/positioning/satelliteinfo/satellitemodel.cpp b/examples/positioning/satelliteinfo/satellitemodel.cpp new file mode 100644 index 0000000..112fc9c --- /dev/null +++ b/examples/positioning/satelliteinfo/satellitemodel.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "satellitemodel.h" +#include +#include + +SatelliteModel::SatelliteModel(QObject *parent) : + QAbstractListModel(parent), source(0), m_componentCompleted(false), m_running(false), + m_runningRequested(false), demo(false), isSingle(false), singleRequestServed(false) +{ + source = QGeoSatelliteInfoSource::createDefaultSource(this); + if (!demo && !source) { + qWarning() << "No satellite data source found. Changing to demo mode."; + demo = true; + } + if (!demo) { + source->setUpdateInterval(3000); + connect(source, SIGNAL(satellitesInViewUpdated(QList)), + this, SLOT(satellitesInViewUpdated(QList))); + connect(source, SIGNAL(satellitesInUseUpdated(QList)), + this, SLOT(satellitesInUseUpdated(QList))); + connect(source, SIGNAL(error(QGeoSatelliteInfoSource::Error)), + this, SLOT(error(QGeoSatelliteInfoSource::Error))); + } + + if (demo) { + timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(updateDemoData())); + timer->start(3000); + } +} + +int SatelliteModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + if (!source && !demo) + return 0; + + return knownSatellites.count(); +} + +QVariant SatelliteModel::data(const QModelIndex &index, int role) const +{ + if (!demo && !source) + return QVariant(); + + if (!index.isValid() || index.row() < 0) + return QVariant(); + + if (index.row() >= knownSatellites.count()) { + qWarning() << "SatelliteModel: Index out of bound"; + return QVariant(); + } + + const QGeoSatelliteInfo &info = knownSatellites.at(index.row()); + switch (role) { + case IdentifierRole: + return info.satelliteIdentifier(); + case InUseRole: + return satellitesInUse.contains(info.satelliteIdentifier()); + case SignalStrengthRole: + return info.signalStrength(); + case ElevationRole: + if (!info.hasAttribute(QGeoSatelliteInfo::Elevation)) + return QVariant(); + return info.attribute(QGeoSatelliteInfo::Elevation); + case AzimuthRole: + if (!info.hasAttribute(QGeoSatelliteInfo::Azimuth)) + return QVariant(); + return info.attribute(QGeoSatelliteInfo::Azimuth); + default: + break; + + } + + return QVariant(); +} + +QHash SatelliteModel::roleNames() const +{ + QHash roleNames; + roleNames.insert(IdentifierRole, "satelliteIdentifier"); + roleNames.insert(InUseRole, "isInUse"); + roleNames.insert(SignalStrengthRole, "signalStrength"); + roleNames.insert(ElevationRole, "elevation"); + roleNames.insert(AzimuthRole, "azimuth"); + return roleNames; +} + +void SatelliteModel::componentComplete() +{ + m_componentCompleted = true; + if (m_runningRequested) + setRunning(true); +} + +bool SatelliteModel::running() const +{ + return m_running; +} + +bool SatelliteModel::isSingleRequest() const +{ + return isSingle; +} + +void SatelliteModel::setSingleRequest(bool single) +{ + if (running()) { + qWarning() << "Cannot change single request mode while running"; + return; + } + + if (single != isSingle) { //flag changed + isSingle = single; + emit singleRequestChanged(); + } +} + +void SatelliteModel::setRunning(bool isActive) +{ + if (!source && !demo) + return; + + if (!m_componentCompleted) { + m_runningRequested = isActive; + return; + } + + if (m_running == isActive) + return; + + m_running = isActive; + + if (m_running) { + clearModel(); + if (demo) + timer->start(2000); + else if (isSingleRequest()) + source->requestUpdate(10000); + else + source->startUpdates(); + + if (demo) + singleRequestServed = false; + } else { + if (demo) + timer->stop(); + else if (!isSingleRequest()) + source->stopUpdates(); + } + + + Q_EMIT runningChanged(); +} + +int SatelliteModel::entryCount() const +{ + return knownSatellites.count(); +} + +bool SatelliteModel::canProvideSatelliteInfo() const +{ + return !demo; +} + +void SatelliteModel::clearModel() +{ + beginResetModel(); + knownSatelliteIds.clear(); + knownSatellites.clear(); + satellitesInUse.clear(); + endResetModel(); +} + +void SatelliteModel::updateDemoData() +{ + static bool flag = true; + QList satellites; + if (flag) { + for (int i = 0; i<5; i++) { + QGeoSatelliteInfo info; + info.setSatelliteIdentifier(i); + info.setSignalStrength(20 + 20*i); + satellites.append(info); + } + } else { + for (int i = 0; i<9; i++) { + QGeoSatelliteInfo info; + info.setSatelliteIdentifier(i*2); + info.setSignalStrength(20 + 10*i); + satellites.append(info); + } + } + + + satellitesInViewUpdated(satellites); + flag ? satellitesInUseUpdated(QList() << satellites.at(2)) + : satellitesInUseUpdated(QList() << satellites.at(3)); + flag = !flag; + + emit errorFound(flag); + + if (isSingleRequest() && !singleRequestServed) { + singleRequestServed = true; + setRunning(false); + } +} + +void SatelliteModel::error(QGeoSatelliteInfoSource::Error error) +{ + emit errorFound((int)error); +} + +QT_BEGIN_NAMESPACE +inline bool operator<(const QGeoSatelliteInfo& a, const QGeoSatelliteInfo& b) +{ + return a.satelliteIdentifier() < b.satelliteIdentifier(); +} +QT_END_NAMESPACE + +void SatelliteModel::satellitesInViewUpdated(const QList &infos) +{ + if (!running()) + return; + + int oldEntryCount = knownSatellites.count(); + + + QSet satelliteIdsInUpdate; + foreach (const QGeoSatelliteInfo &info, infos) + satelliteIdsInUpdate.insert(info.satelliteIdentifier()); + + QSet toBeRemoved = knownSatelliteIds - satelliteIdsInUpdate; + + //We reset the model as in reality just about all entry values change + //and there are generally a lot of inserts and removals each time + //Hence we don't bother with complex model update logic beyond resetModel() + beginResetModel(); + + knownSatellites = infos; + + //sort them for presentation purposes + std::sort(knownSatellites.begin(), knownSatellites.end()); + + //remove old "InUse" data + //new satellites are by default not in "InUse" + //existing satellites keep their "inUse" state + satellitesInUse -= toBeRemoved; + + knownSatelliteIds = satelliteIdsInUpdate; + endResetModel(); + + if (oldEntryCount != knownSatellites.count()) + emit entryCountChanged(); +} + +void SatelliteModel::satellitesInUseUpdated(const QList &infos) +{ + if (!running()) + return; + + beginResetModel(); + + satellitesInUse.clear(); + foreach (const QGeoSatelliteInfo &info, infos) + satellitesInUse.insert(info.satelliteIdentifier()); + + endResetModel(); +} + diff --git a/examples/positioning/satelliteinfo/satellitemodel.h b/examples/positioning/satelliteinfo/satellitemodel.h new file mode 100644 index 0000000..37f8301 --- /dev/null +++ b/examples/positioning/satelliteinfo/satellitemodel.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SATELLITEMODEL_H +#define SATELLITEMODEL_H + +#include +#include +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QTimer) +QT_FORWARD_DECLARE_CLASS(QGeoSatelliteInfoSource) + +class SatelliteModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) + Q_PROPERTY(bool satelliteInfoAvailable READ canProvideSatelliteInfo NOTIFY canProvideSatelliteInfoChanged) + Q_PROPERTY(int entryCount READ entryCount NOTIFY entryCountChanged) + Q_PROPERTY(bool singleRequestMode READ isSingleRequest WRITE setSingleRequest NOTIFY singleRequestChanged) + Q_INTERFACES(QQmlParserStatus) +public: + explicit SatelliteModel(QObject *parent = 0); + + enum { + IdentifierRole = Qt::UserRole + 1, + InUseRole, + SignalStrengthRole, + ElevationRole, + AzimuthRole + }; + + //From QAbstractListModel + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + //From QQmlParserStatus + void classBegin() {} + void componentComplete(); + + bool running() const; + void setRunning(bool isActive); + + bool isSingleRequest() const; + void setSingleRequest(bool single); + + int entryCount() const; + + bool canProvideSatelliteInfo() const; + +signals: + void runningChanged(); + void entryCountChanged(); + void errorFound(int code); + void canProvideSatelliteInfoChanged(); + void singleRequestChanged(); + +public slots: + void clearModel(); + void updateDemoData(); + +private slots: + void error(QGeoSatelliteInfoSource::Error); + void satellitesInViewUpdated(const QList &infos); + void satellitesInUseUpdated(const QList &infos); + +private: + QGeoSatelliteInfoSource *source; + bool m_componentCompleted; + bool m_running; + bool m_runningRequested; + QList knownSatellites; + QSet knownSatelliteIds; + QSet satellitesInUse; + bool demo; + QTimer *timer; + bool isSingle; + bool singleRequestServed; + +}; + +QML_DECLARE_TYPE(SatelliteModel) + +#endif // SATELLITEMODEL_H diff --git a/examples/positioning/weatherinfo/appmodel.cpp b/examples/positioning/weatherinfo/appmodel.cpp new file mode 100644 index 0000000..d07a598 --- /dev/null +++ b/examples/positioning/weatherinfo/appmodel.cpp @@ -0,0 +1,572 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "appmodel.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + *This application uses http://openweathermap.org/api + **/ + +#define ZERO_KELVIN 273.15 + +Q_LOGGING_CATEGORY(requestsLog,"wapp.requests") + +WeatherData::WeatherData(QObject *parent) : + QObject(parent) +{ +} + +WeatherData::WeatherData(const WeatherData &other) : + QObject(0), + m_dayOfWeek(other.m_dayOfWeek), + m_weather(other.m_weather), + m_weatherDescription(other.m_weatherDescription), + m_temperature(other.m_temperature) +{ +} + +QString WeatherData::dayOfWeek() const +{ + return m_dayOfWeek; +} + +/*! + * The icon value is based on OpenWeatherMap.org icon set. For details + * see http://bugs.openweathermap.org/projects/api/wiki/Weather_Condition_Codes + * + * e.g. 01d ->sunny day + * + * The icon string will be translated to + * http://openweathermap.org/img/w/01d.png + */ +QString WeatherData::weatherIcon() const +{ + return m_weather; +} + +QString WeatherData::weatherDescription() const +{ + return m_weatherDescription; +} + +QString WeatherData::temperature() const +{ + return m_temperature; +} + +void WeatherData::setDayOfWeek(const QString &value) +{ + m_dayOfWeek = value; + emit dataChanged(); +} + +void WeatherData::setWeatherIcon(const QString &value) +{ + m_weather = value; + emit dataChanged(); +} + +void WeatherData::setWeatherDescription(const QString &value) +{ + m_weatherDescription = value; + emit dataChanged(); +} + +void WeatherData::setTemperature(const QString &value) +{ + m_temperature = value; + emit dataChanged(); +} + +class AppModelPrivate +{ +public: + static const int baseMsBeforeNewRequest = 5 * 1000; // 5 s, increased after each missing answer up to 10x + QGeoPositionInfoSource *src; + QGeoCoordinate coord; + QString longitude, latitude; + QString city; + QNetworkAccessManager *nam; + QNetworkSession *ns; + WeatherData now; + QList forecast; + QQmlListProperty *fcProp; + bool ready; + bool useGps; + QElapsedTimer throttle; + int nErrors; + int minMsBeforeNewRequest; + QTimer delayedCityRequestTimer; + QTimer requestNewWeatherTimer; + QString app_ident; + + AppModelPrivate() : + src(NULL), + nam(NULL), + ns(NULL), + fcProp(NULL), + ready(false), + useGps(true), + nErrors(0), + minMsBeforeNewRequest(baseMsBeforeNewRequest) + { + delayedCityRequestTimer.setSingleShot(true); + delayedCityRequestTimer.setInterval(1000); // 1 s + requestNewWeatherTimer.setSingleShot(false); + requestNewWeatherTimer.setInterval(20*60*1000); // 20 min + throttle.invalidate(); + app_ident = QStringLiteral("36496bad1955bf3365448965a42b9eac"); + } +}; + +static void forecastAppend(QQmlListProperty *prop, WeatherData *val) +{ + Q_UNUSED(val); + Q_UNUSED(prop); +} + +static WeatherData *forecastAt(QQmlListProperty *prop, int index) +{ + AppModelPrivate *d = static_cast(prop->data); + return d->forecast.at(index); +} + +static int forecastCount(QQmlListProperty *prop) +{ + AppModelPrivate *d = static_cast(prop->data); + return d->forecast.size(); +} + +static void forecastClear(QQmlListProperty *prop) +{ + static_cast(prop->data)->forecast.clear(); +} + +//! [0] +AppModel::AppModel(QObject *parent) : + QObject(parent), + d(new AppModelPrivate) +{ +//! [0] + d->fcProp = new QQmlListProperty(this, d, + forecastAppend, + forecastCount, + forecastAt, + forecastClear); + + connect(&d->delayedCityRequestTimer, SIGNAL(timeout()), + this, SLOT(queryCity())); + connect(&d->requestNewWeatherTimer, SIGNAL(timeout()), + this, SLOT(refreshWeather())); + d->requestNewWeatherTimer.start(); + + +//! [1] + // make sure we have an active network session + d->nam = new QNetworkAccessManager(this); + + QNetworkConfigurationManager ncm; + d->ns = new QNetworkSession(ncm.defaultConfiguration(), this); + connect(d->ns, SIGNAL(opened()), this, SLOT(networkSessionOpened())); + // the session may be already open. if it is, run the slot directly + if (d->ns->isOpen()) + this->networkSessionOpened(); + // tell the system we want network + d->ns->open(); +} +//! [1] + +AppModel::~AppModel() +{ + d->ns->close(); + if (d->src) + d->src->stopUpdates(); + delete d; +} + +//! [2] +void AppModel::networkSessionOpened() +{ + d->src = QGeoPositionInfoSource::createDefaultSource(this); + + if (d->src) { + d->useGps = true; + connect(d->src, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdated(QGeoPositionInfo))); + connect(d->src, SIGNAL(error(QGeoPositionInfoSource::Error)), + this, SLOT(positionError(QGeoPositionInfoSource::Error))); + d->src->startUpdates(); + } else { + d->useGps = false; + d->city = "Brisbane"; + emit cityChanged(); + this->refreshWeather(); + } +} +//! [2] + +//! [3] +void AppModel::positionUpdated(QGeoPositionInfo gpsPos) +{ + d->coord = gpsPos.coordinate(); + + if (!(d->useGps)) + return; + + queryCity(); +} +//! [3] + +void AppModel::queryCity() +{ + //don't update more often then once a minute + //to keep load on server low + if (d->throttle.isValid() && d->throttle.elapsed() < d->minMsBeforeNewRequest ) { + qCDebug(requestsLog) << "delaying query of city"; + if (!d->delayedCityRequestTimer.isActive()) + d->delayedCityRequestTimer.start(); + return; + } + qDebug(requestsLog) << "requested query of city"; + d->throttle.start(); + d->minMsBeforeNewRequest = (d->nErrors + 1) * d->baseMsBeforeNewRequest; + + QString latitude, longitude; + longitude.setNum(d->coord.longitude()); + latitude.setNum(d->coord.latitude()); + + QUrl url("http://api.openweathermap.org/data/2.5/weather"); + QUrlQuery query; + query.addQueryItem("lat", latitude); + query.addQueryItem("lon", longitude); + query.addQueryItem("mode", "json"); + query.addQueryItem("APPID", d->app_ident); + url.setQuery(query); + qCDebug(requestsLog) << "submitting request"; + + QNetworkReply *rep = d->nam->get(QNetworkRequest(url)); + // connect up the signal right away + connect(rep, &QNetworkReply::finished, + this, [this, rep]() { handleGeoNetworkData(rep); }); +} + +void AppModel::positionError(QGeoPositionInfoSource::Error e) +{ + Q_UNUSED(e); + qWarning() << "Position source error. Falling back to simulation mode."; + // cleanup insufficient QGeoPositionInfoSource instance + d->src->stopUpdates(); + d->src->deleteLater(); + d->src = 0; + + // activate simulation mode + d->useGps = false; + d->city = "Brisbane"; + emit cityChanged(); + this->refreshWeather(); +} + +void AppModel::hadError(bool tryAgain) +{ + qCDebug(requestsLog) << "hadError, will " << (tryAgain ? "" : "not ") << "rety"; + d->throttle.start(); + if (d->nErrors < 10) + ++d->nErrors; + d->minMsBeforeNewRequest = (d->nErrors + 1) * d->baseMsBeforeNewRequest; + if (tryAgain) + d->delayedCityRequestTimer.start(); +} + +void AppModel::handleGeoNetworkData(QNetworkReply *networkReply) +{ + if (!networkReply) { + hadError(false); // should retry? + return; + } + + if (!networkReply->error()) { + d->nErrors = 0; + if (!d->throttle.isValid()) + d->throttle.start(); + d->minMsBeforeNewRequest = d->baseMsBeforeNewRequest; + //convert coordinates to city name + QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll()); + + QJsonObject jo = document.object(); + QJsonValue jv = jo.value(QStringLiteral("name")); + + const QString city = jv.toString(); + qCDebug(requestsLog) << "got city: " << city; + if (city != d->city) { + d->city = city; + emit cityChanged(); + refreshWeather(); + } + } else { + hadError(true); + } + networkReply->deleteLater(); +} + +void AppModel::refreshWeather() +{ + if (d->city.isEmpty()) { + qCDebug(requestsLog) << "refreshing weather skipped (no city)"; + return; + } + qCDebug(requestsLog) << "refreshing weather"; + QUrl url("http://api.openweathermap.org/data/2.5/weather"); + QUrlQuery query; + + query.addQueryItem("q", d->city); + query.addQueryItem("mode", "json"); + query.addQueryItem("APPID", d->app_ident); + url.setQuery(query); + + QNetworkReply *rep = d->nam->get(QNetworkRequest(url)); + // connect up the signal right away + connect(rep, &QNetworkReply::finished, + this, [this, rep]() { handleWeatherNetworkData(rep); }); +} + +static QString niceTemperatureString(double t) +{ + return QString::number(qRound(t-ZERO_KELVIN)) + QChar(0xB0); +} + +void AppModel::handleWeatherNetworkData(QNetworkReply *networkReply) +{ + qCDebug(requestsLog) << "got weather network data"; + if (!networkReply) + return; + + if (!networkReply->error()) { + foreach (WeatherData *inf, d->forecast) + delete inf; + d->forecast.clear(); + + QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll()); + + if (document.isObject()) { + QJsonObject obj = document.object(); + QJsonObject tempObject; + QJsonValue val; + + if (obj.contains(QStringLiteral("weather"))) { + val = obj.value(QStringLiteral("weather")); + QJsonArray weatherArray = val.toArray(); + val = weatherArray.at(0); + tempObject = val.toObject(); + d->now.setWeatherDescription(tempObject.value(QStringLiteral("description")).toString()); + d->now.setWeatherIcon(tempObject.value("icon").toString()); + } + if (obj.contains(QStringLiteral("main"))) { + val = obj.value(QStringLiteral("main")); + tempObject = val.toObject(); + val = tempObject.value(QStringLiteral("temp")); + d->now.setTemperature(niceTemperatureString(val.toDouble())); + } + } + } + networkReply->deleteLater(); + + //retrieve the forecast + QUrl url("http://api.openweathermap.org/data/2.5/forecast/daily"); + QUrlQuery query; + + query.addQueryItem("q", d->city); + query.addQueryItem("mode", "json"); + query.addQueryItem("cnt", "5"); + query.addQueryItem("APPID", d->app_ident); + url.setQuery(query); + + QNetworkReply *rep = d->nam->get(QNetworkRequest(url)); + // connect up the signal right away + connect(rep, &QNetworkReply::finished, + this, [this, rep]() { handleForecastNetworkData(rep); }); +} + +void AppModel::handleForecastNetworkData(QNetworkReply *networkReply) +{ + qCDebug(requestsLog) << "got forecast"; + if (!networkReply) + return; + + if (!networkReply->error()) { + QJsonDocument document = QJsonDocument::fromJson(networkReply->readAll()); + + QJsonObject jo; + QJsonValue jv; + QJsonObject root = document.object(); + jv = root.value(QStringLiteral("list")); + if (!jv.isArray()) + qWarning() << "Invalid forecast object"; + QJsonArray ja = jv.toArray(); + //we need 4 days of forecast -> first entry is today + if (ja.count() != 5) + qWarning() << "Invalid forecast object"; + + QString data; + for (int i = 1; isetTemperature(data); + + //get date + jv = subtree.value(QStringLiteral("dt")); + QDateTime dt = QDateTime::fromMSecsSinceEpoch((qint64)jv.toDouble()*1000); + forecastEntry->setDayOfWeek(dt.date().toString(QStringLiteral("ddd"))); + + //get icon + QJsonArray weatherArray = subtree.value(QStringLiteral("weather")).toArray(); + jo = weatherArray.at(0).toObject(); + forecastEntry->setWeatherIcon(jo.value(QStringLiteral("icon")).toString()); + + //get description + forecastEntry->setWeatherDescription(jo.value(QStringLiteral("description")).toString()); + + d->forecast.append(forecastEntry); + } + + if (!(d->ready)) { + d->ready = true; + emit readyChanged(); + } + + emit weatherChanged(); + } + networkReply->deleteLater(); +} + +bool AppModel::hasValidCity() const +{ + return (!(d->city.isEmpty()) && d->city.size() > 1 && d->city != ""); +} + +bool AppModel::hasValidWeather() const +{ + return hasValidCity() && (!(d->now.weatherIcon().isEmpty()) && + (d->now.weatherIcon().size() > 1) && + d->now.weatherIcon() != ""); +} + +WeatherData *AppModel::weather() const +{ + return &(d->now); +} + +QQmlListProperty AppModel::forecast() const +{ + return *(d->fcProp); +} + +bool AppModel::ready() const +{ + return d->ready; +} + +bool AppModel::hasSource() const +{ + return (d->src != NULL); +} + +bool AppModel::useGps() const +{ + return d->useGps; +} + +void AppModel::setUseGps(bool value) +{ + d->useGps = value; + if (value) { + d->city = ""; + d->throttle.invalidate(); + emit cityChanged(); + emit weatherChanged(); + } + emit useGpsChanged(); +} + +QString AppModel::city() const +{ + return d->city; +} + +void AppModel::setCity(const QString &value) +{ + d->city = value; + emit cityChanged(); + refreshWeather(); +} diff --git a/examples/positioning/weatherinfo/appmodel.h b/examples/positioning/weatherinfo/appmodel.h new file mode 100644 index 0000000..a4d4e87 --- /dev/null +++ b/examples/positioning/weatherinfo/appmodel.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef APPMODEL_H +#define APPMODEL_H + +#include +#include +#include +#include + +#include + +//! [0] +class WeatherData : public QObject { + Q_OBJECT + Q_PROPERTY(QString dayOfWeek + READ dayOfWeek WRITE setDayOfWeek + NOTIFY dataChanged) + Q_PROPERTY(QString weatherIcon + READ weatherIcon WRITE setWeatherIcon + NOTIFY dataChanged) + Q_PROPERTY(QString weatherDescription + READ weatherDescription WRITE setWeatherDescription + NOTIFY dataChanged) + Q_PROPERTY(QString temperature + READ temperature WRITE setTemperature + NOTIFY dataChanged) + +public: + explicit WeatherData(QObject *parent = 0); + WeatherData(const WeatherData &other); + + QString dayOfWeek() const; + QString weatherIcon() const; + QString weatherDescription() const; + QString temperature() const; + + void setDayOfWeek(const QString &value); + void setWeatherIcon(const QString &value); + void setWeatherDescription(const QString &value); + void setTemperature(const QString &value); + +signals: + void dataChanged(); +//! [0] +private: + QString m_dayOfWeek; + QString m_weather; + QString m_weatherDescription; + QString m_temperature; +//! [1] +}; +//! [1] + +Q_DECLARE_METATYPE(WeatherData) + +class AppModelPrivate; +//! [2] +class AppModel : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool ready + READ ready + NOTIFY readyChanged) + Q_PROPERTY(bool hasSource + READ hasSource + NOTIFY readyChanged) + Q_PROPERTY(bool hasValidCity + READ hasValidCity + NOTIFY cityChanged) + Q_PROPERTY(bool hasValidWeather + READ hasValidWeather + NOTIFY weatherChanged) + Q_PROPERTY(bool useGps + READ useGps WRITE setUseGps + NOTIFY useGpsChanged) + Q_PROPERTY(QString city + READ city WRITE setCity + NOTIFY cityChanged) + Q_PROPERTY(WeatherData *weather + READ weather + NOTIFY weatherChanged) + Q_PROPERTY(QQmlListProperty forecast + READ forecast + NOTIFY weatherChanged) + +public: + explicit AppModel(QObject *parent = 0); + ~AppModel(); + + bool ready() const; + bool hasSource() const; + bool useGps() const; + bool hasValidCity() const; + bool hasValidWeather() const; + void setUseGps(bool value); + void hadError(bool tryAgain); + + QString city() const; + void setCity(const QString &value); + + WeatherData *weather() const; + QQmlListProperty forecast() const; + +public slots: + Q_INVOKABLE void refreshWeather(); + +//! [2] +private slots: + void queryCity(); + void networkSessionOpened(); + void positionUpdated(QGeoPositionInfo gpsPos); + void positionError(QGeoPositionInfoSource::Error e); + void handleGeoNetworkData(QNetworkReply *networkReply); + void handleWeatherNetworkData(QNetworkReply *networkReply); + void handleForecastNetworkData(QNetworkReply *networkReply); + +//! [3] +signals: + void readyChanged(); + void useGpsChanged(); + void cityChanged(); + void weatherChanged(); + +//! [3] + +private: + AppModelPrivate *d; + +//! [4] +}; +//! [4] + +#endif // APPMODEL_H diff --git a/examples/positioning/weatherinfo/components/BigForecastIcon.qml b/examples/positioning/weatherinfo/components/BigForecastIcon.qml new file mode 100644 index 0000000..6d2b4c8 --- /dev/null +++ b/examples/positioning/weatherinfo/components/BigForecastIcon.qml @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: current + + property string topText: "20*" + property string bottomText: "Mostly cloudy" + property string weatherIcon: "01d" + property real smallSide: (current.width < current.height ? current.width : current.height) + + Text { + text: current.topText + font.pointSize: 28 + anchors { + top: current.top + left: current.left + topMargin: 5 + leftMargin: 5 + } + } + + WeatherIcon { + weatherIcon: current.weatherIcon + useServerIcon: false + anchors.centerIn: parent + anchors.verticalCenterOffset: -15 + width: current.smallSide + height: current.smallSide + } + + Text { + text: current.bottomText + font.pointSize: 23 + wrapMode: Text.WordWrap + width: parent.width + horizontalAlignment: Text.AlignRight + anchors { + bottom: current.bottom + right: current.right + rightMargin: 5 + } + } +} diff --git a/examples/positioning/weatherinfo/components/ForecastIcon.qml b/examples/positioning/weatherinfo/components/ForecastIcon.qml new file mode 100644 index 0000000..7e48536 --- /dev/null +++ b/examples/positioning/weatherinfo/components/ForecastIcon.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: top + + property string topText: "Mon" + property string weatherIcon: "01d" + property string bottomText: "22*/23*" + + Text { + id: dayText + horizontalAlignment: Text.AlignHCenter + width: top.width + text: top.topText + + anchors.top: parent.top + anchors.topMargin: top.height / 5 - dayText.paintedHeight + anchors.horizontalCenter: parent.horizontalCenter + } + + WeatherIcon { + id: icon + weatherIcon: top.weatherIcon + + property real side: { + var h = 3 * top.height / 5 + if (top.width < h) + top.width; + else + h; + } + + width: icon.side + height: icon.side + + anchors.centerIn: parent + } + + Text { + id: tempText + horizontalAlignment: Text.AlignHCenter + width: top.width + text: top.bottomText + + anchors.bottom: parent.bottom + anchors.bottomMargin: top.height / 5 - tempText.paintedHeight + anchors.horizontalCenter: parent.horizontalCenter + } +} diff --git a/examples/positioning/weatherinfo/components/WeatherIcon.qml b/examples/positioning/weatherinfo/components/WeatherIcon.qml new file mode 100644 index 0000000..638a52f --- /dev/null +++ b/examples/positioning/weatherinfo/components/WeatherIcon.qml @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 + +Item { + id: container + + property string weatherIcon: "01d" + + //server icons are too small. we keep using the local images + property bool useServerIcon: true + + Image { + id: img + source: { + if (useServerIcon) + "http://openweathermap.org/img/w/" + container.weatherIcon + ".png" + else { + switch (weatherIcon) { + case "01d": + case "01n": + "../icons/weather-sunny.png" + break; + case "02d": + case "02n": + "../icons/weather-sunny-very-few-clouds.png" + break; + case "03d": + case "03n": + "../icons/weather-few-clouds.png" + break; + case "04d": + case "04n": + "../icons/weather-overcast.png" + break; + case "09d": + case "09n": + "../icons/weather-showers.png" + break; + case "10d": + case "10n": + "../icons/weather-showers.png" + break; + case "11d": + case "11n": + "../icons/weather-thundershower.png" + break; + case "13d": + case "13n": + "../icons/weather-snow.png" + break; + case "50d": + case "50n": + "../icons/weather-fog.png" + break; + default: + "../icons/weather-unknown.png" + } + } + } + smooth: true + anchors.fill: parent + } +} diff --git a/examples/positioning/weatherinfo/doc/images/example-weatherinfo.png b/examples/positioning/weatherinfo/doc/images/example-weatherinfo.png new file mode 100644 index 0000000000000000000000000000000000000000..6557b57b2764a3c5e435755d05108a06fc1d563c GIT binary patch literal 82081 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

l;#XiE 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

q~)d8s!8}{10j9MX@ELWZG*C&ZZL! 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

g`s}QO5ynaZ!c)D4y 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 literal 0 HcmV?d00001 diff --git a/examples/positioning/weatherinfo/icons/weather-fog.png b/examples/positioning/weatherinfo/icons/weather-fog.png new file mode 100644 index 0000000000000000000000000000000000000000..9ffe9c4afcde071ce38d6846f7b2e2bee233cbaf GIT binary patch literal 43896 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�J}hmz``kpmts#GklXq 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 W$Cxu22_JOG2`p6Z{w#w{Rv)qrRC%Yn70e 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 z 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 T&UI|Szei3=S+YjlB<%kIn6v!l literal 0 HcmV?d00001 diff --git a/examples/positioning/weatherinfo/icons/weather-icy.png b/examples/positioning/weatherinfo/icons/weather-icy.png new file mode 100644 index 0000000000000000000000000000000000000000..18d203840b84401e6d7fefdb0846d09e0401c2f0 GIT binary patch literal 49362 zcmeEu^;?wh6Yje#EVZOGNSAb{v~;R;H!3aCwRDFl-SGjX5$ReQX+*jfkdRs$B+vT2 zuJb>f9}XAn?z{QU^UTbB&)l=b=x8b9;ZWfK0Dz~e^5PW$prLM}0ay^!)w$2F8`KrL zhrFsT7V0Me%QhNyjP0gk#rqfVa0d&s!Hq z4;w2tJ092f4%tV~sQ`c;P<yvZP;hW4n7r1j3m}%s&wsXEb9R{-J!{h_Q=vA9LCHlN;MW=yHVUa^L9$fPXipLXtuM9*f2ghXVf|_&{$9{O`G72CyvZnD8kU zC4hQu=&tM#@bCBqraT1p?G5KMzzOs4GVEo^$C*&REenCoix2Sy)7k09 z1y?}eL9>+(s31vqJ^L_GZo;cTlktI z+MXil9VKwGE{cx8d%UHDQpyquG0PIZh(g)&22)@(FPQjY0~z>h1Pt_0!i?7+3)UkT z+2xW^p$~%sM4~u3wOwz37fghU6zE zeM!)o7pOUmO^;BbZ-=p*j=5JQ2Wuq8%3e0+hG!Ta5kRtTY6* z=DFCE03&{T#~pKdf0wzmY=;#<+5jUyiBuPic(0RTPX(S+!c`HiPsDE%cWy+yNIeIa{)B0hxmO8_L-3oyz-;3ns6 z?0;(XpMsXqNlgA)z8GLDrGUj>49)s9?z8$hGKB}KFpPuBaan!of)rDGq=ho_&1hu{ z2Ry9o{ZdZ3v5FG-#{ak`M#rOTOsVV zgYWaXQkhcr4{zsL$my7<%y7HMMUbC!{Uo@6PB#ByIcX8iTzx*~T@M9D*N*Uh9gkBD z$S^xIZ}}ZENtUn;6J?|Be1~z(^=&3l$DX{FEL%khYJkE-|7tG{rT$K+vbJ6cV5e@e zwzjGGygWquI=khSACrOF4$glX)dlvAI9pDgI!7Fze;vuI?mtVNIeKN3K2o0et-R2% zlm+a?AQ2ER2k$Ju7o7gIzcgtd`UV%ZW?yRkAA^Ws#;gUiqreFRC|P{x73$62N4yfT z&=ve!l*L0bqtRJnAIlj^ihmtQO{TIdV!8!3OSEG)^KXw~`(JXOMmKZjxLFSK!Yn&)Nal~@p3&!Idx8ZGkO z34s8b9xI&B=be?EqYsn8-URu`eSgZV$?Ung-gKML*zW9yVs+8@%4%xL6~x!@Imp-P#54VE7@u2mn@K`H*Qz?A00g~i{2CTHKNCq@QKXVrqMqPW~Xey^3^)=hdy{N~9w zW)@-r;#jC+TCxr^`!}efeX^JHs{ye=upaL&{X65g6&@%$DG7`{q<+F zn2lMLDxF1SoI;XO^#BWu&p&XfPE4)N{+=~6`J=8a^Amv-?0&z-CKhHA56QxerA8th zTkLNaT>P>rEP@eh0w6DBJ~d72#pH*lSuV-|4H$uQavOeA(eseDyHs=Npf}xgrNAZ; z!F^2uUjMr~dd^H*&X^Sm1zx73&Hw}?fqYQh25<}Kk1zd%ou-zBmiMhsD)sz1Zc+Yw zO2Fhgcdqf8?a8+hV2BuOV^0FN11eX$Tqq_z!CBpfZ;$f2q{eGvr2;ew?_!7i(Jg$O zmBH+k$@2_u8NJxvpk?&qy5nX!BJ?6!T}&7N#A|C@^l%?<*s$*g5`TiDTct6}rcv5H z+hxccVY!yP8 z-BnW7-{to~_sx~I8u~^zFA2lt1~TV^C}4rJo3B^HHlMs6amx-Jwz2nnUz~8r?8R;i zNJOKo<{HgwYZ85AALacRS%)zdWPhn(oy`1008)y2x@#t&?nXL=#lBr^4LMi`ZZo(t z=d7V#RKO0*g#zV(s@9Xt>lvOo)HINED+-!#-3)&sZIsYI)Af++ z%S9o~CgnHU310$~)nmnr6w82{hrOS`+PD(&5+)@~YUsL87Y+Ul(8fxt2k?8k&|Ri= zp|UJ!gvYInd9V6uhEg#-E*?k=ck+{2n2Eit3!JF1t+3#HVo)BrxdaXyc#E&;`nQG6 zCUlFBEW44Q1O0Pw5_2Sce~{W{T&U$VWmFDyj4z}8ZNy-eqBndIANf_!4zOjt%~ z^gjehj2aoKab7*^AN{Dpu{&Nmm8)6Enf=0GAfV*6YRM-YVpU3PBPB)#2CEK>#?hEj z^x}<}R+ad|8p(uk#>__%OP-3IC2M!-|1e!T2KmH9a9JoMqPp=YXeO&BehFFFA4L={ zsb)lS()DJ(wUSO(D;Li$Dk;=?^^!gHCD%LluT=QZE9+X`u&Mw`lNLsi_lizVq;;vB zjOn8J!sWe$qEI-){t2m7mxMI`1p|$O8wPwU^Usf{Lsv#3Qd__a8|6rpfCJ)aMka)6 z^US>$C>w+}5fS>2kbWdKAEBKRy~0+&Xe3I`VTa|?u#b%E%D#?g=G#6uKJ1fS!MRe` ze`9eh#zd(|L<^xR@+-Kk|P5_gjBOuBM}hS6==qyjYyrt0+}c7?=IM=UWj_O& z#CrqbkkaFmjG4SY&=C9}e-C?W-|YK@Eg!y!pN&2wUUP2DM0yJ=9u*NNG$yC*MX+Py zIBc#l$kEZ+K);654EKG>I*P^8Ee$tT&2J?7p0C$iYadC${#l45mgBu5>0fKg{-Rj< zA42#he~I5m2#7S{%+GTa=yDY6auw@xgmv;NSiy5r-6-LTfGfx+ikQHg))r{YJc)UR z4S$4A3CDAshe_Vz(Vi~2nTrbG!VxsBF3on|Fhq5bRq_1Hq%Gq{3B}Y(uZ0Pz z?qjXu8B%rSGc&%8pks-?L*()FBK%NDB5SnC?g&vUPc)V5SE>$As6rF*U~$?;S-@o- zD43K`#fTGO-OohNv@T5J8<}SKc`^X%r%eahXr|RsS5LueVnE_d;%^w?jttzae>-qc4u5w3h zel)eVPOooI3B>6rUg`Whd4l}q5V;OJiDb*S-w>ELf+DoZzLYJ!u}L)-gAztj_w6J< z+~k;UVOB{ox=1C2F<*;gcer$F`jeci0h!r~*MUn9(oqhW z#t8U%7QeBo7Mhv_8Q4;pgsQu?oMg7hi2qa}WrH77UaHXK*W?}$IG?V|9BHfa#4_zo zwJTcDT|jPcgLZ9H0f*|R))09kQ92edXEsJ<^-kS4bNKKgnZD?I_TO=hx zzqI^7AGcbK-uepX+cU~P;qfKHgKIy&DcjitHc){Nd{T?c49^zu@ZT;m0U};r&k55h zTI$O5l{Af}nT#l>9tYhATn@fXT2;U1`l=^SdEv0!e2D8U1D01*Uy+rYdCC4~DE)&I znNXz@Np0iD-eEudiv7Q_a;UQ&awaX9<4 z8N7K_cQSRC|4g$a$>_aTk7>RES1fRsBiGL8WyOvBH z&T{o>HL4?EOx2}VztDtrw@_k#@$i4_l~g#`Hy>>q6!|_&7*?8%9rrgSq^c-3wc&Z% zoQI+LLQl`BM4G-xUBlaLhJz5~qc~90%a88e*do=2BLH|(;k-cY2U}sHhfgW>`zyzS(?Sn1%sw*Rd@%e|+2ma6 z3&srrsf zo8HtSC5b8>rBS>m7ksP^1ku7VUYJUYYEjty+pwB0Oa+BNF|Rl1o0yn&uC=&Ql=Y(d zT0_Zp`E*|s=WkyUdHzs72PXg=sc4*v@B5-b2HofS`r>xU#BXKQbR+H^r7$VMJ3il0 z?LHA44jh)I^>q;MyBPhBZMTg4TNPKT*tM=e+%K&me;XJ_VUg3xe)-8wV_v?PWd%$X z8X!ApGG(vM(B*#XOQVLmPko(JZSG+TJNkOcE3%zVmh}bJR@_M~{dmtaO!|BOl2%r^ zSUYldEipo9;J3ho3=qsODCKLHy;Q$R4+bm)tYbkwWTm^0#9perGMQ>cH8q%FV3c`2 z6odHq>yw?*P%pNrPpb5(pU14xg4WVCqdVway1faoGvs-pAb1S=bryI%GbH;j5D7VU z`(dw<(p`d{kpndKJdpB7aWCg-1RafIzG228B;EHVjrc}ud=Ro2N@q(Ccu0kR?9~0; z3u2jp_xSNoQbh2xuYS5;EgB-m$G4Yn2(Bow@~yYMH}8Jpe5zDL12(R3uez^qGB}`a z{j7azYo{V7n9TWSekh&4?RyO7nPam|TWm{&US*AIktv7r9Kxps8^K#SxP)Q{PkDo$ zIs&LLw9QcU??OkATk(9ZXZpyDEhS|}L9yHaQGrR!%MD8zA8PrXH*B@iC^D^%%Ia5i z-zvdXvy36VxjNTITEe(rneAz{1B0vb6k$CR>ix!Tn&b(xK10 zCK1V}fbLRE$WqZF5Nr2Xm}KAI`7T{iUPM#2Dy3~qhkf7BkXMQIQo0NTq+p|R(A&p% z)$v;iDaebk#b+i;vfoC_P5B$4-=21-80Owak=$lsQWYE3xt{x_4##GNRHa02C7{uc zx+0ExNm1L}{qv`Uh^U8T2jNdJ5R5Sm4G2aR4HH_$Ulf%oVJKcq0^{w*M!K3jy*Eas zQMbGKpd2xg$jzuIcen54fkqx`xl(~V$*|z@-N)21&PA)c8>`?)X1S)_wkJ4;!Waj_ zn0>sa{?iW^TeG+(#~n)Wno|pQ8-ySUq2Be{?b~V>jBMcK`Bmf@<27!Zp9KelRSECY zzw@*DFMEGR3I=SlUFzLRh4T|Gl*HeMW$GMSEVEmAR(SEw{51tLQh<;Z84vJ@+`G(4wX5pVuMq6HsLrsECi5Wgb}V zSkt=?GHdq**8T5`=6h9f>Vw?atVWl$hEl{?xq?_jL5SJN`Lmj*7<>uxHoj;l6u@o0 z*(yh2rQaSp`iC>>KQsCrQQ(E|@#}fIubr#o8|b+p#!WAjHh>Z+%^8FGqJfnf8Jn#rl&( z&N#NyMcj7Qaa>mNdfBeX>Kf}dXQ7K|$4$V46zs8fZVa9$49wo$b>SjpKzz+(V_zqy zrz19%2agXvDZ8KUVku#^ebgFi@V^j|L>2g0G!yB|QogsQaHC0O65qMc@&&{68MrGx z`fVP8{E~&fHP`;dfLtW~sGDp~ut1Inmvb8T6T;ki|Et9|KS6Bh2e#~Yj@)S?>hnVe zb04r|a)`7tFk3$rYCtiMEya^eUsfzy>?`5L1?D>5JhhSSr^ryZ${vudYXmz;f2SCB z&6r%1xj+tq1pn0P4D<{`j-DI#M~TvH^qSPOfA$)MhLZxp5Jb||yN(bkyMIC23c7om z(ewDn*#)0jwP(^zMk^jU___>#V~VMl;y{z>6VA)k4^golbd6#jZn|V`4dH9%X{#pp z6O{PJO3*$Aj6vx9OHfRAr6L0R>*Cl>(U1w|U0)Z20(=wi(Vt&za6<{ZoVEIc!L8KT z3tT4%3a!RishsQrx94K#IUvEAb2x374Rc5jibM6{u8N>rym5DL{-h!fXni<||JkZ@ zG}*Q30Ij~x3lI9|>JLH9!c;d8Zy%~xEev}3Y8zj|!y<&2+dE(cF3W=}3k|t^w+J@i zfdbx@i|U>&9u2(c&*KA7b=d^k^;4h=Qs5xDBp|Sy^qLJHabSaaGCySny(}3M9(T0k zZZbLu8NgUOQZ>LW`m4yQ1Uhhf=S>UqS>`@H@&m?$u7vp5V(IxKOYG2^uSRB5G3l{7 zo5v~?9_2b7y&@hvC?JEOgXqmc}?M3pwNYoq_R?s0s{{5x>_q2ZhBL z2r;n?Meo+2Tj8+Hae4eLctqxbzcS~g+}wGfYZye{I*p-LLry+&g#MSAu{eTt?D9xq zh4r_c*v`;aT(eA5#zsS%Pjl@;W0wM*2qk&_Ct?qg;J_ z3TQhSNM0sgZvKt|i+Kh>lb%xdTWV8%wN3c>pQPbIMfg*%za}0_UFmOMscw9Ft^}97 z=O}nyo4Y%RNJML90>;@oZ6j zoNr4snSjC#gQ@=5eviYXgbaqIqN&N(w7snJ?qHM6U>R`p5sTA^zO2X!S;B1@-f7K7 zVW}h9wi9HULQIL_tBT67mbLRQYowodc(72Ul_jSEyu-+22(gzCBL5^>MAdIEjNiA1xvK_6bJ%65H9$#Ox-}notu#kG(d)7?3 z3>h`a+gMLnO`J5SN9$iX?YDS#Evbxt!lf5ig0nneiQ~e_f(ryb>-a|cKL1Z8l{Qfn z&9al^4B%qw&Ane`msLCfM`A1{lzNQTkQeN)k17k=99*XLQQ$-ElXZ)#$i7h0T&10+ zUJw`)`OmpeXc^gyN&F-&zgN^7Jp60QviEvlsVcf-|DK;Z+ISAXHSaBiBIR3n2_wUQ z^Do0B@Sv>@&|s#9pfk@g_<%jWD3WL6zQX&Yd(eaZ^J1ruB@w&}e5{N@RvG83F%zpf zihnrc00iK{Uq~W3RiQ~z?*Ruo$?$15)G~i3|6_~xqr_B-pqablcA#$+L0h*oV*wcp zC~WQ;9VOool6iRtYGwqR*VSZ^0+Kk0ai-cc-~|O(0DCaNeO*1<#Oy(hfNn;qfbsZJ z9P|?A3>D8xz0{AJ?09t1#Cm&ZUiu6R6I1YG4ZYzkblGHBLG<1IQN!vJ?IGykrDyh5 z<$f~f+vfU=n!7yo)rpzD%hab63 zZQ{rrax-^C(9rOQ;#{+v2Kah$^!#_*VW+#`fe*O$X-~cu%gOIQy<+~SgtRi!u_1Z! z&u*-FySS~9rVU5w9rVX?9ez9Sf_Txq|HcLqLPwi+=yL8by*t;;@zU&6TWR^Qv$!VF<5aFxw<-flw?*fdd64m zs*0@4fxOW>*zZuB^W@@o-55^5^w~LeUd}ebZNhO7I>2G7wZW5ZiwL%@rQXq|>(5la z+Wg4Wiz>sDwOEsNN}jS^TKQcZaTbNh1i9Cbr|ZG3+TLjYCj#7Y-YwWXSnI-5!SqgX zeEKU|G5of#)$ACkT!d>GFE1VCj-9zl{qOWXhBzPH$9>iXdpKe^urs37)%bOJTC;vd zz%5JHb=;&HW2tDl$nS!ij1Pq4{9C6{mVJnLH+^bPNnr4f{OjAMdCbGaUfKD_?Xf$2 z_l&8~&x{P2ZW?m0>OjL22c8afW?GFU>XuJA6|uQJj0?bn+JDWj6ZbTAoPW=23=)@c`oH3q5Sp7*x?bu z@AMu8wLPJs+z+));>+^CKRI?=ZPP?&9%#|Rm=3B}gC-lD0azp3!%VT^?3jvdm3jVJ z7wOI<_vV{woWVkmBfmomwlgV(4%O#g1)`_#LOdK5mwlRvj8zVYbaXnS9WSvorhfoX zuQE3`_9v~Har^yymnaqlB9AjZ0DCvFUp8owBnjFpVgF2HP+=Jz3gN1s@9$hygt}OI z2qq;-XAUNnIGTw`nK3gyD{1>&G3`fvd>8od~pWWr39K=rawl)ll%44H?nIrTEJ*M7@F{^sv) z^MJh<=bqoCo7)oO1W(wsWPHEV?Id1ANnD*C>Mfh_=%yBvS&XC#gEaU*RXI-;3~*fH znF_wIb3Mrd=+$dSVc{A-1Fp9mzu!3+?BM=f9l=X%7<^-%o#i;eY9s zWG>Yby9EFa_*aTSE8Z^Eh+<6Yj}~(y9&AzW z)|EjRZsQ?H3p4or)_~*eQmJgpfiRHM0C3y}jMqhVn;v2X5H)kwvYBswpPX~jKM1Tj z^cXAYNmf-GVzsqRAx?>bJke2z4V3y|Hn_Y233~q);@a_uJ=+wU(7^PZ2L6a+`Mxa< z$GPGk()wY*yU^X3_Z&yQg?}1UI$0>wM)dsY-8~d2+gDT%ti#m7?#PmANR~o%1h``Y zbtH=u$)cNy0ixQ)e2QUG;>SKpV0E=il=_oLSwl{?lyF^ZE++vz$gCIZ;IsRqaezQq zglG+K!RXY|+!kyIQCDt`As;)37`z@-v#fMo)MeVmw zw*5ujFScoh-*LrZtYRFup+Ki*!2N{JDGpQ{dwur=czpY7?r2|Y(_=z9c0$^d#-{gv zsN~bk^hK5uNj6MSjAPxfEuFWs>E>NX)Dq9-MfJiwT~|Fr7J#JSL68L_&t=Y&mkimU} zZT>DY6IWcAc*lvlcA^z~Un|IN4DLwoP?zR-8*m!Q7k8tgA3}$cXNXBM85q=)|AS%e z;1l02&IvbBz%=CLMeLNYT3-p2Y`?hQj{3jEkE8y^3-EEoU%Nk1zJ6oh`PF~c5+A;XQk?HbzVMpHHU6)b zeF;3G!E2N}HI34am;Y}1Y()r)eG3LMb&Gz8e_faVD*_Q!TnqZuZf$5qhJPSb=WSLV z@g~HvL8fqU0#9}mA~XiGciWW^&BuGa>=|Y<0r^lYK>(+Is9`=en&?H*G1`qdypizL8KotR3E+&*Dh8 z(*?gWa}^0O7)L6XPuW&UIQ`aZ_j4y>5jO9BdZf9&#WpCfhikgbzP^;cjg}C^X1Vi} znFL$aNM8P9vX0@*hqoV_`#NVM4v-&@;^SAphaY{1wkT1DRnuN-nC%#vt@R|WcEpIT z{-tQKwpCCX0fJC+=PC6|Grs6~ynmLvqFuiRxN8IiCO-|-JRD5>T*hqf{!xqCJrw1+ z6lt7adi}M$W;c+ix#B-_j}FHp^g07iaKyJ~$BKH&g-#huvyRC0vQLxDP9{YbdMuN9 zMD)Kwzv2LxaSSty0QmudyhX+k6inB30~gD8!~c45l$%? zenU5iGjb!qvpe}KSX_wnRLXFC)8Bx6Uu$Mxh(a(bA|*CF+!^|NujQdCrsKFg#?k&Y zF>04@+DMo=wyLPfZ;dfMd*R#qAf#zlchg#XVuC3%c8LUR4K-JAXYkULE86gXwgzJ( zk|sCKJC8JoyB=^SHvX2;DoT)kKk`1acD13PF}CB%N1D~S9%q%i;P@3*7JrFdDjgDC zk|Y+)ITWAWcn`PsMGSwJy(R#;xi1Npx)NZbia#A~oP0?s93z*9D_gc+P`hs+w&2;x z1dqh?ocvlD9pL;b$gc%|M%` zP$7=fI{+t{80@NLDHX_6z+Jz_wRSmsYE9ySO-wM_PmVML_Lb?B_&s3Y>wA;~2e0l$ zghr43Q_b%Q_&9HbH6^|x(z{Mc%pVRmcZYrIy~n(o76ZaO-n2fuNnN$O)BAt< zx8G9ht3*e(H4aVX#r`E_Y_ItUmEAw55JJjzexxaUDRp+GVQ)aZrF>J;(#Ty_eSp3 zN51+0EWap^l4G%5HsHT)4dI+auJc;>)2TahD{gh-BakT4r=*|CF7R(_SU}fvKu}fY z6#>DAb(veqiOuuks4*on!&#*Sd)xD{?;iBsDTDF6x z#_&h~q?t#`_Sy2i48lBuFisr8$vic5xCq+w0CXhW5|S@d@2v?dr@p@Y&zH+}z+@ru z9~(&Akd1!lp8%iAdl!Oj)RR7*WX1O)0UVY+@Xkdz$VO3YUIEg`pcDT0aSV#roFvHy z+?(Mj`H|SZUj0*@5Z{5*CSIF4vT5QP0GkVbUy*v377RxG(;=8LIiCCny2j)qKYN=| z6Ldlow63nr@X4hU9^K?nNont%QT{B8JHBp^i2Jysvn&CEd71`~le)1M&mlU05@|CzVey$))8T``YsysQ2FYYv1Q$gG&Urt;rZ+-XrN$fc{w?|0y@01;AG$`7;~tmh|&K5Jl~~w(!K}x?{};`#!K?5O2S6oH#MXJ`w33 zF?hvI6m^<;qpd<708K!)rXIrB<4Yk zrU_wVp^*l>c;Y^_F-SCCC4<4VrF#e>qwGE<`!9-e-OE4f|u;H@$%- zo^iQ^MrJM|ertTWUGo5#Ofm)E&kD;*UhMz6bA(SM$M`-3?2PSS?;}lJ>{}7}|9niV z*p>M0kADd(H)7$I;2!(#G=2P^EfBRLt?1g)q4%GK&(_r>mq6A88Fpc!74` zIVI``W)QgYxg#6km-0x3g?61y>!4iqLRLnQUJ2C5#OU^+1Xt$lTIDx}ix&<`wOVTx zBVR(6jCIR-q$&QgFE^V()22P>FVhOoP7rD`?n9LUSEhD9I%3643w+)Y0dl9WtAtbf z_-3==ot-5{psCwL`j5vs9w%XxcNJZJZBw2YP(T}HL(t?<_6uWSAQ%rJgYW;)w?jKv zbHD7N;?plTKsoVcE4!g4iK=8xAI^phMN!FD z>~z1e!Wi)y1(7#y&GK^IWF9`7#;as-{7y`|6cY-VK9i69(6rq9bcWfhR^9!1;8WQYlFPQt!bbruky=f-5To_63$t z^u~bKG2lCHeqcb!Rx6fd>VFBus7g|LZ)UVy%=y|j!s)S#>40)lbFaN>E7}_ zfynXESX!iAjC1L$s>xWWkbqFK7GHc4YG&)*^T&P!4bq}5spj!70d?TM$G_g?+v`Zc zSas%prIyWp8vAax<~fq-!SltUAJ12ansF^&T$|E^7j9>QAp@_Rlz8;57kPd6nl4OTi27>%LHyyUM>2HQaM#f971 zG~sTpIJ~(}`%&&bhzXV;a@ABmep z*RveAbDV%Qdt36O3%B4Icv2S~UVbf#Nw4dKBnUcFM72UPQd*R~>%dqkZ#4uL+-^#dkuT~Jy)C|^b}RzYE5sAeS*g^Y?loI8V)Yoyis zKaymOd#YNl$!!@IqcUZOIO-Ml?pshc(^6_OaJ~TVDLw&YXFl9b*92o;(egw&FA~G* z>^aayB$L*C!$}_RnL2!Kmfh#1kIs&Z_GnsCKpo!%^3N=`B-(F?eCXV1GTUmfGwf}w zI>H^VM2-g<6x;nhFq+8U&kwoQTnj?xezuUF)chKas!dLGCOP=ebIc~ijmPG)M{-nR z%{XPQycbDRpQyeahVlGhbED(Xe|W<);Ao6uc}K-AtCtZVWf}FNMK17MaSOBrZ4`9!YrX zmQeesd1+mgVqd~!@ayHPs>H7m`Ln;(B&wGuY=q0iCCKIPNECZe$)Jqt?INFsrb~uB z9z4A#PwZsFXy}{n>w}Ir63U$=1}xV?9G8R`Yc{lihxrMrFT9#>)K5^&voV}2jFKO% zwc~N(>c#LYu=0KrYcv@y_8_AFAs%x1d<-}GsL7u8AUo+_ zSO2_RQY!T0>YEBi;-#eSv9~LKQzGS!qRKs*M7pQM$Hk@dI4vs*BF0f;-1vw4gP+Qh zgnm{|W^w(j1c{MvGaI4ErbaZKypHQiZNE$jVvf+i2H}BUxud(Bv(lsO!nHZJuikv( z_JlKg>#1nqAoumEd6$VwM-MU)D%SH%FbUD-x8O2OA2-O^f!vFl@4f0A^mOhfR7r7$ zAZ{qztzD@>$O$u25YZ7i8{LG<#$gf)ZN)lkB-8#2R8%Vund6;MHjk1`qcDFi7#l*^ zaA#u0UAA#+KrX+fF+|*}zH?c~n;qSt*>_n;O~Zn!FDnZxDfL}auK6bZzG)=nJDerq zt$D`}X+Qp|O#N1kUGwsvWhC1rAG=-7z5IJDJ>}{DrbvF@@QWKE{6+46TbK9xWGYPa z?zk7_qK+7-1YBqE#1Z2M&R@ans~e-#HxlA#K|QWfQyo(8zwX`7wydrOY_;ZLR)}DC zBgu;?y-dQf!hXNNDEGh5V*n<^Nj>N@2RD8X^lG-(N7c9gE9Y;R)}`bjeaQO6BJ2OON5fBed-W$1n?up->#|TXJSy&Uz*cdy4B~na$38}oU%L>Jlhs3 z^meU6JiHGY48IJstR1+G;A_e~w0H{fPCD_AK=r8n71wAXZgIk<_Px}rHHpN!NN+{@ z7xn8B6OI(^hi9T_)zON{*9JkIKALdle@VTn!x3$q=BT%&cuITeiO~YbCZ+*7Q&PKK zia@8E3*9TCbcW0K4*XI1k#|prN=X9NmpgU#aS7Y#1UW1-e9hn}ykBg{{0CF{F_C5*zDL zeSGKD{eswBw50g)&3(coS}nEjsi6$5%rEMFDSHE4pZ((9C|$pL?Dh{{znYdEKL_{( zgXfS@*@2U5sc?0NtHTAmvY2-VrXeY%3ZkP_B96XvQsoSZ3il0j!$fa}>qM#3IY3{s z-HRRla{Id8xz}7QB3D|w>zd8`?u>~lT233a=Nuv1L_Hi<;PM0zIzzY#31`iE-jRcI z*2{?D#$y^CO!y<`$nr?X4xgdNBZ$xOc^ueOEWfuBfLD3gGP&=gM|hstjj8&Jq* z&r@;c{bnmY(qw>Q_Bh6>&p_(9_m(=DB2LP-)9m^E5WP_D=H|lK{L#seJ2HW1l;i`& z_s}T+*Z+v#!3gnYdkZUko$w)sVl zg;C|%S?1G~pk_S84?i~UYA5-b4<#J`1v@_e#?HieuyUJ-Lk+>)s{3+PlG|`w6N^@_ zR;gb{IHRBYtdsd&1<8^;sOiCn9d~^G<4%1neD!qF5fbXQedTc`m9}e=ZHYjPu0iK( zG8+zs(P$c*vymy4fvcM-7eU1dtcbl1Pd-Rak|Ep+XF^&8T~m~=J~{4?)&N<}l>N%> zRQ7vB=)YvYY@<-8kNevV2P5HfCO_g|MWQVu65q6_)15r;dv0e(kt4Y__@yfLOuZ0S zZN;@zYK#h|P1pl))odk?kS60$4p@%Tfaltua8=>q*9ZK$-$I+UHYaa?iXPMT292Z8 zx%o;pjo*#qPMioWdgtcifs@gqqvqRM!#V{TZ)#Rx7x@l;lzxv<#qqC#Iks)R~8p=ek zqX5Zrj6KL8stE+=B)^OrzLw#iO|YispdglvTaABZ^mO_N@vEF9V}!J}mZml1*NXk( zFDHCBo0XRF3cID(YH*Iw8?G)+mP`T5!`}!UPq26TDayI+0^Ga%Y5w&M1eC zZKkOwX#d1L-;g^nB^Kx{zAa~Z;WeHuiEr82$zKl(;QufdpJ~51Qwj1B9aSy2N3fB* z7V79aj{hP5$P$_N9SXCm__F>ljlNBtiTD5e)2kcCAy-M}$DmsMzEkqYgqhD^@QrC| z2E=Z2>FS&W{k`^#hfKRXrxvNLyh+%otmp0X!DN=}eLqlbWdBA;dmn{# zu)UCOMw`(=-npOjqP(1}e>ujs8kM=LrQfDt5`O)?!iL8w7oDQ^fW-O)%aPazJ-tRYP^SXdpSxy+Xor8G(Swb zmsjylVzv>-aE*ihD=-o7ZS&pRRd1?1Z_j}HK~HL~+~dyw-g>Kj?+uK8Qd;k!y%$L7 zgEK>rIxu7}a=D;LA!Uy18inS%%HBc+i|DF(6B8(V+89yzUb(T@(eW0I_?yu@>p_v& z7)ti{xqp8i5o#NQ(xgrdaC1z!HwGEXL>ntKU)(OtrNhe-uL{_v4K-tDW;E~mn|Opz z)Bd1Ny0p{H_^ZZAnU(h4s$NGNg*U;P2tl5XbXYJyJu*zMtLDAM59I)i zFof%ya{t?zDZokv&}eS#!X}h?eUjyVy2@5@&G(z;WJT%K$K%r{x0Bg16FD{8!`J3e zP7>DNm%6w)nQJ|_`QhrPjt1G$m&vLsJ{xZ2T1ON5DGxobIMR+?AGe*Iwy1|aZiA^> zV`MqYZ=Qy0Kt@S*12&aK-5j^2p(}12kCc$cQ^iV`?U(vtJCgVx*p5eiMDg)T2|aeW zMQA|PWMu_CBCGB$t}ozA`wn_ib{_c$e>4ro>nBL(hzP?wewLP7%l0U|&(2g6f;(!U zzCKFFq$AGiJ8*6`&@YAC;}~DA37eCJd#;K7{ZmD1n&g|gc*vOh`Q-G|Nvu8-i#K&4 zs*dans|r#gQ&@mwHu?dHD;Yg->cQBR?+MC7?}MF~}Nn`zJT&C#=) z-g5K_-bxFTJIWkWSe9allhwJ4$xgH+)CB72=^!5 zIvI?w^MwaSx{V^*-vzHY^afbuOoN@JFLo#M#;#Yn-k>^9PL-shbTYr<%IGNL?^@NV zSpsf<=J+qV_wfPwl7g|lC|9d?N;_HnPy7uLK``M71HWaZObnC4*U}Lw@>9jTLvP;o zgew=(o7uGtlpauD>VhNtusGK_d*FXE@qnZ=M?V&T>Fks7RBzX{H)8)_S5Re5RuQ6= z*R_o-JVL*`U@4@Qbs_(JlWNvs;jJyze6`2>(D}n9&lI-z7UorBx_gf+byWV_Qadaluv%O%g> z3Dtu}j;X+h^(ogng~<0qO;8*r#%^)Bs-?=X0t2n|rQgX0Nsc1DE!1)cQmUIsAt5WBmy9okm? zAN3(64gj0Tp&4MZDI~w1*W+^ee4IPtVc&5^k<1dwo3lhd^1X%NsCv*mvqrKCk&1i! zQ8ki!rz@^tQQUd z39tqer?}+2U;5_HM?MF z9sT#cw!XV(lGd{o?hj>lG&CyKC2T}LNUSwyj&G8x*Jng!LRU?z2P*(pR6E#&_A#*! z08sLzE-wrT7F*l(zxDs=^LJrWEbx>M5>-@DhWak}v0Fp@*4QNh!FA+=)aHZm7>sY* zLjBH+A^1nl95VhMj7t0b&n6|nMMt=8_wFo@g~S6~0&}#2b$(C~ zcJL9p+yuYDhk`%_5Y!%&aU%5&4K*YsPu}}B(n;-qJ$sS93_m6}K&HJhS~Bj0I{+QP zgb4clIrx&?_9`1Sr?LL&@1*d043;I7;z$*NkR|TOj1HPW&B6Ypde}2bce|Jks5fZA zN3_`Q-22Cj{?E8yLBRi^>AmBre*gdR=Q$kf7$>sVAre_p*&I758j8vaNs$rRoMX>W zLPfTa?7e43WrfH*$jCa!JO^j|p0D@k`@7xFIscyL^}HU}W8Cle$K!H?D*s*&0ELo^ z<2;$|@(DEb21!GD+^ZLh=^Q7&zqh)m`&v?6@{ZDh&6OQo76Z>i(zZRr&fzHwBhXbh zcgh__^TiScfGwaC21q0mmV1xjNaZhEe*F>bL3DG4M|MaEXDeJp$)ES=;=z-O&j|3( zr5D)Zq(9nEc?6{f{h_sPtZ4uDbrb0owtRST|GiCI@J;9&!``Od56Rs*Lo2h=Z%DQU z!I!S&UU|#ZO`(V#5hTu$=f1BoHFzCfZ6p8gv#@qMS@3{;CZYxIHH%&Wfd2#(JU^Ry z7K58L9L5;cUp;3Y*7RVux9@(#44-lzs{A-s(NJvXcQ;PP9O<&&eqRqcz6H3kVob}m z`e{2-Uc2c-pivO1@|LWkYRMx7q|{eYMB=x*B_Ow^%rzz#(m8Im_uU-_4%=KjOl!gvT;l znhDfaAcR;{6!Y`>{zu2ySx6U;-4ct;5rUo50Cnb)IZ^gqT`GNBvkKPa&U==PDhnI_ zSHXCH3?^i!XReg%NdKRwWCe)MfNAZ^Pi;GwGc`OB!x?I^i*L01uOFmTF?YY9d&t{- za70<5)1jv1YYnRe=L%njqu!EkV$@Vdp7NlgkJQlcpX6_R0zi=z;2_N2cTy5ShS*c( z16EATvg;TbVxeYlp;o8l_|79S+G+V-eB;=;oVagWSIg!fu~~7|^P2n*|JSp*;FOmQ z3P09v#d>hw>BVpl$1h=THK0!|jsD|3L~W*z4^6%Ks(R$dOKtO&LOOZ$798z^;N?`J z>U@hY|H^N)4~5;aGRuPfuASol%rgase+slsZ`4MfC=d_+==yGKU1DZE)MfghKXW0n zv$CbY&f{+P!?Mc9p&NXp$8RAe-xf}{%xengI|MXB>jLA5AZO9B$~{fF{gfZH zW`_qFi)h^C4!%Vyl4Ha-ae;4>`^bZJ|M03l1lV8L-+yYLL!k5F$IU8%aO4nkqj8oAZcQt$5tp)+WbCtzJ5gz&nO-&aC)UcK zN@cD?EzNbg^hbSV(;1^ZC&mspkVT_xUXZ;XH^qV(1Z67b5Rw~UklSD_Rg8@R`!Gm~ z67KFUh!_Xb4?5?j4|)wBCo4XV-MS-4zq3s?k7+h7&$>kS;LE(ZoG3z!vh)t+7K}QS z$0Q*x7|?yqG8CY5Z}(m%oiy|f>W^|K17OFE%)yXmv%IMWhb`eZjlNa~k1q$bgix!+ z1iXLzeK?EvTS(`PN4ObT5Vwj?uU_Qpe|9$JQj3@C*;D#l>elpanF@(yu)6Qnkz??3 zlS~x0eB|_~?YoKirzH820^15>ZS?(+Su9iFVD_e}(9bgLYadtaFT?8F;U_OvA*3ac z=y`P8s)Fmq|Jmpj=ooE{x|nS@Pj@9bh`1H8tbE)bd~o3X?P}e!xXrJNk+#)NfWVF_ zDf0mkJb=+&rlh^j!>KPf7%L`eiSH_Eqwe&2!2VU z$aX*XstpbxrhpzMk{jlfTE_>ZgZ^{@zz;+8%n#{T@f{I&>G21Rc|3d@hE*)LAszn& z-8flXIA4Kqecg3BFWt+uI(KYe2PN?0d1wYcuHgnQ;W^R8{&T=6&y+^6AyFgOE$h*r zMI5AI4HktwIC2&F>&CF;Hq2bajxLGK^8GnUJ9U?q#ndee4tCL@%{tBmQYA4nf-vBx zTqE~z?^ybrtMVGLvsQohQS8$*nIKUQ;xv6#W*#7LBP8&B=^Lm^IyY&)^L6ppHgWG_ zU3z}kNc!!O^f#m6CpmTI_dxZZ*`LdE7k%(yKAOE727eEEenUX5emhul#QRLgM7_B5 zKi~5T(V+#wmlQ4ZxkQ`Y>9jYb^Pn6&R`+)i_V;hLCr5aQV;Mz`-abc~|9bOZ7c9#= zvu>qb(<5e1S_9hfLuk*sPS7sF2pZnpTCqplV+pJk2Q#2}7>jleyDvfQEV7z!V32!4 z97tAr-+;|oL##@a_}^e%Nbc0BA*4=?yTbrP-&O0`>@SVCEx3AhOS>vI^O<_2&$~d& zGZq$J4tmQ8Y!`F++M(INeEh<}u^YN2o}X=*_u(@MX2*g)P2;TqMW$abwie^oKPITL zrR^Sl<8ATTwtn|Gn4cAkrpFUNWd(O`n=en@ATR||#~Lt2Bm55!%XYQ&p5ooxpitPJ zM@yak*<+Cobi9?_P(!0to^X5!zkuHas>~t#*3~4r^lrKv0$1^jWOIF5F`uq1V^TyN zZ7MS>4?EZ_dfCQLNm@Mz-SLURiEzM@b_UUV+L}TQyn$KB)x4(yXdVMizL`&(Y)Zj# z3-Ai7SCEzqsf^L~kc@2uMP=)D^~8un-!D6_4?g4flI`DcGd(x2?T|=3T%H|u*S^Dw zeeDcy;NSn%dpqwMvqtI^;mO$phrj*QR=!So=Gkzr(9ffGUZ!fSi*n$ zh1Z*OkEgt8Y!$)ehaK#+{@1WGI+M~h_B3&+>%%!^<=<=%Tw6b59A4#!mp8<=zHr;K zTH!G?TIo*%H#ninHT&xaoxj_M^!L~u7%cP{GBexVneVG@UkiCp%itRc(V@urgWCR6 zm)jtzQDg!zUxwVv#R*MvM?pz%sa`^wOjw^lwdzvNKbA1J8aLbr;GPnG+j!q%K7K9k z2=C67r6#DEA(ZNU5ANb{*ze7o>Bt$AIFJPKgZJ)G@FnhYt#7Zy5tsQ|)kF%D$b9xUH zzj?`UC%)J8&ZZyMCL8|fn`uQ2->IFk6b5XHV*}0A=cmPB!vFbQP~gM@P#HQ#8*oY^>AfEe*{}yO zG@!wT-{_Bs?#XPm(V$GXQf-CT1A5vx z)2(iI+`J;>TU7$6J$YP>F#RxN(Dm>22*~ipUa5X8|MzF4cgiI?jG4|ChQRv645RBW zOn2-)C%M_Q98du#5p$rG~Ck2(E#PK_cmIKrQf<5GS>Be@{}wgDf@K3p=0I-Z%0za% zT-<(P;?hA38EiI27?oTzOzNiz9>uKsQ)NB4$~oUXLbjOK4`Lp3$^T-$`}aAUZo@g& z6s;E`Qh>YI6QpE%R~aAX%O9hl3CHaF#DyV>kNJ_OC{#Oz)S?TSFAlhO`dlBf@gBPC ze{|#xf|D(zp83m=bQlX*Z0P3*1SK|&{=Ze_De|W(854hls1%EI$PiDG(R5Bgp?UjQ z7eTw|BiMNk)og2xE>RM~4z>Cft<=e8*3e;XphTM`ix!FJ-Vu-raJa;E2=NI3Hlgls zx3x*+?x)nJxH&t^fimgJSE9u4YpqJS(xw6_qkH1_d`c!Zes^#4 z4%ZaUX=J+EOWRIUDnmM_DlXMMKAqH}bpbQlufhSE@tsrhbV40RJotuy&wZaC%GBn+ zdnlD6Bwj2;NKwLx?~_d&-+mY{*AqbNWkhpyA{&*Ge>$8WersVw#}@YHXG`Pcg8*D@ zed4ayD{j;K^|#boQ_r5li7FIosijmxYqqUf!);nr-;51;x*SS0g^&yZA7Tgkrf=oSU#VweRY{iQ$>o{6$s2K!vr^@HG;B3t3=ta-8|E?0FSXh zv1iazvn&-H0s&Y5;Xhxa;yK?yP}_L&WL7=QQqt}?7%8;gyh0UYT%X@17{kOK0rbT{ z)7SDf@~eO7Ut<7MV|;`bamm*bSH|D>^hMxin3Y$rJieDON2TR*G;llX#pxQ7xf&*tK$c*-L<}HZrbt*|WuBrf+OQW@}rp)1&N#>JkA$ zq5-H?mQwQ#|86Ly0vBjsL8-EV_pk)WUVB{5;|I{XCnENbDqg_r#BFD|93A(FKhxQE zKbW?LnxgH%)n|3P{0^;HYCf8Wh2`C%J3wW5OXu()MUJDIDcAN0Vz!+T3>#ST!UqSf zcFZzkMm-=YYB%!aBw1lqX|O`VjOhR~tIzCTv;RyAQ$l|t#E$-WC)=EAYR88X$vRBI z1bEgb(h7_}BpyYnGo~|@;WX*Z!4!MjrO4{C#~oqkfSGj>@u5dTCj7D{Lo>dO!I-^i z*&}Z@4{z_%V0uA-szf4DPAC0D(!RZx095BG8npyb zOXTpLSOZE>!>i{IX{XE5jjno8Pw@D07 zaeug;tMbP6f8<4kf!fWo2IRenPI&2F{uRHt4yQoG#AcR zU8Vfy7GBzs5rZ^OoNqhb_5D0QXVbsT)<&G}_vvEK^MwDL60P$jxKS!ir3aUX)aUq@ z^5;?8b_yC$p#6UFZ}(fy4O8XT*;Rg+R-6A0-HxT9{5d%mE~bFdwWKq_P`QloWb=EgnAXk6zZtyWZ;78*NCf%PN zJzur46t7E=#kU&L+lyGcNe^NPZTOA;{du#yLNn}@*+TO#DU;fblvAHodGl28cQh(u}RKxydmZP z9b<4@We`Bs#0*;>1?ogk?P$qNJ$1=}QM?K|>SX>_@y@o^W7OjI8+?3(%Kv-kfkF{q z7mU+~iRB{Ol)IVyW@n3%Bi2vvKGyPWJ+NgobtbZz0V%8u_l=`%V_=e`hXAtlA}X@) z-t`XL9ekH}Zvw}h&ujxp>l)1c5Ex1#j-_CPzF>S>KciZ2_5UGu%e6&*dvp!@n{{Y6 ztdHLQcvkClTAeao{<B zt@O~WCcFHQ)agRtzHA%u_xkHl%i->M55WYrHXb|15c4#yq#q+2y!r2@N-}ficyks) z-#K|Z$TLo`_z<{3bC~8$NHB5t@(o?i+jzBx3&??BsW3bmLiBGmYYG1Fmr4E}Ey_^- zH9j$P21kP=f4i|tz1OB?|7|5$V~TJe-jovR%hxHv^T&m5-?RFCdV1%qB?P{&XpymR zq%DhtHO9jpu`vFk(ac4bG9g67q%A`7%ePNz`=q`+YI|za@s6~lHHWit>RdqQguiQJ zLKZL_QACk6U2=#6!1QD=Dk;XhGhEkJjxD6Z8W||9n>*q`Ze%iXD%i?dE_wiO`&N}c zJ3+VyJ6|s-&!VWpk0usOR=C5BI;u0wa17`zYN#!&0 zNv1?)fMt|o&(Ms=lZtc#4f(;3Z~8ytCZ1iB+zl)UeQIi*t$026(nDpSjZtfz8kar} zaxph#AS1g+=I!3}9qlYx%%m4UTd7s2kPOA0kQ%MuGI& zOKOjzT_e<29pU{?VJ~^%jqx_-NwqWqwm|9%{qlu==U!Aag`*d3+QqIQO-#N+cgZ?Z zA!K~VF!Xv75+S-pheG@$Q%k%;Ol4+KcYL~0Svqt{>lfBle{WwJ!Cf)YF>-3Av_#0m z&LAR9P{?T${JoM#?;U=wDuM^5)xbNU*~qshIM(LCsrJVdl-HQp^Pssr;c4yZ$!S*VQy#R4LY;YmI=jcos)z-KK=xWJsVzyUb=in8y^$C$=L8K zY?D!}m8Qvu{Oo!N^YA|wOOAo#fys@s@PSEHh~%};;%5G+Vt82;{yq0u9Vv7Kev@Y+ zyfKgt?QU$;aWtyJYASapjQ9i@-cdqWeKVXuQFN|RRaWN3omb5Ift_fE|4rOPELtLkz{dao?b$a}3 zY@MYnr-TB%hoC%dHrszul@^2Mgz#2`6I)(9vct>Tz;Iq~M=t6kw6eIS=<^w*C!QqL z?w*ACNe?+b{=>y|EWLLu;4;G`0fh!*>uGHtP+EWqh;57$8&3DU6lE;f9124XGwKZ! zlKcpw{;>=-;uAd9El0^HI~{;OGytqOuycS)TJX1*bahhE*e@eTzIvFpnln* z|H1x>ePe&TJ!}0GiV5uPbqiXhSe(q2^G}?>Hn{VI&$&E+mdnT>6+=B1jWU#`mA_cE zX;cV1!d2886uH$_{e`9lxd-dw_EW=7^yw$|Srdk5l%1P`TDjWr0}rGJ3HS9M3bIXj z(o1l&w(X}0+dX;m!eQY@k}?fmkO-j;{{WFL=eEq42oZppw6j3X63$ebZeR1;TS&S? zVG-$Zo2o-pzC+nbXvym%>Nt8=s0R;29AZ%gbfj9h7KKE5A9zJLnk5zHpve34=U@9n zAXS{)AqNY`7cv?QK)%iD7ts+%-lOo->kZS#n-0h_=Aucx$BNfz817xTa5)9pwD6Kv zHS^jV;@B8qz2u=#vq`wz#Gg2X%i8z_)@f4Bo-6e~qaAtoAM~6wzTrP0@dQSn-aXI!GlJ zDhty{p2O16LWDcmz85T|umTOm4MEdbo?SAUlqC!ya{o8{>I3!AGRlaL+T=ga^bj`8&zj05EeWtnOSXWC zlTtB=aa=V%zvo+Yz>MHzcjo}Fb!$Ch+AEPM&J)&<*>_(q|KQ?wIyl_Q`P4?O(u(9{ zw(~9YW~N$&kFR%1CkPbOW9dm+a%r(@=7+uYj0O;_NZ#Dm-(_J+)k{JanxH7y;{N|@ z0Y=;Fd@QIXPhTI|{8Qd3pQ(FNH~~4h$#~!-aU+?UdNr_Q||J=x&lWs4`|==3PU*+I|=Je7cFUtDl43knb75>^Dmvar4i-psJ&e?ab`t& z$^)j(Jhb9731gmb?6XeKWzUZKh-ftzt6n%8?`a+|r>v}^n|I~prVW@ZUnHy^le=gI zGBf*P;@&x_NJ2CjPD+O+)+QDLlls!xoLs}Sh{whxt%Ay%Z5jan9Dsvb(Ba2sX0y=u zW9#4#%?k+LrFTE~b+`h@?Zm)>((| zJ8EKAM(!|AHwE(_$=m11+}a!)2XYD|T=_^}aEj`dAKPDvVLLzh31r02V?Y1d!4eMy z2plShj~D%g1p0Jm4zZm&$JsH}pZV!djZP<;lnmLa_w+(}#O~4>UBue%AxXPEcTSS4hh)Y0WY}&RBD1P%io>2Qyb+Ek3t*Uz*fHGS z_aC1kN#(H_MZplZ`|lN!k{CYG<}<)|EQgr_Hmw+y6Rv(Wylyc~kk$({s}wg|v~yq7 zekk%%VEraLCg1K%(5Y1w`YT4H?Yg;wE1$sRx8OtO^7&>fF%$zfrP*_;JTvFYQrrH% zBY)kC#U0X(Nzbx1*qlJ{rc|5Ch=2sRQX=`e;P%i;>63zYA;RH3yAK2v zC2rOT#>Ay^@GW{l6B*X%VBS(YL(_}B_K3P86P0s*iNHI=HeQ%lh3aq100^6j%Hyx3 zivLa1BAo2}_alPI5-F0n;3nAK)Z}q+qqTt=Wm4Yr21v{;&!Js!^ zlGqo~B&0;HVd@GDih(hpO>x~%q3pLcP({~JXlbPSy>*FPB%haHk)38v27SRV=3JaH z=l)@noH*83!}63pU%SK2@2t+72v_}1x45$*kKdLl=;1z@v7Hofofp((EUJ#TcZEue z;5X)ATlY5CoghXx3vc)2@(5d&L^DPS)+B#$JeisMEpn z1D_I~NKCLq-r{2?Oeh@Yz=rjkeYXQU?Ix4gy_P(oUGH?=wQfGU^*#E@w-ne#`MY=M zjKAL-G8d$k{^16zw3B|ZH{vl{p@wa`hI-ii#Oxif+|T%}beg;3FQUzUPK@rl&%bmx zbBodr{`GQG3F7ng-{Ruv_p58;3wAc~ofhC8vW10eKDjx)9I`1aJ2)vMJE%p2hiXrD z{tn(=v>_3X(LAar5sZ-9&ct6dpAz^r#+GC4!6Ij&7$|81)B$aGbGdOfsu;WRJv_R1zV+2a>ud%hK=#Y<(qUu;w*2E{%j}Yr^K+a6L%~+s=s)aRAuz>kN`xk7b~j^?N*MK-!Pd zl$hMtQ9M`M?JdYAI9zY#J?lhU%^zHLM)EG<=2n~kUqvp*{TdQF?@#CSD&%ndDd0M1 zF_+Gy3lOi(NU?}QT7O5sJAHQuAg$3)a->KmjZ=<_KdJNt5xpC%_X^=*We>SKs(@km zjZnnJa`@!BpBJYWVw@(N^LRr=oLqAH4E^iSqLhBGN6iOzboP9`^qY{K z5@T3_X2+qJrETtChfe~)7x{bxVrH6!oWS#*DBdwRqFJbis3Gx7N)DRz{K z;qX3_h=XQY@n(9HgDPsyt$sOWG{S0f>4csTgYx;k(WDx7BuN5L$&4%UFds z4Rx{)#I95kG~a&djca2I5X!K9f<}%HY1HuDKbKH`+QodHj=-}=t=K8<8d2&1MusOc`ql#GQJWX7V(-?+ptOT@%G<|b~uB!#+z@Gn`gz4Qp7=3Wh!{+%7 zVZ@DQUnAn`zs=K3C2w>?w8qvZGqbD0jsg>)6rrd9W`vP z7~wJ{H**uZlxQ`X2Xol=HAmcJ+!*-eM+hoKl@ABpeLs?IrOBz}NAG4UZ}M~H#b1S* zkz1u#+N9$DR?H|9s#S|&(|LMk?-DcAy z*9wm2*fLu;(Tb2xv85ep4J!5JezfQF>eDQF9;kbFrF^=Cn&YK-RatE#C;8erI=W;= z6DqZuTbiJy3!G<;1a8npzK(q9(S|q&7-jiujCczu9loX`T>DXh(|RSjxDoZQ`|8=w z4(xLgTK6am;6g!9UW&}nKNiS)@DJqG@7VuC^hIhU(qfw7XCUiLl$gHaqauZ)g3XX| zp)daJ<8Y&|qc$aFy60^&l+V1j^eX3zyl{&#=C%~`XGVJg>S?~ERMl)Kb1$1NU=Z~K z0o`%#>hoEJbA!X~!_a}1v?Gj>`3Frv@mweqCOc6$=QPU%YS(%cvl{S;A-^pxe!)EC zs-N|?OUz+VnwMZM-Aj%gme0Hy8`OzI-J#-}rwg=QJP?+s;B?!eOW&9OW~$T~Ff6WA ze`s26@WCLJzXPvi~$3)9(1 z&8?5d93dY9>+Y+$!J1t(@L#Na-EB^Uwo;$;#gW3WY&|qCMeN~fhZrH+wf!elY^&MD zYzEz6-=+RXLZdf2+X3q zOE&J%_kHc+@`ERIVrSq-?4raCHm6GLFm3;n7d=aPx1=!4HzDIf^Kg^(NoY}pEH?3U zRL)e7Kk!Aju$7_CgOM^t=G7B@BbliULh?e3zsWgPjeqw<*CiGdwow~w)9wHbP(C-f zSW53#^~{7lPLVforBubme5OdC4(mU88u#rDHLJX`m&$jH)_Dv@`!vRbR-rmKdgzhdEcweV%0J#1o}AvN`v^=$ojSHslj18+4P_=O{U?Ijb< zxvG3yf+Uh)R0;98jw(!}lfH~ah?2=^;*G`jo=%v~(tVq5`n||!u zbu+EH<{=6#O+Au(|-TF%@g@=zGPQdu}&L9S?5Ki`S<$4*RCv9@hlS;R566eR@t!KE(D-y zaAv(YMC~H*R%Kblc8CK+bx`uPCgm<8vhRC)qmaDlC?%2kTcP*_c=#BH(D}P zB}jg_HwtIksy~}tdl4n9zu^#sI^e3>47PcIQVT3z>e`8-yBkdpf6%h;UejqKl3s~F zR&vyK=@ypkRi{kyw*17Zf$6jxWHMnq_b5v7#W(YQnZ#|&&W?MqF!JIm|1d-Gj zZ)L6)EO&tf_o1MC+y88YcPjlHq$fbaC29M!X>QHrl$87m^OtsaR-*kLBGKwA2-I}Q z78sn(wH0pt#5HiG3n9l4FY4U_$amW1>W^+V4;8fL51%@5JIRYz$b<~HUotLx+s-UB zKd|^Zps}(&kyVl6qxv`&@WayjKgZw*d>exo|0>qr?`sA3J{5siXN0t854W@V>U4Qn zHe~Ac-ti0~TQz0%r{N_C$M+~YKqjrUNO$>0tiFX>Q7qN0f-6a<3x1Erc;?vo8Pj&N z2|JDHxpN+&ITOtN@7Q!C5YwkUVg5^P@e8;^4%~gnXO4)k5#MSM+bUWMG6>v@Lcbh0ET~to{$a?^(hs|y#YBr6ci($6o7B0)q#Oh zSDhZJXoLy+mjql0_*ZNzlMC&^JD>d-utTx$)coL&^0kEFlJsdAnu}|)bhf@UTaQ@* zA1E{?Ua~hBtLi$QTFclzlTDu9)J&_tlvW?|ZQY`J`|*?dkmR_TH5LBC$E2DNnU z!$4A27B5w;6|C{1thLdW^_NAHuG2Zf|9pIV91-=N2_R})(?S~Nc3S}i&vE!dETU3Z zlN)i{A9;a5-~gIzFzsXJouhX<7m`IpVnW;FFG8cC&XYSf@FS_d@~FLvpj*$IgLpN^ zUb(imiFl9M1}nm=*3p7~Aezk) zI#t>DXEsJcJUl*BRt)KYAZl<{h1{R%B)OO*@4)o15bVNen-yd+17EWhuv^5P+ zs{_!Qa1lOx@pRBkyt>aqSfhT>9XPm~2B@z)Z%r*(F816N1nwA2P_4(IMG5uOCejwY z-n|NM$*m}+Yw+3c^uWLxV$Kv!*?or@zXNS$3@wCtX-t)DPMoWncG_SK^T$WtXfLNf zk{1+NCUgsvWu5{)j*7(X`ZYOj(iJd-2XBsf+B;|QOMqkuVeQ{(KKCX)Q36eJl_t_) z*8F^_-3k<9x^N`*h)%>FE`7;+ts8TNLWl;xm7u8{{|5)$rlSVy*&-OD=WLo319Azc z0gv4e!`e3eM0oLP5O#?9!ejbjNbm-#l{a)5dfS%3zBzH)lo<^>@x>%g()ixlypIZG zxs&iuTZ7W#AKueAep`>4G8&}Oiuj}$fe_nSF!-y`rIbIAKr!v!B<%S%ycaz7>x-d> z^S)d`7cOJ7NCS!3Ln9y3U+&?`Did6u%}0LR+P7i~y0Vv#>g-Kb0wMwYnO+^IOS!|Z z`KOZ0d3i_>UjmoobBw#XT-&Kn3RCtRU#Yjc?H8ovJxv?fDhh%}5aG1K7_Js_pMrXs z^`_H2jZ}x>6+lwkN`g)*y3d=sD~Oh!l3=Xn}-)U=4vfkSHX(ALKt15_>RD|^%zVIn}_ z0sP2Pbi~a{j(vm^d`6v)A5XR`5F>1)(Xs`tRV_`o8V)}%Ubci-1{73OYUyr1@{W8S zP@G^qthM1Ed!YwTGQ$(POhUvMqv>~RIzZudOPk5_cxf?~Xf5u$h+ie-0?Nbf;FohR zdNzGL8~PZ0yOH+rmVNxjYuCt-Cye(aNQuD@&X+!t#4YEJo-h40IAeeA2I$HXmY4n* zzl3D-QUi2t89Ll~&Q6`e0(Q!@u3!u2UOqS#V6HZ|~KuxPu04 zJatYUbO7Sa5FXavq6|RRX%F+)ijrVQ669MdZBx8uT@urZ~ol$!KK1 z@`GD8ZZ@Poa)dNjB(uWPwVPh5q@y;M-Y$OQmNsHZFMHkz)!sTlK&bo|{I5!wzcTpl zf=C%psHZYV-*_zH@ofKX??`~^k$79IW?%l1g;=I(>)`Ne$**Mhu(3Z}*d_qm$xe~X zr_G05ek3S8w6e%gVH{+;5bgd^7(*YDdhwF|abvKEL%%@EvM^Ai5mT23yXzDp9D`!* z`2~G*JzK*-)b_{v-cl`Wy0(A4wHuSkw{`co=xv+qxyi}dtL}82K*u+I8eC9Cqz{_H z9@Y2CLE&l}@mOisjXYGoI6)s5W{R-&aBP9Orgd7*pZC$JQIi!?Wb8O{_Wi=@>S zRoXg+f4|%Sde}4~_BM?AZGp^B+HUwNS@TYLE^$Acruhnh-`+j{*Kz)y!99m<4`vu|bIPCyOk= zdP&^&0d)UH+*}E?53|NNETq)4F|Fw*V^~&*NujR2rh-0>1x$vz^K5K!-0LcxHb=_y z|2a9fouLWZ5sy|^cI*P7i>q_r;3C6I1+Fc7ZN4uE#OUTUd(;}LlyUqKyCFC9@G7aL zJwDV^ohn=oi>{~lo@DHCKEuA*vfgMN7NNrljXpzD5WB8Mv0l~re*L)a2Q{Ok>fxWh z#9&G;?&Dmy>O6VHcuz&*5=&AgEx9Sbhd%Lll)vzO!H#G$pseMiNFf|`AuvBWVXtYK z`AL!?3lqXm6^>%ev~9k~0<9b!=)mC7>sz@TdcbY>d!9fSY6J3`)qRwa8GRXU$B8)y zUm})i@&2@5MOpPAa}0_E{a;39?Fo!GA{NL#wU=~eb|pfCs$3VZhH@}yimo;;P_a*X z^66|XWk6GIRcSh1&_0CrNj%kU9Uhuz{PWO!0um3M-EF5o*lnL#xwE@F!tLn3B2SBb znIx&Sx_KRTGs4J;RUFq8tJd+NW#Bns)G`MDO+k!5_7U~JEyFMmngYh9B`VQ za6it`OEHECkf2h(>ZI+CRn&RvSgU=x+@S#?Oez$@%vRi`hWjA&;08<=aGrxZSis1NH_Z-+y)xVE5DdG8?L9R)LYEZB(t6~F1p`7p|R!HTrmZ69v> zUSYN*$}5EsB4ON_AWRKjRtwgTbv>t0*Wr?IB#|BbyN7b-g~XJH`j&7On>7P6tupCp z0-unQ@-a2hch8r*ERPlAnOP|3tx|aPn93hHaFXVum_r|Ef*1Zacym5W#v##cp`E{h z+?Yu}dC#GPy3xS?`tuGf=>jy|fDTw-kQjNMk@y^KMk+v_Qb}Z?EbPXohn0#7RhL$cI#{WQ6DuHC4c3;d6b$;LTeSfFT9l=qDhh-yLdB@9tOY;Bj8p zJ5a!mgj<

P+cIU`B1AI`$Az5G4HdKou_RTOvw_~2s&MA#SmNOfxn#MX(}xF)f@#u{ zJp9s7c@=b|@=<$+6eSot2x|~{(A4KkmKvr^f)Npsyf24yJjc5w$4`oSONBI)w^#Wh zzEi#&nsU3Ar=!B?y|R^UyHJjeqt9@4sGFPBN+|Y3~v&c zC=|a?Z28cP;fe6&`@SE2sHp%~G;Q{uEnDZbH0acPdDknAU(-gn0kAUC2|1FueLaRP zlX;SoMk_I5UB`-*?OVWD9PoDh_fU~QJnJ&^QR`WVk{?(QM%-nrv2R8zmC=FP3q-~z zCHrH)pgv)R57_tO2d@;kejZ|k2dD~SObY6mZ`qS>i(osjsg zzB)(M3DIOsNFFxFtH&&l!GBHvY0;O_=ed}=s`&R~^rvpc@y*w7TI7b=Y<;1&kDzg1 zciaDNz61i@{)=6jH>^2!1|!0S!%zPZ-bx2?C<*4bn>?7&d|s!PRe=oLThF{l0!&9$g z&(fIu+Ytu1cti4A^p8RkALA+#%u`qC%Q$pw&p%zv)xXQ!3riqT&|-j^d12^BxhB3oeJF7+#Ns(ph;+9Z zhM@H{4G=JvK^AS`^4P;C1Dk!QfE;zWCX}Vpv*RCa!~A6oLOozMG1Ib)EXQaf*fA&4 z%AriA4*M;?eoaE_f;Wb)zq#MgCl0e>MX$?}HvK>x;dEEDqll{`W`f3-ax2?bLSU7A zMJHQ!mMb`=JvgqO_Ixzv3q3wJyNsTaf6raL!dZKEla*qnk3MmxzW}wq5fpt9it>6< zFF4K$^c~{gj+T_!C*M|bpHqZzJyqzRs+rsM%M07b8cu0$l z?8*lW*f?vF?`Z0uOukV!INlV4^dJ8rg{&EMK*J@-XE9)o#jt2h$;97eUEuDZSb{gUbU>`$Ui*kv$-5>m#V0R*4*(UWUar&;1WkAo)nks?SW zsC(fx!{Cncng4$+fVm!+Un@dVR}qnZAx<99R6up3;iyF_D|s|z{t$SGoiqxu&Pa1S z;P8QN-Dg>nj9d5ZrPFLQ>-h9Qt>SX(V+;W|)6kw2@s52vAqtf6B*W>|B(@(J(<&b| zJGb1fxMXH3H{#-b>jLvosf`a4Rx^)E9sTY!P4(K^$@;YkRFPDHA#lnZ?kSiBQ|B$; zpY8BPj;e7c-S1qGgN;mVX6;!@Tj9UA9&5DD$<4F#Oh6klqo)0gXM#4rx%(Hiw`bMM zoY|x%exaPHJCPi)iPU~*OhbEKd$i1|_qQ?c){%XPX67a?Md8{-WMDE#5<3Ycwn>#mY3ez&|@1;kENe!|qXVuzrusk<-9hlmt zcBCG|mgVYhLKS)?Qo@S)N8eHGW>H-6Tr-}84)ai>cHN|-tKGWIMD^!7Dag1ONcz1= z@J?Aww1#6F-keENz0al!A${__9wHF>5cuZ<6`P^x$4^un5EEx zk!825bA^dYR}3l6$Cggw?m=qIAlbtBR*T!&dr;+KB|C_`7)|uG5Iaa6=R!o3UPqkw z{KP-teR{Oe=XG+bI39elq}9UJ z^RBjo75z^GkKc#pC4<^pr@LhT#{r3&+dKa~>*+$Fx?xW_jm2kKp-D?jqk2$Vh!!R7 z#`I6{8ux^mUGo<&JB?rmZ>eMXln^Tj@67z&6#Q*?wh3(@yZgiD=ulVpu6(9!Xs|Ra zdm-QTAP>2H0Op4U*;Cue+? z{f}nR6IJTnZFeM~yFvd&)J{1)HKK%_4Jmsh>x`3Fp~$l!KGUh!ObcFCbCt$rtukML zI5eFFc-f9Jg=X`zabHqVuiD;qEPN6g`R#jK#5Ti0uXBODV}^|>DF&PMk$^i#Cg@zX zU;GrM*iB)*!3_FKx!+NWz^}^6S9-1`@7p8Kf(P-$J!AR-JYDTC@P`)v2}@Bp)Zb+8 z(kos04=L6KT$Pz24SZsEA+0V!hV0<=JI9ueN;e?BLh4E?$bR^}kT^%R=jgZ<>ftmH z6EEI00WoDe8n6l$Wf_scH(T3HX5}*Vznhd~#}5<;y!>#1NV5^}?-KzhP9}6+jC;^h zh4P6@*Sob(1@K#MkI{frotmCr?`q*BxpnpQ05ueqcO><}0F zKwQlXBvwj7!cCzk$H#XsQj8o?)e&1|nFOCMSKtE@%6_=-ZT6EpxxX0>jTpJZWUW3W z0SB7HH0`89Tp7kW@mB_Hz87WLXnhW)e)_|_GyK%PQ%h8Sl+Hw>Q>q3J4|k&b^`%<7;F&h?gzJcNDkKTK>IK38zio=np5k62pdS+!!?qAzqV0 zcmskd`{15Vbm}&-cN7yPKrCSA({Y!H7{P}dP<72`%CNd@udS&em{bPsxdXAj-eLNO zys1Xvd9V%|=0#vP6xA-fIC$E4fEE6Js_^x6td%ZOySEO1v3%~QS;tcR&7;tTsT^(` zOV>`PgE#c&U+J^arI`tvi)Y+?EbsUzzXAfH4pcGfIjkV8msaThevT@G5dqBOGFwk2 zhgqcPw(hB~L)}4{fLq^FpvRMXofA}(6&`**PdGRGM#6{s?-eitRndvFE%u@Dohvu` zek>Al?a$%n7nHgnp#FZiqpoxui#sRf9?#2SwG`!x*8K5YF#W9i9(85##~PKh?tBdg zW?#{AK}Em$=F$IG)O!a~{l5R>_wyXbp2;SJsH~8cag;(bl8iD=W|Am-KP4K3s3=0& zdu6X9Wh7gavK84g+xgvky+7aIKXvMy=l#6z>%Q*CbzP6^y6?N%`TO_Ii`Gwiz%`cJN=I3mx1#=TL^(&lzp28ZMZ}X>Ik6TlTN;Z-5yP8QJ zL_Qhv7@Bh+pW^cg_T=xoGaJ1}a^i~@VlBOuDK9B=CV%eX&9ALR=0{Rc?h?k9{x!h_U%+W^UPKuKh?G|Yo|>eV?(i6Q zF01ZNI5!FQ*Y7Sf@BWHR8Z8f8)!7}YUo|F}z6WlxmoLdtF=8eOU3bppCM#1``wF|5 zvy~}t?iE~w8z~y@sqc=qDo(A{QUWSH<8oAWQ>b=UbJRNLlX2G5MX-H_~- zJdVx=XH$%_?p>Y*3E@uj)5@f0Z1|`{l!KVaoT;T$wsd6eU7gJdKI_MQhtb*dJcv(( zN;U&!4>s#OdbiS259=j$WOrTh{#H#=FMRn?%db$wQ-` zTJbqUY9+spJ=s|UoO=@1!c^$zA>l{|r=TiTlzKONz95HE^3|-~Va!pVfI7w&((B@= z#8rxbwKi&t-1QB4Qm^ygeeO&?uQM&P@Z68;j`ltHOmBNR?89vIv2uq25ZF-s~IDeL|)AvR_aQh4Npw?Xf6-MiLk-W8I zgGCMlzBmy-{!rClcIn|*m3Y7kVvb|?5Z`e}uYk>(FP!A(K6I#LFuLppzLdwIyvo*B zNg1sq>bAa^!d+nCvAq=@!g2K3EtxZ!U12J-AmD7BeDLE?elWmr9-E~~oi{Ofl`gBS!&dXiB;4!g45sa%Kil?3x zJ|N!ik9B!*@tMUXk?vdWg|!1I1}B2-!$+H`vIpp9Ce zm%_UdeD%cm@kUtld=!YPqKDm^!%3%;iHv9~sxCNY=`&Su$F-!g{pOTJupxe9VPsvY z?}HK1hB|rYcMd2HBU8^plUez`@)Vw-i=u`5o)|6JzS11NXY`z;p>7u4zzaO+JMpLY zOn)e;S5HLucd*T!Y&EuIEl~(lqO~J!($3RwE$(a}Q9jN6<q?~*tLHfTpFfXX{jr7ZL0?o=IC?s5$A z8RX20eI|D1_gP+M319#5mLwV-0APGsbNFE^Y2+yM>b+eU7a2!~&tjH;b=DRw+|)%A ze`BT}WPy72LLZ<0+8-K|zcY9{Z-t;fM26tn*l)im4h70{YhJjhW;QdhvitgH0p5S0!0E?e=`o{q*Y|no}O65$x<0FjUtYI7K+nz-El&2rQByM-qX^cj);XZ33 z`0~k-v^DM41&4y$UB70|TRHE!-%kNb{%XU^5GSy~VD5-Tz0gt*cdcANy-&~t^S$bw zQ@fqkj8JTTA2OA@SU|+CzwV8n}L(DN~WMSj|fsC0f7eRxiQG$IA1_FXfFhLDC8<(GZu4PGNrFX>&B8S;!M z*mN>-W-OV2UbU?g(~tS)!8OA|y;Q_3D+jZDzriBNvI!H|cRH%wgmFy&-JMp;=DT^w zfDqeV1l+QxOE^SY>aY>BnQP4>o1EKM$YOxCgTMpWnafl90eQf5K>flO#e-6 z2uqW_ho@}g5z@}7MPC_eq8Lnh$zf}4_ARndmpn$R+MF3x8Hy(cNW|1zk0Z{wHRmBx zo89<>U;YSFgBZ0D9nC#+2b4@Lu9ce4><20M#~IE$sq=r4JSWEc3*`Ykqwi1D znb^~TZ3H~m{&WnTdd_zHHt)KSqhixDj7dV#vr>mCr}x}Sfm*r`y+B<<#dF)44_702 ziMK=L_BRS6Rzsn`(GDY*-}tA@>64_!>rG|-W=?C)o-_n?O5IM*#|b`>Tb) z_q~1|7AfjAF3>)VG!3LykX?NIJ(up*^^p#hQ&GaiS1q!w?!cNGteu-z$)ZpG>fz#Z zp@g3O;lWyLi+3x=j0!_z+L-tkyG|!VW5A9^RP-m%Xzo`SKQ|J zMt<^i^q19K81dG*#e+Igt>|vQ>uwSsS_~*PJ2BHfF{y|KTVK2U#wjeQWTDc;>Jo|T z+>Yg=yZuOwhnz+o{qK#N+azaxXpLU!-$HfCswM+L#|N{wa@mfk}Ee` z=k&~33gSi!+YJ|Mn1yT)A>+p_RhC{n}rz&rsyL9+aZjmZS@vLB0=DWXd9o-5T4voPvmMKFib5t&l_ z=fmTSWcxPxfyeBhd6qu>N~3$EoF+cpd|5CZk>>$l5WE&fpBzukI7@pa{n;UKVVxu$ ziZGz6Cu5&9?naAUVN(g{c-^OgaosI|2yOsLc;6^N3_sP9;EC-~USU~&{ZMLR!(F=k zsqR}q>L~3}>$u8*b}I7z^kq)IBWu+bBLXh2_c6_0$LL<@_!Pc=e-@8ygJj+2^R~e? zZA!2}x~Th!8rY3n8GjRerMP@Q(*T@Z%Dt=VCsp>ho-siYZJglLot?bpf9Oa-AvyP} zU6L5wB2>=cbBlU7pmRXbg`4SnNH4V`+x@x7Brjr8H;NZSxB})=VcHo1`NLq=Dqux zuG19Xsn>6V*CnaCa^E)CH|85a=9p_a9x%oUb-Z7sfDvd_$1T#1TBRX(erI1Z)R7Y@ zCv6!aS%!t?J{=?qx4q>loCv8TO+Lprue+EzJ_u5MH89`|qDu4ljFYS=609f>p9D5! z{yc`J-`s`DHpJ^42NhCrC)*BLCs{Qk!le3Zm4^G4L3gNXJWJnzgng1mr)kfo{z`o) zM6l+X*_kjFi>JZR!97Kb#L*k}@vwY*fka6m2B~nvhtkhqCy#aIFiJ9~B2{zf6(z7H zLo9?I7;Vwu{@S~zcB?Diy-&GrQL4{_sKk%dLkxAu=-!deXDn!*`L?}}slnBjYjHw} zjjMtxK2;40dufy)gMM_nBD7brUEP6E<=PGZ$WZXp3K|=p%r&30$2{gKn%7P6i^i03 zoq`^_<~+#cD@8$AJaz9R+gBGmM)t~{$5~hr8?k$;n`-Oa?M4rMW|AOe_kDs{E_UD$ zU)3~Cmi%{N17p8YU+MXTe2CK%o@SuJIrDBDayYq?EjJ|+`Z#ZtGazu1#UAp5-XLoC z8~0?rt|7@)zoh$Hg8fuoCP#;Y$LxS$!?IP<;d6Yj5SX)|A~$xo0B?Zk&a&N<`H?DD zlZ!bh!tigs7-D9$Zwwo^DO*Sz7wTMWnA>+&fYQXkeldg8kF$uiSI5=(5tsW7e}{j0 zY6JZo%c#9s7=cKA{Je8_oL+!LvU+1H)tiIG2i>5$ByIZH+-mTh5Sh2s5#Z+%^50wu zca}@EmnHR3a3fG+K2T!Hd>h~@$k15UdwUJlH&_!DJ%T$FkAp3Lz;}j-U?c!sNlcHDnAuV&z z`6GrRVcgB1yn|md*ZE-ZzV&4jW1~w_L#B6KT0XjGATSehJHkZ7)H&JbV%f$0xUQ88 z5(OOz5p&s46FlZIMElYGh_k>-_)G>!q%?os)^`VXy9H{T|C30h z?(~`IlL8=|k9g|!+oTxX6Ztvx#6xQvcghsGIRaA5P8gK8xM>sK83#L2{axgM`PE6> z$*<;C=9D}T#$Jf+FUq;@F8Ur+hr#IP5906;0S`Y8iG#@!<11wNmqBSCn<&qK!zXB0 zU(aS^O+>y;UT%#KKdRIP^CBrMF0pU>TzWv6&~ypoL7sb{b*gFlS6~Q4i>Yw_t7DE{ zrC6B3Jp}bnNF?^jX^@)Nh@_mK%{~mW-#Dhsk4-+9vn@nMdT;VsNQM@f8S@Z%E_9x; z?>ps1hUS9X5v_+dhs{DSJ&17Bnd?1sp#%6O`SB}>LZf6lwu4AElPv5$ylpth`S*rU z+jfuLePg!f!9vED^ftbM`;A3Z0Z#zISJ$+n)b~;ntw}{k;%=TGC)`Yf z+9H<{hS^31fa;O;$^u6zK6gy=tdCdGg$~~&%w}9wh1rRVGuuPKoZQVqZ(>)^z@;C! z8*Upeo&QfwZiL(z`vN1jea~#@VYt0LYp(f5$%DlLBoL~q13xI74gldqkMH^S9(6tZWpFpfZ)L3TlKH0B3k!;+W$Av~`2RJEsbo&R7SAmK zvJbM(thJ?i^s1J7h@bODke5Yzd6;=A2D&p$L7xIoi#+ReP%L zzomZ?-WymQTXTm2mK}4z6(nw$NyG5ym>AG!Phlfo>Nu+Bb|yEP;s;pfJFknqJuiI> zYw?Uas`FY4v(~rnR-8TGmmTO!$I(2|7iY-+A#r6c@ae9qE!@O%@W?FCyq+IC(Yl05{!7BvFqN=J0~h#?t30sqp)>ax z`vqwAvp{LdF>%iK9aU<_(H+_?lIg1#IbH}c zyeFQY*I*vU5gkZ_->u!jblxo+%o03n)VlTr4jtU5022MC*3N+~b#auFGDPHZWvi{I zjjx*`CD`~qy-(s@rk-d@BpI{Y@}L|fM@h>6wV6LVo~yQsN&ei~i1uPC8rV#OG?GK!Rv`~-k)tW z3r&pYW#!P05*A7{e=pB6*7-T@sJ6J~0X9fKkGH62Nca;dAs5N_$EZrRdU8sV79#x4 zS9s=dJ$W!(TWdi-sl7fdVc0b|iHC@^%QYG6tb z75+H1!fc-xBx3j!KHr6nQxp9!kVq3#xkRt4UrCCvD&0xUZ$XsEE`X1@e0*W*;6 z3gu)B%;F^DGbqaaFZ?*#Lj@WcZ{9nG6_+9hFXT+3m(nQlqpwKe#U8FZl+h;6(dl4P z3gfE&PAKgog`UZLpz%wRG2ttc(6>BQ|Fd}`DY5BJ;O<^5#Q6sN|0H!J(WMC!xo`aV zG$YrOtG*u|h}j(yn=lX!i@&@oyxRUt8mW0_=C( zcp&~LDQWTp-zLyPI1rN*nE~&gg2o1>W*WlUjffN=xxK0kB7K#b<7hQ^Zi|NfzM)FX zN-g_c(7~~wi6h4E)l49d4TgBx#f=63hzK4%Vo17Vw_{0tFoHv{*5c+{3xSvo&QMoh znF<_SUW%9BiYn$OcGrR?NybN<@;@RT2A?a&6b?2IMO*^19_%5gbv723a2vCeUtoW_ z%OFWf>_0M;tSKDoX(wgV_NydJwqLfD98aHrnRBWSNl1d6)}tQIot@lkk&6~@Tc0&N zIKWY|KMY}uIJ~RFNyh8lnJp&w&5U8BjA~1G!Smi)$dYT+7HPSR|E*^Z-Y_O66z*y! zNBevV%gzgn_%!e~?=5~J&)HiX;uCXw1I5%g3&MAu@i2VoeGL5&hc`x&FG_F@7H3$v zw~~|$`ZZKBOZ<;w5aW|;W-H$ggLg>Ol($Kmo>UFED}RW|e4W+do2t_J=C@z3PS@9a$GXG{>q#1FULcfj-OY`AiCPY`E z_0MTN@`QHNmcjR(zNF~{v-gyv2Ml~o95CR-Ukub|!bzK+TzeG9&-$b)@nK}*ov42D zijIG_>}@tS^J_=f?dbVHIan+B2eP*RMsjL7Qc7}A7wC@tJZ7CLN=_lzWP?At%-(nq z;RM>ouWyB)AQj-@?y56_RfnnT3!_VRMt#BQT1afQ^H<_5hHMII>wfx&o_dw`+Qe35prULfG z!cp*RwD2xG?8=IbPK0b3e!TEjJln#zrE|^x_P4*GXb5=-%a$~4}W$a z*}64!#d47HsWbU^vzzI zDcArCbcK}~o!29?>Cu#9HIsCJ^97c0=)kr#2E2a4^$k)gE9@N(lqj6ME8$SDj~5L& z8Cn2t2GAj58g7CBFLK!wxE?3#l2+_KL~0{^%pm=pG-JfX*m4OMES37W@~CqFS*m49 z>K!GAG}WK;AyrGM7d9~LA=LP*0G?-*%8aJm%hG%rs*1ZS#n*hp`LH|mLnZYDHQCx_ zJ^oN@9USRJ=YM0bVVaCRZ)rz26 zuNDtcw1}7&(B1F}=l%Yq0wpfQQ84)Ve698S0vD;#@$h*)B0s@3_ViJ2v+6)1N%YL~ zknp#Z-&@okb(}zN4>~W%@?V9jtmT7q%x_Vcy}>D^%SK>=`nix*7L8Y;^t~L zR~~h^QcLd-@WFuR%98_v*Q$PJpQ*NIZ-orq-=0@Cs`C=12Eb5ux_EtDh#J97lf9d$ z5Gjb6WzGgi7`#mlqr`$VzETx_h(|Z)q1^vc*O7}x&L}}E6?EWzKc%+4ygtb--c2)F zY=`H@p_hS3gE3IK@64mb*f}tLX1+}XxZ>iK3~x>))5nUZ)n6B9X}xpt-2F>L4|X(OEp!8TG#E>?1-UJY*XOy^?xe6(}b#pApM=)>SUW4JxbPvR`OCi;bfAP8qbt-+RD)e4XV1eIXFQ5%hVJ?_7ey4`O#7Ba?LPqcwQ$L98f+L(a=iRZ&nR%}82nHMyK~fGBbsFR zi=#^ql!zkhWaXKA5+5mN4vY&UWWW#lHa6C}*^a{e+jZYurDc6;Jgfu3;>l%?s)%$iyA! z_>f5oH1z3<6%Vfh6KOE*Jx-6dO)5LYAV0*R|7Px};>aI6mZtTu-edX}S$;auKj18! zm378VUtJ|FwI`3RrW3qd2K)_Stlz{)UVh-vo`R7!_9M^2{@7JUFeqLQT^jtxF)vQ} zZaSKKoUqEWZq>qu%q>va9P!$YR@&3GI?IB#oA*a$Y{n}MO^VhtK@#K_zoq>OiQF6- zz+?!F_k8G^)3koVEl_r%YPvk!NwT9%{#25FNHS)K@n+Z5nP%RiCQg zT99T_>5+m-R7bvS-Ps!1CPwJcewC^O`k2ja9Yf2Yd;E>nbDR^lnLYNJ6L;?M~gZo;Bh94aAa zf<^NWTD^{0B_*frqxBRv|33wA4MkqoPp8dK5|oi zCQxc3kj@9XdB-&>4R7C{!4V~~;}yug$v`^`FF|Sm*Pd3bS`uv>ubF7;AChHWp`S&* zHUb`f{BqaK(D8le%G|rBr>nNdF`LKSmj&OMod|$83~&|Baol*?aJZ2XV9_`|C1qs+ zU*rp8#x|maA-X-{dVvAq4pO5Z&_0Th zqqUm=bHj}xi~}c0SXv2iu`OfPXLI@w`P7~>4qp9%w)o3c?8OfFDUi$2UO*%PJ6ZCz zlt_oPY*6yiO>MsBsa;Gup76dp5PDE_Krr@ItYgY6+Llw`nz=Q{p!rDz@<`W#rW94$ zFIU(4rmfmyy^w_PG8PwcxiWh6 z*$IHOIH2vyK1Jha-=683Dq*dR8J~V1BwdAcNC0{_ z0Z7&W`D&L#2;nfebL#$y`~&i}TY0N@2EbwrcZ(a7?wh0x7)AYT4jiA%eeAS7Jt~Vj zUt!~7|LFmrN0YBUdOo5e|8DWaaWYi#H!EoV-H%&}^RrS6AfK@zQa3V5rqTZw;t%{; z44IVxhRM)B-6$io$SVxx^)G7n@5uw-5Qf`Dcci1vQBL*JjnygPZR0R5ifo24q&1tq04SS1QsGm zHL=c~AdctGmk9Lu79RVp^LfECNWHIvORTT6nr^<|L$~Wl7k}~?e2du)v-qHfJV@4) zQ?tx`U=C%&*UJD1qp{PJY=i`AqFyFV+w9tyQrIOO#Q)%6K6)c^`U$ov9?~6`$R{~4cehE#C*|ycK5tYH$e(@dGYG(YMR6mG zOCvC;6@2*sF?pZ|cwE5(z)CUYE+<~IYMcu#r!l?OaDo_5oKoZ<2pA5D{qXYaILkPvd@+hEq~zpB_^rURJffyy{>g&vtU z0(Ot7iLhW_@v;-~>-;{V%RkCm%NX^9t>(n$V<9*Lcew(>#__pMyjBH1K{LlI>DCX& z*az4UBu_zL!fQZ4ma+hq;rN##?!5GIxFq7$|5izU(H9@`a~ENi$;$>I)KkM35ukWn z!NZd@#uh23FtumY4x0@v9H>X&>s}6Zf*@x4A#wRNJo-!{$j&_>}{R>8@Em$k}o&Dt{$Sr@= z@&tuL_N;!?`BFH**b5S$e^a(8mHLZw1zkE(#8%aEyBM=J5ya0rGw0NQY8<5=Qk>~Q zB>^HZg<29GF%^S{0r3@Wg>;q9W}ACINPgT-a)cg^^wO#phyQJYWEKj6+p*Z(@G_Le*{micF{kP{-Q*!g9j$!{c{yx&i zvJ8ECmJ6@Q<;j+MnG!pRqp;)x1Pj8P5Y%nm{XoyNXTTx$EgNEhi1Y9qg7=sFoHQbr zPdJ(y)bR8G!L(r12ep5TNDHsrsHv6f<^j}h=lBwREl(uInz}p5y~W~hJXckPK?6G@ zL{YUvTzo#r9Per^kf$b+`#-+~KZ#d+`|+v3InpfIH}gSZm_KuO>vv)63Rp zs*rrZJ*zFytp6aNc7tdOGmg zH9HNu)Sx}~a8Xa7jSDT!yDkkfX?_`Wwp9u0fGE;%?VJ4j_QBz* z_2(8|{dN16SskCKQ2yq2G+#U*X?kNPksO~9J>0&2q2%hC>(7>+$zTxyFSSuF@G1-v}=mmyv0$(?I{(w{|8 zA+>*RHSx@K2pm3zUGi%58{Xs7juHM%F@lQfyp|fKttfi4Xh&?053Ak}RO{wz-c}vO z|M$?35(+T89a!D_HQ%3sm@5b9zuzHOYV(vHorwvtBn^zNB>FiDPHCu(Jdx)nj4=Vp z&#%L;43^e<4g92^U#PY#uvgwwovh&K8eyDH(a5Jke`g_Gw%)PGzDjUgGFo{75LrxL zyV)xJs53t~JpA{wwCB_e88J!$kXfFw1#{gVPBvmhwbMQ<5ApiHySW&8+WaQLr(;SuhUvJx-~GO@&`}lhV)wiBR$#n zE0bJ8jyp4O7{gNp+pq3OR}BTqU|SP5j!7p4aZ`c{-9lC$ALLTcZFe^tlDpXxOA=cg zmdHGDUPJgze*auhCTuuY{QMKEu6n}PEh!9$!XjYc)DQ*V8$E~S;t6@9%#~ac!z(Ot z=CxDOu-!V|ehzWPOp9i)fnE|G$~Ro5BJpb<4I#s0RIy(+B1KDAuV+uhnKRa#gJA9G z_wCR6w$DulAg1M+`$=dcJ(cdzJpSkC^Xb7s(1 zTz7P)lo_iFp5K0}tEUqq8NGJr<*|Kj@{2bMnU*p_dwA^~tf;d<{(sNUZ0zK_&JQ?n zCuUlcKAfCbtA{GIH1;4KiJgQw)7=Xb_AN2Rp}r5X#?u8eD!*JrIZ>E)Y0 z1Nj<&DBp0+WW@8$2(Lr$z-rwbGD`9CL+R(?7ND;x-#yk{w7$15w^?jvV)|P2Gr2~x zj0xu7Bda}FGTc2$?*u`l|)B#<5q(7UoWb2$AUuI6LD=gU3Q zpZLQIUq)Bn6#MYCu7&O1klnT;el6wc++hNsf(f|fsY!q<_SNrd!OYW_8NZxfWsn}q zVfTL+R0nz~t&n~CoRU@rD2cR;KvJvl@S6=x3n`_hYWB672)rS

cPap-->&iRIdu-e)kS^YS%)>RSkb{y@Wsh(V_ij2sYOn1?N112MjAy zuRes2^62uFjrxTs{h^nEzKrFyh=F&P)<5N|x+jrYd0ovBm8cwXv=F`=6x&Yt{pu>) zcS#Qt-lhXdBd^2f(#m$?nJq0u3l~6+Vj1Z8>W=R_N!!Kc@sCeel)SsMO`+WD>koG1 zPKiD((&dr2PHB_9b8cgg z8K#qrTNsS^DOG#iBQiWDSZ?!G)a)#Cg*sze!f&r%E6n~%`6kM|*Ss*`_wAnX_0>ep zO<#7dEfp12D{#4lefaRHPecFx*B6RDcr&cXIj1bm2j-5B1cin6z-}-TZv!$eBrpFa za1!A2iLq~uXuoV866BAo^@crcDW2H{iDf2lE2ETh%$o5hKOpQkeDFZW+hvAUZ!oC% zeL;JX_hsW;Hwb>x*jpHd1DD+4Tk9d6E01=Rn)5X}7*g4>7VppxHUm;eaR;AAMMW+D z+8h=0IT0gdlzQJbk`7CUF5=C+W_oTp7E7XEOu=J_jN11*pYb{!+s_=`Jjp)qXAGd0 zzB*C}N&k5Qhf(alqm7It@*C=siB&hb**oa?L%zrbDfSc?UT6&PeDY1j%);VX2A0id z&-kPXqd(`Rol5!)h+Hn%q%^q`}-gM z7`yU(n`29(mL?gIpRY(HvwpH8%%8ryW^w7CYYUEr+1t0It4HhwW79E^(vt1o^<&_0 z;3PZho!sVbN^M2t<>Xj`JJ)&P+#Mr4_Pc~{Ko%p%%>GG@8U&cHL6M<>b6mS3|8rBQ zEq=3}56x1DU1~j%r3!1KOMzi}kTA{v);LH+TgZp;q}$c#)p;GN`mN9|wC-Z_PEmyg z8Q&i9n)_VAw+EFVc*Kvmc`pZ-$*-J@GXi;IFH)t*NWO4-h`zWQ*Xn~{=d$NzY$iX% zfpofo6CQX=8OY${08~AM^x`2bABeIvI7c{swo_d~kxHIg3lcf#1)a&ierOhhQgW}q zzu%_1=C%_DlAD|BvfSxvGi7_Ke>-Ch0}_aC;#IiL zm8j%(Hf4R-mLwIDr4aJ~cF&cWmgqv0Bpuf$r0%`pu=CnUa#E>5xADy?Vrja1@c}$9 zm;JD?m=f4T|jxj*o*<(dfNN-lMu(Nn@l5%cq@QP8~B`Vf{iZ7~rcA#=(= zgbaIi!k54+>%g!9)w`6>06ksliCMXbSXmyz(GR}DR$Lg*P}E$Zd+S^;c^c+ zulM_fx^hM!;NC%ZSa%;@O?h$6ff-+9=v=L*tE&s*ghonkasScU?(XO;EG%HYiq%V{ zXB!oAUFlYJa5!0ALtR4x{?1Mv6J3MFD>?W7w|Gjl8xF{!(NKmh4JMYd*V;GFr5wgv zrWi(v`_NLVq-{sheee1#pUi6#eF9kr-J_-a$% zkEdQrc&(c@-8n11;;bY);k7xd*ckS``$3(o`g1J2HtETW%d)O z#88ug>E}f-U@DhaiG8peSmqFY#bX=g z*OjLeEbR=gX&K!#F?B^dKKAqtxI-^X^ZNeyEltiydUo&}&Pj4WM9^H%_BO58!6KT7 z==oVx`H6ofVAm`voe`z+g=1VL2KCdi2pc5f;^_KdTHr~`>5iO_J{zR+t>E1g_`>$7 zR|nt0o@WcK^jW$3A`p^5Bq7FVCTS*nxEls0I`r15y3{+i8Bk1ySq90v*p{opJIV$B z(?+I{#e=1%7wx?MeLjCVHW!MUayEr0bB5hDM$z z$#al*G8jFxwCM;4D7530a$L6ln2WhxVB@o>u{F0P!6elAl4@}p3U}o!Q|1-n`ngjM zOM|I1UKe~vH+zI#X8e-JzeanlDxm4FX3o^`i~iLQ&%Iy!1>|Jc&!-$Hun1LJ97%%Y zvm%s}curS1QtmFn{=TbW6yFu~nPL66W(4LzkBsx^cx+VEVp6~op4V1>9@+&2DX`%S ze;mE^k!hPUN~>+YWaQz;AVcz<2;omd3pcyQ<~BCi(l^V>B&|w4Z9ab+FI;3t`tt@i z*D+2C0*+xmNJRU@-0petc``ondw22Z@^V+tMzMqJ)!mue-*(k57YC+$`9XHS$Q0r2 zn34&Qo&dicIop)^2(0@BD@Bg9w6t|Tr*=aK;@o&e$>S1h;@F=08YTaY4adY(HOgA2 zx%Q)@TVvsO4i5MU38cfPDCt=J#<{kfTAnuv0*=uEYyZ}%Bkew|ep2(|UrkR#Kj4Pc z8^r`JM{pc=@%rJgbg+Q9dEqWFbX+X2IcrJN8N50Z~7dkb!YY$AKV!xMt7>KI(kqZ z>reMq`t%Je>3F1A#@n6^cP=KOr&CF&dFs^(GXJJq;TX|$^S(X=yOZB?Vh;YYA z#!|q~#MtPKT(gl5)YwIU7=I(VM0Ua*l3WdswauK_)gvXm$G|k#TDRI5pks)3!;sE9 z5zFwHeLDEVAoMG#fL>30UhpLiv937wFtdsodHyGKH;LO3OcjUU~-mEYdXVjZM`va_(Cpy!)* z<^eU~-8#mFQ)V%wsIVVKwj3y5WH6HH+z~WwQ@%%XnUDx2t3G-pDbsAEP&xIiaZWC8 z6jaopz5#C{-Ij=MvoPT?8_6IG=Ae;9rjMbS*D?5-Cg(4pbg7Ao5Q&&AWlP|={cQ&V z?&rNqr20nG5NwNxyrFB zBdKl5;YJz60>yU-ouo^zx8LrT`E!y*rL-bd+=JCt#214nR-L9){2R%HPjXpudx#^u z&|N^zOt4dJq|*df@oiNuW{pvU=2WPKeT5^QKqkR#OCY1?{`^y3cQAygs6;j1*m!sl zh&s(q)-(pFj`BU`2Mvzx-=oJCg47lQZ!83Cvo)!+M-{U*Z9AjIXa+F1PQ|xQX$q=o zfJ(1mjH@6K_h+I2P#TpZkw|VIs52rDEri&h-ikuuTQ@x> z6tR~hSlv~FOSZ$Q`8!Xj8s=gS+#24c-?7&aRU|~6qh~}YnnG&o69%XD6!bIb<)>UXi2 z+WCg6#GH@?*dh_+#_CRJrm@Ny0Rc)kKb+P(iqpFFG+#pFtM;T%LzD*BO?xiQ3@A|= z)!D{C%EE^aZ6-Yjjo?EQp%T2`u%2KwK2+%>rJ~|A_4A_(^^=7PzsTvCqsO9 zyqWXiQ>P(QuIG2?0<}HYeSb) z@I#6tQOti#(G;bOiLV+a6z$t2BI^H)0&LtwEmEnwF|Hl(fIp7MyqvUZ~ z-HgRqy!4#Mn#}vJ7%D@MLE-9RQU^C!2M=3roIeY?U_hlQH5yY1|5X_9KeuZgHVLOF z4=&Sbpf{FCUC!l;a?)Ol$`aPr`dop!pgteSxFgLE>~aR~9{f&oe&Ue&tXTQXL43Z| z0HRkT2ok$faS+>KcWP&oz8=J*LjO-#_mV5o05!3l6{ts3PUTkLbnMkxU~MD)#}+x7!V zr+E4PLUhl)+3x1+^P-+1!q%*o#r;10 zAO#qofWSvE&9Lm1Qv_L)Fk!t@TN}J_q&gIHH~dgyJ0p_Awau%r?cyhcquU3!P5v=n z^xR~+m;x4%Kj0>i4s}aZ1xlr;nCJ%yoDHgb)?u=H{3GrJQx0F6!wYP5q~U3s*>G0rc;kAK+bx7^78)D)6ldlOW(*!qD}-~bgpDJ`(B~fs@f$vk6S~w%6Te zt?%WiX#f$kl*s*iP@lZxt9X2D#h|&2swZQ1U3xF_>@nY` zZpTdJ4h*OoG`{nH*d`%F|5XZ3hzWFZZ>StW~psfB)_gJBh?~^8iMc_vnYMD|`0nXqaq4n4V5t zt=T8invfM?o5@GcnMhcxvnTlrE?G0HmR$@!FumbBI;u)87j=N2Dj}egXsn%Fsn)%bkQl9kKtWjnbw& z4TllrB#pDSNRvF!U4juP2Y;6r-4%-Uf;FLc)y*jmGM4%dAHe8k zNMwwB7IIfP_h)(ystG&puoIW;3{syg4+G)L)V+#2RamiEA|HrT>#F%;6U;{_X zlj6$ZO?U7cgUnnZVdc^Ybc@P2uM4Yt_Y)_DFiegG1uO6VSXZB+@1}74>*!ku7KyUx zj!SQ60z(=7FSF9FQF}HFJWo2S?R9qc&w}P~Z=po?<;g2OYS<7t2b9o&kC1+$R&CV} zWiparrUg$lKNH1lk*i<^W4p3>XWci3?~soP8@AVN?d+zL`;9ydeLUcf!Kh^-F!wXYaf*tKR#V-Dv@7 zEAe=k^x`2}pl=(3*gowndO#JQC|0bp!xpj?Ev~Jj1NLeSDh6@-a95(9btHf{Elg3& z-$~iUWV-{4744e}MV|#pv^>o4I!DdNP}+o+hVDH>C9D`S7>{%y6;geA-`Is=EUbB3 z2il_0*j|&$)y+2|P0eS<5A@tZ`rJ$QaeShb?$wRe)z#cBWM9?t4l5CNkDD&qBhMtU z)Uvvy)TULn7^qk{_X1l13jO#G-H9Y_m;c3jY!J) z#DnWNcSA6fZ+ndQi9vS3`Cr(m)N4#TQx?Cl59b@Y%Xs?X-itO*Z*9{0_@I%H5KI)F^DPK(?&cs1SmPCQ!TiE)C>M*E3!{q4TbGf-F=k~7=0d3X7Rdng%pBNE<+TB>!Ekf+v;`L*FhQL@pt-o*N9lBD(d`g_I zO9xuguA`sv`;RQfIpQ0jFFMaOcyfC*ILrD{EKs4E7`hDCf8Sm9VON|nJ+SoaX>CYh ze3V#LgQW1ztpRQ2@87@EAa)jB=pdFo=~Mrz*c0!$2d;{`8YB?=_HRnn@2#>-hv2rm z6CV68H{F+RZXdCS(~v~e31_8uJ3t_`q_Mz9LIFx76)kO7p46r7P2a>VqE|X~w$YOj zP~De)QX{Uup}=ZX*3dBBZ~gfVqy=qp;ncWrPWgHXr!@(UtOK_NsCU~)W2HCijXfsR zvD;Q3CY=#nSdUQCiMvyMQ}MBGL$v-L#j!E+svcDFLHWE%t#hB76 z4ByY)-JdXULo@lv z-E@<`e-FQ@xoVZcF}@w)Wl0)}aCGa2k0#7)K>d7Eri4Ma6x8)0QrE~xZdA>pD8E>k z@}fU`yFqZw?NM$PK(;|}p}Bd-t#>qBUGdSW*F3>Rfg=p|+;1xZ5p%-|Cw@;W>XbP) zjW}M|XoxZIi8=MTNU~nHI$L*UWvI#{g@bMI2yRUD(3uKT3Z!Bj&z3f9bA&9;qZ)Pf zeCl{x1HEjhfSXa`*0GGLwKD+!Zw}Jqm_erR*nsgWs0r_c%7 zRgA)mv>)C&j31fumA)8AQXyWjgH9*rH5XfzXn&v;PPlB!7RGxA@km`29Z0hd=mUL9 zEo3TQ@usgQpiwXSC!mE_+#s5HDJlb|!`K_T#IjxUZq!0%bl3sxgYv!Eh<-)f(#kmWGI~f6^_|pdpZfiz=r8CX znP|3Na6y0N3<5%Q&ris3TU)-RrbIZ^`F=k0fgT*2V!X;HlLA@0zI#dgS7_-*r7O$& zl;71+A#iJ)J)DCf3js0r1LBd7wlTE{y2PI$tlXKff8U`%lcLBtJ-#oy8lTUTemzjZm?=@E{YB>!xRJwF%2v;@eMg$R9f`2ZthM1gT)Ep(4bY zNM!XG!*7`s;lh?BTLseZ2L=YXtKOZ*n|8V^!tVysuo6vBMK?9+Pg4GkhLV_z{-&#e zH>1))vWWbEOOi69HSZPQ(iudcBkZCPT0q$%d@!_r{5ARe^x=Nss!4Y|8p$tZM`CYL zT>&G403|;B`|CWGi&YnZ(a^LV{$|m)hsGZ_bJrF1RX8Z-bMMZF#sCp*hhLg~uPsct z$Z$W6*W8_XB3JA<7LISk+$9V_nKiMj&Kv6Tiy(%Y38Y(lFpuXTSwc+SzJ(O2@7B^U^M&9R0#p4NEdmmvmDc6_xVx+Jw+^~(00@h{ z*--Ga{L9*!Jke6`zsS3rfM$dV!S3lxPsh9qT84D@ynE;Gf@#If+4kUG{D)zuZeKXt z&|81nU)QYUJn#@K=VBabA+hD6dKur~pOA6qpWjr*iyPzcIj0}OYlZftB4T07MtyLS zjT{kQRv4EtaZq&PHx+g_s5n1AgWlOK%Gayx0B+B?{TAy54uJY%Lc6dAL!P6Uk%-!( ziM@YKSBzC9h7Ji+RRs1#u0R$`me<1dJfq^dPjk%b>Mh69#vmcp$Q!i?RZ@u2Mx}UT z{Fw#R#M&A`^icXZ0U;}%`f`snwXZ-l1%OtMb_5PQx0zWnHPaaxXu5lQImLvg-P?$cKH| zU@p~)I41?~WaCdAQ$D2P$PCpc22t*Qa2KB*QU}QJ2>$|V(9^_Ia5Q*Y3hm36B+%zx zA)h(fz>^$@E(JryO}J}b7f#&gKs)jAd(bZ#u;x1Cq1v=@i@KIT3aqHTEY}UcLI0VH z*sA4e`~+47S$q!32we2uoASKgOG<$E>+rm3>DwhgSogANot&(DvNA#VUOFhNko0r= zfk8`ww`d)|5xTOo5&F|o%{Z0-$(P5u5!I(2Hf2(jZ+#HD%2({9IE25v56B#U7E?OY zK6|k8UrmAfP-idp_@XsGq-GkCV!EgPEI{&!q$HW%+1P{;2EX@Y-mcK0$&!>_aoSqM zIV%$J6!k4vA(eCc)Q4czjry#2icr2(mC4C00E58mFqATcoHh6d=pNgGQIGNVDAJ%H z;(TD|(%bF5O({EMp7cW5{pNxnFrJ*}eXT?gPCE?K05yd2 zJ-@oDr`^An<-LsXSbils=aG;;WoPDZPTx6h9HsL`M2^H{|3$9mzsKz{KiTyHNP21H z=8g{+*-@H0+~0<(^+Szps^N2A;f_la-8Lf0yWpR@a!hMAhDf)*w9`mfpwyz?djlr2 zO9h-;78di~^>O|b7c>`ahFp zMiL1-xJx(t0v6h42sOI<4)6wG5Xx>TX|DTv>iwfcKY{g)2Yr|+Q=-!SZz^Xf4jQ-= z#@BW*BWE#Oy@K4@FOW|^uUaSh*8!kG_HkJXx5d)}uBeAlZX{*@o-R{d)4aH!gztcM zgN5~hPXqtS{pBx;_)>lgfCas%wGS(LWV&Z{F+Qn9BI|<;bD^v^u_7ZZ2(~Vpbc0RmM#v> zH0&faPdr%;&2&osx6e{JmD#~s--UHt zbOG$&K7cN0Hd=VQh!|Mo7Z=CGQmpx8BR%{cnp0q0Tm1Km@9R25W3BD#t%NdZYe&v= zYrB!f#sG@hS`O;Cn8SpnAa-{F8D(ecj_^=5@VqvVxq)w2t(WdlIMz zz{7}eUn{oW%w0agy6t<0o3KI(37_pml5S%)2DTrevDwfXO1dVm4pj!=p(|6LFdq}H zM+H%ZSn^fXcs9~Y8t8Ry^irvq;qJQl6zco_t$p*L#wU%dGI~$n>No$_QKoQ%AsXmAbRMiMi4*Z zEd_X|sih@3I92VB8HK-r%3m#9R9rlztdYbgCPrnq{KJAP!a;=gisN2NrfLy@Zg>MQ zTm+&t*i+le&6l$tCB7YY^R~2A4{FKX9RHk;3bgKiUNlz~D0gIVm+#G7{b~J8;DO8< zm`?t%DcpE1w_(k|76OcP0LIf8Jyxw{op17+qEqCdN75Az7Yaj$-7NpJYb@!gDk#6M zbQku$0%nXyi&U0rXPn9tC8fn5z9dEvGynSAbDq+mCb>O`n81gh3ELRnkiaj-AFc`{ z4}^)yyYvXPZrzoaAHEoUxB!@&iPDAEtE&c=k~w+MwG%P&ERE#!*ZEy4{qTG)ub_}+ zwALA5L~Ek{AG=IirtN^6eU3bDmWz8S{=v{$h{i^Y)S2^r^T^?InFYlI4sSnA`&0*4I@a^vz|+(*7r$gQZ2DZ}K`#AxPO|2!PpgO5b2@KS=|1dU zRH+w{=Izn=A{rF2$3@3~Z&s!9yH2-X$Bs(9_iWyt>P1x~{m{%`+#0h7Ahy8xgs9}| z$1WRb+SZ0HAE&~rUG3>tI1u3?1fOi(?yfuP{76XWfQ$=+%S%L5B=lXchuuTCaK0vyXNzwPD5fRGEisXl3+>%u z68C@<@r;Su}lcZsDQ;eSONrPpj^~D-)US8M1@LjCHI#?kioWBejAMv+FLcqQf zok3Rey%)7jHWGKS4Gn*r;~rn4WL}|QKv0uUQ$(cB=Fe{BF5c2dXlJh=lwucbFoBCJ zb{MgxmdLIFBM-IMgta;MbkU?0WvlD@{gU#^3(kFLNa|p-XMKF;UnA6sa1HXG+;rsQ> z5y~fS7L*P6NXKaTr}$BB=_iojRLnqvan1Z0{x_K0v|?9}+w=+7jGD4rdMl+|j`!d> zT#7Fqdv1F8zmaA{J((E@(j#_G0z10UzHkcxeeJ)7cj5#Tm23xZ-3W(*>KzczzI^J@ zLp$*E?F=wSQFg^-6>ZfZ_fQOw1rDRwQ>|U{D-s5QjJu}RxpkZn4bjwqxk=){y!Gv> zwm$*vdAUA%Y2Lh}S6AXZ26Z;2yJn}VBDCJxt)6L+9TIUmZ5OYvs(0H-=`bAIhuW8%%V%X^`bbf`R6qZOFBVvls<^v~)?cVfa`1454XxRQdoavEF^F%EL<2*E zi(Crrz=t|&KhVh7yAi)?PyFSCVPG5YWgm*ZWU#w=$}U5@P#kG-E?&uD55tBSjsn5> z<_i>(9Nd$`OdYlfl&J(%Nr4urrz?{3W#JeQEwD_H!p+an-LGTLrmf)_MiB7mjTbqZ znXg_jJ>Ra@^H7Jkm{L(O(CjI5{A7hgUU*5RTcFsFJ0tFHC5Z&)bC`NIX`(&F%J&T= zn?+{!@=^-2*Wc(HcT8?jg&*58?H0zZ;|UTXubZa-a&+{$UH^@r_;eY^N*a{OiY%_V z=uN6MUi%P_P$=>3M5{-TAs6&h87V0!vB3o|61P0@=AeueBIaU3Q`&rHXUuWPj}O~) z$E-KN1meL<1BN~e+z1OOzu|}6o^*S-BKGvI2zZI_isAP0_`0f43h!U4zpLifk&@D-^&wh6PIJEm$3J zgqXOyJLn78mO>bG-B1>epS;Hdi~|&usZC(;muT-%j~eF)Mfy{EE#Kby6uN(&S>{+V z5RHoRL-7gxQUBveZaFJO6_(k=+j_UQJ-ZZqF<}eX`;(i5!UK=Tgy?@{hc|WKz3!zH z(~s*O{xW1$K}ApB6^up1eA=QCW@_T+Kxe+P0z{R-ZBSl~%Q~}M+eH9lO|@DX>Pyaz z&~A)Ys*g$>0|Gi{V6vaw#y8RNfj0ALz|0SFzY+bZ+)oDdqDWGR+UX!TavLr8t3P# zYIgjC4gNW5vO{srYq?D_Ib#MosHGRv%WCy_^6qJUcHHNecIk6=A7K7SwV8c=N`GWt z12g8c{Kn;A!_wDW<4LmbQ_Bu>o?*9r>QL}e`q!YyE6DTtFG9NI74F4y=+4w__lm4e zubK8F=ia{;nETmi4w?kw`g_k130^%0GR%AKh?WZe1)Pn^=0&?!F_RUOe zVU_YU!!_skx~?JF1EtPIrZz5<-`L`ae%Lk3^}6^|OHpf7jlBx1dHVTK9kbIsQ{xW@ z)9|eA{0T%wRrNkaWN5&jG!=H&u6RLibD#Yln!wfRBoTTrJXvhJiu0ajqRajTxw>%gW0iRM}#29_7J& zgTRVFo)P+r20K~}sR+$%l-?O*qQB50?@^9fekti~S&euzaSRc7YOV$(OkFWmYRlD0 z2DhWqqysM3b!TJ1@4W9S^wW}s|B2@*H?d~er5V-7BlPq! z!f%ilNCw?J#|S+vN?My4UvwXS?eJ5(>i0ojiZl|Qh#pc`6?N5>v(yek$UC%9 zn#~YYp9VmciN9J=Hj>{6tM-^M8s8ja-hb@kdl+vnA-PRy9H6g6BWZ!`(WFYi_WLo? zXe`=KOQjeM&E3pkq;v>&Ct_ncx1^$e6G-%CUOQW4)KRj9{u%Z}tRG`YNhETwps}V8 z$yhxYn?OftLoZbh12zaCLBLt*yLx)k)$xJIn;ilm={$8rdq(hw^lfoZ!*@+@c@(U} zJ){QR-m&%9#jH5$R&e{M8jil?By+hsdGNVk3jo=aV7IB!)<$5~9{-K~c%}y}3nS)* zYCj~;D^Y=p1uL;9OhzxNWAkY|YTfA2n{0aO^Zx2xl0hKmG2KR=UOMhS4K>g}C92fJ3UxLc)iKxj1U`y9P|}gG@x^%K+Bm|_jK0I^9L7QVC+{1N zT4Rp*7|V^J^zmCyeC$_AD>5>3{Xc!ieE-#&g|8pYpUGyr(%Mtq&b6QgiuQ%tyoA;7)vow0|Cq0R9%Xyz7>g=@s zF?3%NZ4#r3a8V;J?nd{;k4hVCITH+nzDtZe9pH_C1VX5`I_9MD-l=VO9HQln8uO&R zeQlh!FSxH6x8fe%-QO00#|Kp}9jLk9%;mC-!ykZw2d$4sd9vd*$w|DFv{_YGN`DLZ`R*-XZEAXji{NbZM^04aN!TCc~wWatG{_wMdVwe?^mrUYo_4` zyyj2wuYn^5h-fXooq+H*Fts_`|ZKb$lFO9XL@><-X>Ztx`~( z08vSYouskmu7~$Q9?j?kFP4K32UsMTU4AKWUO~*_KEViunXXVfPS8L(?2lc`Ujna* zXsZl!{Q2HF)d#=_S{anDZ(myYxNxeYzqVSi4jFUBPlBxh0j5o zK`>ap*gc1}QR{Y2q>E4q%W)U4d?KUX!{ufegZ6TJk4kn|zC_1f>$B31ZS@F;qqxrr zRd-J}KRQ-kNdiI}7%Kp~!lTqBSiC%gnc=+X_{?C&0%~*L|d!1wWQvw=P&=>UD`L zn(m(BDh6^pT(xnEzomT+QKLFF&vg^zQ>FZI|7jyTJ}Cc0ZUp_WDBOB^0RH67X`d^0 zXgSc3)tWz~Yc5^fU;h=tl+u4HxmvpMx+1|5Yv}P0-k~6h3B;e`L`j=Fhg6*;ca`88 z-$U&bAp#)a-e~xriikKN-TvL=>rwV4=vU7vr`q2RB1Nolylm#-htn%l6eatXMz}j} za1RU}S$Q7EZ}78IrBfg7DhKfnj?+ay!sUy5+k*OrmE9^6!aygdI%?7$G9_GmDA>r? zXz1R^t};sIC+%n0grxZca1uU>wo|n{f{yUYfOUUOnzz;uMFY;*_gCnsrv_(dE&?!w zW;n1_M#vp0of=YfoY`UeS zJCzoYZV(g+K}3{pk?!v9PDv3EX^^f>r=&{5rbW6NzQ^~R^R2~C)?#LudG5H1W5dI6 z5c5h$l5kRPihq39X>9Aiz10W)(u!AZbFfzxzd$wqI`P-uerv!Q8^yz3UnuthBO|Iw zPn%w`Ixeki@e+|y>k}KMl`&^(3h0lBQB#c8aOI+1k;LP&h9V1!T#K=%(VzS$-?tP4 zlINsF{^S-gXa~JP{>;bseE3aXw1d`dQnbZfeJr^PdvKo8oCgbSUl%tb(0{X>+jEhZ zlv}2NEJDEck>xO>llKm+OKq-uk;d&LLhU4C&Hq=~sfpUdaL+?$fqsO9nSAj#*nk2I z+J1NT)Tf0Mzh!uhCg1xLq|@kusw#Pk0}+Hlt~@WhiN3miY(~0q77vk;_!-rQMs~OUv`s{WdJKXC9tx?PK!Yt9 zxXPK;gq!92d*tt(Sme)0Ee>nPl4MrljB~xE4T)E~?$-2g(}$~$j(-h}Yz-lh&3hG8 z2$LW7v*{k&bt)Bg-Sf(dxmU$<(rJEm9$h=8JfDFsnH*By2jPBr`soAzrw>0ZzfNs? zqk1fwxBi93_P)n6SY4X5Gf7$NTMe(T?kGc5318e846j2@R`N^&J0*+&_qJ~7Ny?tvA z)F$-+NsU(Xp!3sG->|AzuhCjA+ISiuk?~0Co628@!E^9)jc*>n1e9g4_=@>GC@P^>|bQEnXxl3tXU;+``BhhOW154jepzghj~pS0jQ zUS0jSy0QDW%Sn2(JID5>Sr5(uLo@Nm35~agm zNb^~|mg)d-*92h~s`{prr<8(YAD=e*p<;9l&!B<6j!!lzD+=AUW9#a{9UV%6`h}}< zCY*cghuhanKf-JPA(ZQ=%ifCAuNFcX8eqe2SQgFnkkpx4aND;|Nx9@PZkB8^gP7l)&;PUM6t-}|TJlSBC zTRB}AV2uX$!{d*zpoHb=IjL9uq?wnHNf)tSIJ%IX6r8ek=cgj8tEf1Jv>1T`NckgM zK^70%Mm;d___r~O+%!cTt}JxOzrk|oj%B53`Py#Y?l?v07sb9RPXz?9WRLLZ3j1~> z%ywNy@>V*oL?~3{Xrfe^6M2-m{!F?!ZW8V$Y*UVy<$YkcL~o_hALMo5S-)DWRmEwz z{d{_QfC*pjQmFG1Z;NGQ&Og{_KHDEnw<39+I0@ET&jdczTu)K{X}wn(^?2!lB=ypV z@O4N^TftXigjZ=p`N{Ciiy0VPni7i$e8*h|Jt!I&V%Ub9BaNg zm!EH@b5fNjQjr-G&=B|8S)X2@D(=zGMq)(_&5v2?xlX4K2r4?9lKXV5L~To~ zGQ(eC@y5MeEKH|9P0aYgLJ=L;KKGFV4{T5iw%zA&;@vDH>CWmS>#wKz$G4pD`EZEQ zBhs=Z`~V{BK|j8BHBtPD#`#Hhn6cFE&)vik&6S;kPjc@d3MUiCcS(wYVU!^YlHI?! z?oIqAmFq}ZS;AemlTSdu=5ItGE87*;hb#TIf9iaua9ac@_R+e+Zm=)xp?lq*fi8Tz zJt&A#SkRT>zV`hIXWE3V0;CnUnF-LD_xxI14ZO0ZAv7p=GOtK+X!g9%E|1;sYh%(9 zU*N4dbkfn#yMNvYA0CRKn%7skcW5ehZ@924pbG2R^$XDv2wh$#`11O3pZxUcg8x8| zsCDFEO(pYmaAeWhn-Z{$wU0}@(hV1eqXCg`&Co}^xq#aUgbTO z1`C$}Z$OyjG#8}9tBJN z6pFyH+pig|*nYCq2NYJAeq`N@VXQP_`%a53*g}}x=(gDpXfTHObi=X!b`yq9JbdScFej_hk3cDam^_c@Qirjr&XQpZrAx!ao(I+gSi?M-!<9RW@&#* z9A8oab5HyUMJh$ug?>KGcWsMNW=J6TjvUdvbj!u(ws^5D&@wRam@7<%U$Y+#a{xBq zV!S4{|4rLVAnR~g{?cF{lT5FL^x#0HU`-avg~ZfeR%{Plx&PUpvnl*ET$JzvCncnG z-L&y;rwrG}Ixrwrv4h0z`sDHMeYgcy-C(`!BX>%O;3{H<_8$F(=Gq&-8r`3}E+D)! z0M%N={>E+1((PIkdf+T(=l^*ayKQx2i^zXqz`~P(@5gBZZONxW`>=6^xoX{sGZCtX zH72?Ll&nDLBv0I;_AM(* zIQWw*)pHCAhpfXHuaN>#M}7&&9o-yk9+3v@Z{6(Xklj~)G-s01c=aa)t%rA+*SxcA z)YvvPqQ`FxFdb$7CB0EVm@pEs5QHde3~e|&TxF+WM0;=N+>^#`qU-Wq_B)_JSEf$# z|I>hkeaRWCP^3Lx)@>u6sPDWW#K3qGKeP$rlY((1Yw}JS^q<*Z5&0FAJL(CgpmnIg zq$bN$AFUoX5V?NXvOy+oU(;Is7)t!|A#VWpQ60z7!P)kAf4ai$e-i5i?P=W1J!vNd z+zBySfpygdKvMD09NyhxG|(>tUR6DG<2G&lZ)s9l32ngbDCtTKa<_rxR;ULj2Rn&( zG*ik2i$|Egf5*GB18d_E%?D`aza zI}VH(B@PnVDjvjeRR0OS_XuBJ{I%L_pNY`BEegeVsoaj$>MdgiW!Av*mT|7Q&;kI> z&wrjPs34_9){ocE$Q&QFeu2>(HVqW7L0A#yo1wwcF z>==PS9#CsQu%lQvavfD^4QB{-vzXKFHyFBo`;?zJ+)S+sVT{DLUeeiV^b<&CyB&PK zlc7Coq0&NGe9z|e!LHwHi97HADijIV96miagH@{fQf~*5a8K&>iuEl zpMM|?Ymy-&ZSOV4aYJWAPAV<=1?;_tBC=zzXSj+vZQj}KGTV@^YvEU|nf&Jopkwms zDGs2BDJh7c!S_c-e#rSm+6j)A|Hrkh1||{k{WFgk+uOv`k=z|({)E??5XX|+%?Y{I zv5zc|(Rib2pWSh}hKpLD>4U* z^SJ)E+@6qypef+TvLekA)P0#sy_87N+YM^=w(`QvCBTzfJ7oT^Nplx9J*%1g-cmhr zTuT{^cDH8f8vukM7;~-GAz&fMwWw56)4D0V$kaTR=06<<@Es5 zrd5J*W9E~v|EP5g$MY^D@f}%{J%M4?C}M8h=6G!DQXJN-zgL)mr(snZ4Zha8!CKF? zPWyhr=;?*My;T~j$N9n+;uh293N##1ozPfQv9yuy|! z`M_6{s6T8sG-%(ofF2X&EM3pdW~%k>5wXYR2rc>pno2!W5MlU@(x|U>5F- zCH*N@`vPfm!OuuK^S40i(2Fye5LGJ;2JcqXad93M`_BQtKaStLWjNxj6u+Cg&LeKM z;!dMIOKEk_N?<)JQW`u8KtXX}_eDQ_NmNwu*yu~-4#RBn|J*&~MFgML9C5t4u)jHb z>w}(?bxl=0<2S73kYvIe4iHn}4^3Q1Q3xKJ#W!#FjHg)6YB4Gj*KQPP=3SUaMZXRY zNqyz||3KS=TkmWCEs(2q96OOv(L98bZ!(Qx1$uHDa}FAy1s!f zT?xg%F3k;|=RW$Q!XQ|-r8WxTcG87D!52_SEFxCXf0?H$_F0`S*)@;U411o$vmpDEMWXbNu%r|72 zt0w{Z;k{1Vzm>i2uWf~-{e;8*dkZcs-}|ac944J!Mk`1y)~WxAq{~LdC$0EXpY?bv zt$H`z39ZLSjY6NK^6P|^7<0Dh)Y(Dx$fEQlcN&9VQOTz8waj#vO)gN70GiWsU8bkU zM+(~8KMK3y#?aqDi1>n3QMPy{FoX+3TZaC>qniB(C(7uaMKnJgq z=I^&`!mSvhjh&+eD!qrO_WFPd(nz=)JOQ=^z#vn&^n%6jpC85)qpk_*Sh>f)M!=C6 zE;z<`VVM!~PmtGs@9FDDs_zyqj)bNYf_b1aYF*T(|6A~iwY`y zRJ|sbd-o@ZGbALo%D0CUf2eUJQz8(%a>EL$ryj^JU%qVP&G_xv-{iT^rFXf(em2oM zyqMN%wNRg_zGwMYbVSHrVzWnfV|j~Rei#n-tDT_Bq&MZ&-0W~))*4f5Djbv0vt<|x zvsRz^hOf}DN?)6rMO$7cEz(OZis&YLi-Ro95FaM??BwFYEvX1Xg>s^t{B>oTS^m|( zbm#!fFI+DMz*rYIqh2)ncPreYFaKi6Z{d^j!-_^kBdoMk#qNkfyNjA{rh=STj-uSx zVH#SwIO`mhDw#9)72ZePVlw;zLPQuVZI|-nM%lwkTg5>*T1HY$6H7pDv;Thzn@Qy`2H(8k;2+ zBO8k6(S}MZ_iyb9gy~C7Ph{VM`bUhFzm0SR;VQ>-gVbFmwN_br=^=bz`c zu;h2i(h>cH5608nY3RBHe=w$PQoTYX7wj}(DV!z078V435JK1wdnGdX zbERH`$aC2rf!K=iZi6rmvSPOETlb_U$BiUsCFK5O`ACu|jibEWh9RZ3Gs| zW{%r^k_roVDGJnV4pu4ZLgFJ`^MnB~z$v<{Vt40!jUa*bV|IT_=VhbR*R_uN*G`%< z(%v<$qZ7L=Mm%=M#U>Ljdo%%@yUqP#>lan(hekX*!4LG+Z{@OQs{8jz-oMku^g59g z;f-1*ICF6U^=TlHHM7WDZEK|myTR@d=@@3W%65RU(P8~yEnM(H_W5DBU$tgM-ni8) z5z@P)+{raXsjV53ck&SrZQcp%HO%exH_Y~o?5D&l#%eKkqrLF;h}K|xxV7FEmP1GZ zNswKwH3xn6eKg~HRS)?H{gnEIvXw!;+Qpu1G1ZZehdJcRq9vCcyQrt*}pJPw4%tl zy>OP^Ob85mH6)zL{#^Ay9^FNLiA#YMY~(w;%4Xp^)S{v8Z2~a8*$Q6T@_B2(AhX{* zYpEDM=;tYXSW>su6az+I#NDli>M`2QvE)4;z(at16-BBF>*451xFq*%ed;J?i+9-{ zv187UzCrjuw*f~t1x+B_q6{A+SogWhe!VKWY^e-q*Vft@vn2UU`_0)vRfo3xBtIxz z{293QPaZv=B{%%U;APWEXm`^oIY&HlK_-TB^E2f zF`L?hgJlMQ>Yois*L_Khe~(a=PJ!smNag3H7}A4kbvB`<{QR$)-=?SM>>q+w5I&0S!p#*s@b7D9v;fGr7YysBS-6#g5;xIRp z92n@6iJ=xe#Pg(8VB*qt(!;%+UbMuF@;v#;WV4!Z7$Fh|C#R8xJNbf5pEEAa%{mkO zUB)D^du*y9seKV9PTPz*$*)%StFo>x z58mF4M-hGy@E3V3c7L~7zo#u~=qR29l0u#{CE)!zO~A3j{bL#MF6RHZ0A2n4ZR_=_ zXjeSEN_8_F zH(&+LSEh6n+Abf*~MtdQ{Do%B9b{#V)iSk0%CbpNJ>?r^G#mBq-O@00mnAzIF*d zKoko@AXDI`;jxl2EzAOj%=#ene3SP}%o~Vr$L@unb2L!4o0WvC)EoP^zsMm>qbm&8 z;0+!K2m2GWtaX7)IjPt!o30I~LtF-jF*Tv5I(P{E|6Sk?hMQ)?vdH`p+D9V2QGk)E z^i`no<+uFd^C~8Kw_@8JUBc0;#l#0{dn`?^`w}!BEhniU-er30^>h0YWBI(iybe80 z=Pt>cRmohvlFO@vZ%#8+h{RXN`2M_Mo2&kAHHe-`%j#X=E;RPES;_W=;LQDc!zMX< zrnkeEP2e9^_2D>^KUFs5zXdufbgBX{b`hib7GeC)>?Cp?kQkk`J?f0jaJixWN5IDM zlZ6cy^iJE6c2eIJi$bAAW@`ym3%rJ&1?wLj4nB4Dq>>erH<= zD4>hxmWj?6X3whhsc#6kwqD?`+k6JBVz9_S%$8eF0O$qy4*U1Nt{>zV5s6SKqTK3% z1JG$|$&Gs7cj;R8YD{y8bJkF6$=;1<=;cc?a`M3N^4tf<6$T5`+s6rqzjPDqov_DM zHYrvF8i`L2Q=)r}@|KXp(jYOCXBTRd`EHm8Q~37PIiucZj=)ZLX$aOP_BAf*=j_;S zMuWpCVTSVkl4mE>E{e%hW;uARtc~uO&N=BrD5XS{fkuptN+yr9y}j+SGDd(T@YKgP zyLLQ>G%F(j-C3I1R=u~5JS0YX> ziSFC$y&MHFhT_qP5TB}*a4V~*9Ld7QGTuC>sFHBo*mSY=WIVF6nM#zgF>+P1y1Oq#05zstN7LK3Pb0qjKUwAlp zljN4^2D09$tJI%RlV+=QE?M?I}TbUNHGUpqATH7 zQA3d+#8&OuXrt__yZ>I|_C|G*? z>^Bbiyit7>Lm$va@S}3jqg1rtH5t^$=nIJ*{;?Gx;mV#3?_y-)?t=-dJl}M!BO0xK zU-tQZ+520$Kj%au z%dd$c8Yep!o5K2~3L4n%i70fBC-q0rGlo_&Cg*Nv6i&fvZ}%88#KKytu_YXMfM6tE zm#RKPsnlD{M^o@yOn~;bp}kQimZn0?M0|Ekg!FKjv7H_MD-molx7r%EEWx16=98wj z+4N_dCeo@u_!f}dzq-E%E+j5Kep^Gw4hMfcUtDv3XeJ$b6=6P7Dm4P zvxfFOUSC^Zb}oG^>UQAlF;!87KO&aEIM5NtI#9>K+HT*tV0!B2sR?=2z0Q;8d!#Z9 z%B)$6%v}*5A(aq`L&ietm;c1sct{rxT9@EF#g{RgeVYV+5RE zMcjojokss8QU-W}udc4f$G8K_%lWqN1=SBEHrHQr<=7v$+>Tw;RafKSu8{I)+YcA; zMY4`O;c6|8J>ES0h+I`s@dy2xsLs`ab>hp;6R10lkoi!FMOG39osEq`qL+B^x3D0U ze4i^v$-wb(weei~-`0HgI1TTMjePE0z8(*~t3V$4i7$H}E>Dt)l9p@t48wnBmjP&j zG)siJNky*wKP%yniiWs8?tjO+CdNg_;#I|O}y7! zzMZ}~Tz>fxTe>CxvYcK9!)Bo=`q!^Fke^{UD5@bqc>LVf;5_i3GPtVB-$Q)LQ21YcO%67@@6p-|uLEY0;smj<5lus$xiW!r z-FAwRs5#9+X*#&tLlyGc zFH=|vFAFC3{o3IQ56tG27eH{G?YxwG4Ng~>p_3Diaen@9_1aiJe&w#8Kbe5h3(Zwp z$=h-xFDs?W+E>hUkhNR(Ozy$jP|x`&n&6Nra7+5oRA?2-3cnugf6{52)2tky0HeLP zKjlWGgi8^m62+X%!1KRkfzdxS&h;KN^Z&fiMs%~E9YL}@Xo+bJUh9OV?pL=1*lD^5 zTv~kKSn-|^(NE;mY>hwZ@t7GTSkG8B1vDF<4!P!VVHN0K3WVtt*67mD~l|XxT(&A(*S**nYY%lY`>M^1H9w9*y%|bX3 z6kGA^v_s-rg=tAW`JZA4s3-bu#KgRYv^Vj2GgVQC`lkSWOMddu zC@>hxEW;F&&L5X*U92ReUcY40$8z4q_i*pUPR3!~UVUBOAU#(aXMYh#d}v-Go(Nf8 z^EHAF;x&e_s5%Ep*FP5#Az4nG>}SPf*)10E!*d57XhJKOaGC3a(U1eegJDr3{6Y< z)}9}+)}TL!5`1mfMpSX8Z9Vs4`MmN^n|if=QoD$7Q zcI~Tm2!P-8-lnq>T)Zl>(w1?$D9uJ-i^hJ^pUL8aoFV2Q1 ze@PP7DXVn-ynfwFXGklj{cL-i7E_yTwg}O$ze-GNO~uR(Jw;rE8(ToJvtN`8)ZON^ zeYKvX`I&PB;_cmfFLz!dXrGQbC)u&W?WogEUn37n5%{Q(V@F0B3}5HQy>=1d6ozH1 z1Pq^4i^a_kzA_zRtVvG4Tk6Q##L49SaO=+ogvO~_=$+!f?MDke0}D8)(&aazV>#w% ziGIdpXVVa4oy9uoHrPr3)mPr}9ZXX*WjU1$sk~Fj$@%n&@nn5B)$&2@IebQji*_6@ z;3Z>f-Xf82EA_bJ6qbt|m1F0vR!niS$k~b>%nbuh@+m0)d?D7^ath{W^h);ynOD!s z=PMO&OqHC^l;ROY(BCELTjh2+j0v^h#SIMz-)C`8HVdRXQk%n$ywdl(Z|K(1GEKCG ztIeS6>LkfjE+O@hSKp8g;|6)3gy{+=ro4s3s7)HB6&9y6OnSZs#8Emby^@rdmoJ^v zYjS!)fvuE4f9m|xJ?Y4B!d>y~iahb^6GNn2Kaol8`=0sv(vL(Ytx7JPlhHNS2Yn?@ zk`qFy)>Lcx{J)7QJC;Z2}&xEcxeZR{>e>!pWBnIh>a3Xu2`3bA?X*9N?6@R~IY2JGD#h4}C z!esFe$~og;ht$dT8=AOowcqg+{ojXC-r+>MT@5-Q#}3P+{2kbD4&b2BR<9Fc`G5mf zWM#(fJw^n_$JjF3{a;q*h|7lM$VG(dUjCZ#)7$8EG*}wuH6e%3&u8A7O;%px?~<`% zpEJy03FBh#DbT1!P#bo%qOFxRavRQvdHfbBWDtR<`3&YdVZcayx_#+#xMB(unu=qf zBiP?h-?HG~eE1`sH^xrdRic@8XEG#-@z2`*H!VG`JPo$yYp6vP>us{2Giu;7*3g1g zG+Q!@zx}H8;(;7EKm?bH&g)E!ZqJex?nCQLi@Wab;jt<1{gB*jW+g4x9{fFE;9IHU zySfqWr*%tiME?Udp4?E!BR@T%Wya<#7@`;Mk5HKeUmAx1)&E z0fR?!XGk8uAS^%6IcJe5bU|BC-AglLh3|gNC_+QYYLSM2)8g>BhQ8gAqL%D=;$FZW z;XSg*WeZfN{<4;fD?W%OB~p|;9PF&i zs&CcykUwWC(l>SBihEpzWK9h?|4dxcHx+L3SH#El_CC|6ZE-&~8)KQj_&wAyeRP#% z*UG(popkG_%9!|qK1;DV7T+8&DS?_*txV%pxrSO;z;)3&ZZpqqwap+;1!=98Mq4VO%YB8*^%DReDB$kHmgT`qQNQl7Vjf;yx>M zKZ#;Yc=RTl=*IiD_o-diOFs)VZV?zqr{nF{aZH5I*v*gRDlMJvSI*HgcP=V^f*7VV zM}KguJxojT#rk*AIE_cHhnkz3aL}H9(`kpP^tbZ?lbX`@LkV&5=#K?#twDnh!@r(r zu1O@S6vS_j(B}*>PW}pZVxzVq3CWU3Rz&@!i28D92oT1#^fZ^mnm}@-&AzPiZ#mn< zke8PBOD!tV4rm{HI%O`J&4k4C-Wn>)4N?1nB3HUp36oj%K!`-Uu3VjNn@AWkaU|4> zR+dUaD9RE|w1@nD=lZ`GDGoD0I*rD+hxd8`4oKD2yh6aO53M;BkIuvhL4jWF#_^V| z-^rPIEhm@$Us%V{HyZkyu7CvXRBygKP;WvYM_&&_MudQEGYa!I&(74qk+g^3^j@9 z)w5cx;UA-U^+i+3Sqqmo_YYgMYj#fE)HjZ5lP9;nU)uyB z0XusQ7qn#Ap|5tTNX^6k%ie~V8|B=H=S9~RTlJ}2r<6| zP#B=!Xz;CJoJah@m4EjeN8M;lUR)rNZ0Mm+upBQM)#Y^!gZ#j^5w-Q8o406JR~}`P zB(1SdzZUibjvd++>4W#Ok*=<;;vI^fWw%08_0MNs=mb*z$pPQUBjT|IU8&;lQEY+d zR0)eAk6ij$qQ&s|EpD4uV)fpu6;23XO-}NuCHNNQ1@}b>Q9~v5);O^^FR7u~5#DyX zHvJAtrM#dLAy}m&(nb4#my!y!lLZOUBEQ#4Q)tu>Xv3P$ShE!O*u`hjCeSsGp&IrLM#2Xx-jrM3u0|R(tJDl(*(;T%kvbID;qB}#(BW)^o z4YuUjXgdytnGesJ>w|8zBg?rwkLz1ntoB@=($whc{@LDF;QACeRUcIK&_yoVIF`{j ze=;+`a`oFh>#V7G!}UrpdRa-Dyuyu{_HrTpUD^15`k|b98I7pq^@8XR&s$Hld&> zl5HE^p+Nl!rC~*j$2!FxEB*b5tjLf3&w}|HY?rs)BLt}fYE+}(y+!uE9kKB&w|&NN zKb9{jVKU<|f3QlkSTd0Jg@rxyGx4p4|S z5eOE~$xOn-!JsbG;cDCz>O_sK{G@+Jt_fSC)jA-gmlgh~SNR*=&Wr+-0+m)d^6nBp zVo(E(tlqA8vwWc8O^^Dey$F&nbVdD|u7PWqvw$Qzcb(mioUREsE$6QMk5#(ar~Kn{ zUkE1E?@3q-i)^30xVyIBA|PGyAj+*pu59=troZ)6Y$-kv&GoKaZO_`!15}> zC+BIriFwrWncCJym2|_kXiE{hV)OTMU(H?!a-6d=C_7wQ&et!o#bj#HVR=n%C35MSY%d(!S%KW=vZjBO` zSSxd0a+@7H*p9>&j7ks58{&aMN9tV_hjymAj4yKRMx7xPep!piLC`B}FD@ zIJV?-h4mt?$;8X)J7QKyF@pyno1ipZB9DG@|M|>I>0hVj|F2WCvM+tuvyy;1aJ-q9 zD!yQf{P5Y?tU~wxmB&uB2OUtWDvbPHf@+l>7z>B+Ho#t=iaN(FlGN1Hd~Ld>$z($uhq^t8 z7$$ls>8Gp9u_m?2lTP!~p5XI2j7WT7dt}?By!}~nqvyl*po=+b0=@kRFA46bCmB?M z#v;j`2N{N6CN08;A0LGV&kyGt&ckb>%YE<%B$!E@47k4(!$NF> z|Nh)q*zk&!4m$)Hya}A-n>5(dzC>EtygzFqA4~p`@!~C=ZN=*N+Aut9%un%eTueNu zI<}X`=qe~e+-Mm@&JfZ}38VT(7@HCrXQV=5ymMei0%ag*Rub;DNK8vFj+a3lDj^!D z@}rWr`6y{*r?(|4#2=&-^6B#U%fK9qUy3oSukIb%(WZ5Y(q85+i8ZphW361pEZ~cS zjgr=T;Lz<-fppLwlOyEvHlNpuX~hXD?SzJgm^nxyU%lwNr92S*Du}V01(~DSFnl=V z^t~#=`FMrHqYzZf#rgxZDC%Vsfypz2O85KntO1n$I21E?)6Y3bND1P2zxN6$ZEV9i z_}-W$Wybjy(Pbcym67x5NCLl>J0I4K(HWd`whoqyTg?<<2eXL=+bE^@79pQblM0&W z>8>~^lyotavzm1zpxJs`uBA|ET}2IFqLH-UG05jDC*^bnL=85ZQ8!9h0SpZz=&6tp zalF@-N}&+!M(u8HJ1xoqiJ`@#L7t~KlNITv>A(r+ue<=oa$triZ|^!bcw=|Aa{CW) z{{q?J{fyOUgBCe!LF^1wt0z{Xwk@1+nlS-6ZV1(Qh|$v*2i3t=HTL?MYV1MgcB?&h z#VGWBCz;@rQoJMsbysgH`l5WlK%;Kok6$I+a(_t)D)Hd@w37B#=2YoZ5hP`Cx1F(HD$tdJWK4~t^Ii0t<1af*<B9GXANb$5 zf!JsB98qi^_{G|O0&he6^)=daheF>s@jPWdyzz=z$-zMnDU|L;%J?>`r1te%t_Kc1ACt8S7A+}}6 z@31c;Gx4uJ2X8)-OGc)AEV5ZWoI1et?#}gnoutQ=y~s=Bf`wijV;oX09sa+`Dn(Vk z_Yzcq$0#l?UQ1GoKBDONYsz6&URwxKmALdix2GDxM1Oa<|UM9 z|5c4S@y#s7g1Ftk488B^9-N#v^T{T!$;Kjj-!Z*o@?G>9RI%2mwne$@`o)#3Zf(-M ze7Tv^*V!5H$m(&BTArss9o zlS|}&FC_#EmWe&iqXJ!$yNZ;Q(*`ZU)cIY>Q4qzQB|izt#zKRjq?Eh4pP+=9a6=zY zI%f4GRnmNhziydk)prdX97oWBa`1?XZhGm>B3*hd-g9tCrnDGKq&f;<{+7906q*=R zVsxyFXZ3uS((6@LEn@R1otE_K@TX0K&#g$gzMyZnEl8gsqtRO(0<;TJ7puNHYi&zh z%z1Q#1}CWip}hzc3#+{1H)_=Bb=;iif#~ohmd3>acO$&}!PW|RZLCoHnAdE~)D{T|aD>u}DEXjX_zuMC7c_Sa1fH6*wQPpzqx2%0(X>X7^ zgQ>b^v^!h-zOM;*c}*r;&}|9E-Q@Q_N%W5mneDzFnX!=TUP~Ps?L36l(f00cbdtG5PG6h1rzIwO z3nAP$%0rD2zXTbCVLW|+u(g`JUzNO19FrEmpiY*IS2Udaz3UM`682toBQB=)E(5oh zY-3}yVG5^9(qkliF>#98nWl}So;xKaC19U5DBc^CSHkg>dxhtrjqHiisVa#?I3{_jC8g;7wIIzTYERR${HiC^*Ya) zZto#PCqsr1UAh#|^SEU0#EcUyKR$1A-oCB32^NKQFHx9F>7ag4MThKM;M^R;fp6X@ z^cFKH>b=Wu!Rc+eg<6hD$vDe!D#}b7op(Rw9)cB0UaY4(cwS^QH2zXa<+3_|?9D@w z!iSht2U<8IKiz;`ph~OE8T+RjZvD0>aQ?Gz0uqgY^{mEdhy-nGYwL84x#~~v2NqDf z`rD4Dx#W289HdGQg1gR6q`STSdlkBSIC^fDpSb54J+mzaQx-IW@Kk}8alWcZc_y@n zDi#EQ{j!rSRXVw8I8znx^SCD=1qIAYTE`};gBW|-NurzCMAOTE-&}+E0$kiHeY#+N zO|0NOxciwk5xrp6+U4=skbue|mSaW*NEJ)f^LQt`1R`4jQPb#nLm79jRETFR_Qxm2 z_JjR5|GH^US>j5$9XvP~^j<=DMb=lo7ooPxSI(QxQQ6WPnEEbA9-bbRuA_Rs2kE|k zv{+D$Ti&aS=DRsiSjnFq1uAC8Bn^9R9f{X2iOT~{q_f^>Mw~AaDX>m&aAsG$@hw}? zLdKb~!b^8zA!8r0cw>Wg*|pN5A`lh%P{2mdZTVTtL1$!VBk*aqo~v61;tG&EfXnR& z4$?pS`>`G~+|-ixrESb%7$17EAm}FCx)VGP5ca0;%A9=gnAKom)_JOG_pHgMa0;(c-&;THX1Ktd$AyG zbiB3XB3G;-Q>=4DP340q4`Ob4|Ef$gv!3+9<* zL8y~bRGM~cj|-Y;2;eV=e~bqcBP;qEw+OOLHVN;lv%p~1Ha}ml-Et>t%gMycyT-je zF_-8L?hYJjL(69+`WcX zylwMq%3#2N`f6%}dQg@2lVGg$Ap~8VL#2}Kr{_yoo;seF>#tZL3^XH=tX^H$h2XZk zJA78D|IYCzz>r>tt69ZQ$xYyB@EOSKto_R&S61h#{{s}_kYQ=aL|o2fB}|g%yW8%hpmCUb!Wa2IrPDje|-FMN^88`P|6Rr zA)3gSq%_Ko5YX~)AzdiXoq(QsRsRZ^gPW5)3-X~0fHhj-DeO0r0z2q~ENGUz(hdpU zu(>l=k^c#py@K6FvOk53&K3}MClFP7?0G*0uOK3rR4>fw$4FK)(%f>i_mm#+QjYj> z7(2|w7d?Z2d6oR4AOH0$7<8_3jccv;wrEyo|4s}v==hven5Xw(eSmv)@E7}MBIr>N zEsofQO6P3tw*CDdWG%sJfeG-(qnX6P*^F)sqD7Bnwzw3oDRg zU?d3Z)BQ>iV?JiGrSpyLcCH=}MwrT&zuD-!AEfhzUw(Kk`s2)5tKCPJSUePJVU%~y z*oOd-^damf^wAV3)XR=J_oc^Jsy=7?H}t|Vd^9xZgxJGwEj!<>r}aFqqJ1>ttIrlzKU z&W`H|5Bk1f`TZ^hF9a#%#XX4nkCB?%cEfDWGW0-7${wjDaS=M0zyaAlZz{Z=?umJ^ z9$jWOV^xauOG*d)@&gV2X0|A)`_f1Xr}o&dU+dE-JWB$8i2yTl9FVj!^+OdHksM8I zZ&}$C6`=SBhxwH1iV`wkQi6e3fH;+l)BfKl^9{N?|2idp*u8o&DR~dfQzLpYp-pmh zR#OGTp^Y-PA7BP37v1k{svSvC0&O>;lK>b!N3y-SCJH6tGkLA1LuL+*nfBE+=(!>5 zw@tAjM>lV)meO!dR_1y@PE`zG3M5b9Iqv+)0jdh4htyYGE?XiyrZ5fB6w zq`QWa4rvr5q@<)f1_22v0RfSgZs~5MOS(ZiB&3F#nfLH~zQ6UZ#hS$*teZ3E-e>P? zU;8?HUq^>AJyG}~30!g~m^xk3cD4oUF%Ih~l5Wxd%_j7BE1WcDTeaaP&j{X| zW8GNM6f#bZJbRZpOBPyd-U1)cU>F8ezD$(F2;XVMosJ&J+Y)DvP02n zE?N{PKM34JwVP|kG7xZ(0<`+vLY4kgjvpX{eCI?06hTip9s&q4?>1b)YLnSU5!49c@M??gV@@+#3tD3ytm&1|6>P&=|Zrd;ZDBcWy{xUwky}>iZ zm!6)=G3l0h*sti2r0f2}Fsu8%Vdx{kl|W3K z-|cw`nL_p*uslKXK#5wTU)?uG8GIdlh#T@&{@p8rX32Not2|PGl(}lbos8d&;aPncsdjHrUWtetA9y z@EeHboez%kqd^pkR3_@dO%CUAb><@6)Zvx;0^AW|{(qd%*{S7^@^V3?6a5Rc>Q5Mu zyHkO}U1haxNaPM_yDe_(wvsqC@Z6fS0)JbvBt~T)vi}>Qrqx16e-i@r}7Pm*p zIidYG^O9ClB#jg=!PZB|-9N$k^^RbqRGe^~7Q@@uTm7kumA6OLcCZJ%pN-V6y{djM z(Db7TQHK)RuLFk#Am}#Qty1ZXajP>`2I_YExM37VPQj6`XIb<0H&Gb2`5TEAM5Iwt zqI$LyI=;pCR0#hIkE0LLb9kD1Qm%wS@VcEGE{RfCy7;sEZs;4n;e5_-cL%ihl^WG- zvdyPgkl!*gdU;31BC4!1BF-$Jk3dfm9KydID`<~LdVam;5@+Mkdk$G_Wn3U*iFrFm zJ{#`?vK0&xx&Sw)ow2=q$kSW)fTgfY^8lP4%8_*Oh|}%Sb#Mpj__!;^1kAC$cO~e( zYrNs}1OG{s?+9P~wzH|JHP!)c;IyH9UIHCtkp<*hJB3n*C0#5Jf9RoXw5 zXLLfJrvf+4KnJYnKtrgbCjt0_CXXXIm~oqkgv?!etWp8#3`@8v8uwTi`mQLxCl1Of z&JBiNH@d*mblD&QsJKkCu*=^JRKP@Hp#Lz{5F^igH;ausKp$`fEW&m-!x=pj|J8Ou z`ufC)xvU}U?&BoI7YMc-b|PQ0~XoxQ_--`Og=rfpf02)p*WO%d^TPdeM0G+7>>}K_8uH<9M zPK^0I*TRp?KuPxK1`IOQ3Z;4LZy*Y^m4IQmLA%8ZP>mX>DlWd?)zvjy=Rxm&F#m~Q zUobPjE{Pd8E|U)<{*gH~IMPz`TL20GkBAv=ONY->+Jm<`FfD?nL>)xNKqEPT_mk13 z<6q|ftgYZCk^tbW%Zg^h6Fiu^du^6mo8>SuNNu-aK`kUX^`;2y92+mjSE?H;lyT~P zZGQjGCPq#C{g+vG?<&Rw>ykwFN8qS{ZLNh|-V4y<6FCBz)5nkgcFWaR3v~|Q=E$1| z_pku!{W53)1SF{rSdej(1RVAQE`4D%qibT;cFq}9&LpZW%sy)s>y|bP)+ejR-q;_Q zT`JXf_#TKv^w8OdB5W7b@fK`XF^U!!Bq(|J{+r2iO}R^s+62Tu=p#vq!kiE2!s~o zbC6Xqwq+sm1Sr`-b^?C9eSF4dWhBMdtNkY&~*6wpWs_EKFsY=<8}b{?Bn5TYAXDbW*>U< zAqoJpy+9YbBO8w}F0mjL{xzZ8A%NCq=j1f=_kYCM=KEq~jg(p86&SK{3(qbuSATuL zO_y!rQ6=VlECkV8d)l_H3-P>L4_-hM#P>ma#0y4T>R~bW%4i^{>~UG`(pjpiTGVb zJuX-5t!;A00)2}QUOd^!(F0jm$U8hV*NsOIGO$fX*vI{VH$uom959aEAr6=(h<}@y zc%;>jzyq0>x+sl!BR_Va2@xTydwD!_HKm*icY^6{3|rOvU~buTx)9w|j8#(*@I?Yf z(JoVO2tU#Xuj4lpv|p1KB{E2%1G>tGTTCqS&maDgbzk)ULi3q{-eu6gGhO^47M)iHf$Hi&8LQOvXKJm!&E04*L$qv2h#hJ6mpsEM|~$~0TXq@)12yY^wb03_v)uR{RT8`gXB zBo7l$mj@gz=LXu723qGUG4Km~5U888n?z$&L?(WVuHanbbRS_bZ)CKi12BaNO7iyc zMBv>`!C$MO7r(=55AF{y9E5PBjDzFKPGrIqAg%S)hU>#75#5`<4@mxyN%xd9qDRY- zh8!};lq%A}z_+7ih(d1|SLQ%~ipVDwmMB-2DDbHw5(GZASq@%{c2Ig>d2I_X5PJNZ zNqcx=4*5vi>>7zSrAd;6y1K7>9uG!vpCDK(9FEY+nc`2^Qc#tqAdmRsK)ao(Qe@j=c%{6ykIv28vZ$O}SzP=!f{E53F@|7xuBrFh=e z)%(x3Fq*%F>-=T=mr)NORJ%WtsqR^6$1J^cHS)L<95~I|$g;A#6sd$uoGyH{O+c9Y zp>k09w_XTY0VN#`3OAML>FZb&(%#nb7KiW)YAaSZfjfuSdgJu<**W>x%1V$lW2jy2 z{bW};Mp#A;NuN%8#@m~X(QX~ATF{t91nqDI(EME? zQK0YhvE^-NEVGN#77BoVSTh zE9!S9@s4o+A>;}%z5#+caO9;d&Z_#_IFl1Z_o-ZO&&vH(50k6kImmQciGL$LU(|~G zwVp97BuII95F-4r&K{fqfu%yO7OAjHFlOlD6;-SHb*0NHN^CszYq@unF?o{J!_-*J zu4}pr!uCN<<1S^+wvE9`vQ@J0Y<3H+LCjX?4gueDQ_>&_lQn9P?z3S@Koh)k(s-|T z2_NeBrLj(x3hpOfkoc0~(Ix5F{k=AVn$n_@4)D$9Lq1KfWySfZHb*|MX$Q`E?F2RmN9wXYDXbG?LREqt_(I z;cvyZhl4T4ll1T|D?LMHoC&XI>|{p+mXO1s+{|k=5;llfTxlMHM3zv=89P7^Fg+R3 zOmzaj71E@k-DiauQRZEl>$8)m^?u6{IH4ou!Ca+F{>o90^U_kJ0Ri}>it6eIQGWr5 zaSIc=L0tp%tYX;6OxS*}YiIV717a*4jG#O69X6s}u9;2(7j*A^qP@2yY(AcB628Q> zATTnXFxUJ|H+l)D0bS?ac>p27D~p!~SeG{r2)gH+l}fjVEvKxM0dGY;gbfz?FzJNQ z&YlOzBRib^&ob6AwM&mGV)ga)B^xyVr9m<_u*MTDwnV*}*GbQ3%Rn*&@B(^+55hOM z#GSc-sP02e@5!A@XS$dR7SQr?3?-_?PLw-?O3q7IQn$OKIq5LVm+pou_I#; zx88YSWQKO5=`(_}4igMepUWG~(YoAdI_&6s6~nteikX5^kp*Qgv}7M?ky|QkJp2>kNQt} zeK1H)+Z0le`iK%|f_)^_tEc<{4XerfVd}rj4Q#<@;q|TX3>xZy z+|#jWP|G|lp+K8+@l%Lpyoiweu;LSWuZJ>Gc@QZ#4ALCdJJxew>82o?FAHR^CqHApPE1WQkZWWM zJ@9B7X_TfiyPThCC8t~m=)7y|g~3njbxVwJ8q2dBGD!zaJ1daWSWWLQk=?g!`#j`& zG5|`Xa=B}bztI67)kgiQ>o01hCo43v8*Rq~Oopb%I96OZdz;MXe}PdhYpkwzRVO!G z>lGJMyF2Q_#jwGa_@tCCIGFLWS&+^XvgBu5vVSo&$LqOvqfTjscy#3xeIN37p^nI) z){b8YEo2D+E;&5$V{LzpcbwSzYp!M3DHoWCBO6-r{<0qL-RSmDpMHUjFmrzQyfz7S ztUwz;_zSq8M-UX#06+Ai%fk$w$hhgdFMtTo;Y?pwSZ*TMd>zM;SNUbYrJW-jvPfLoVpITES#Loz_`s#ho9_ z*-fuvzSqU8f8yk*+?|HqLpiWn=~@n9SU$4BFdK4<)ZIpulXLYrf(G1AjS(0D(A3m# zB6LY=Jft|8y#Iw+Z@lefo;Y(QVUNqV{bvVSGkW zLPy%v1NiN$oX-+2TkObN6hg}sUVWtf|Fi(pN3l@#%dccyTtdOWZw}=h9Lm%GzVJ_r zR?3wY;bZ%=Ov*g=-w+%>(ea)(o`8%9MLN=Oj4@Nkb!5g*)r?Wg<0^X3C*F-yy$EXl zg=tZA(gx+=zws=#fAgT&ht}x*e|A=W(qN=qyev9(@IOttoB@2jD>~%UF$OE7H6CD; zb>wfoccX_bhpetwIssFEAdpv<&1{>kq|BL)2lH-V*!GJ(i#A?RU1A~hbG@^LKpJ?= zSu;w0#fhlaf%4VJM3w#0(DJ(p`gmI0{#XV*bjm(1snQhW89goOk=6c^gR_Lwd%PY> zmR7Nk#hiu@txz9cepZ7B$#-0#T%SjQ;@h7r9Qq!>Pcv|SEO$MD|I|EUlR|$4Bxzt! zj^^ax7TT>e{vKtl5QGk$DM0_kF^dx@!;6l*fE^| zkUO}NgQ7Ng=T~ofj>eF8%xyqxm@NGDleEP*kXKjEVq#A==$coKb%yoJ&wN-wC~a7#S2_nLReM5PD<8 z{aFusN&R=ODru1kBvgST(g4uRRgD6bynM^qO?85Zjv)6cC_JKLR2)OSg#xuw93{l z{vIe?Gb?<_tO4@3&bxK#v&*%(K57pFsp?9EROu^lCR5B@?R+@{U@wDnQK(Y{e7&Qo z7I)ilg{_@#&ez7yR58JrlF4Nn=a#?Mi-atYbuG|vaQUCj#;{Rr17jpCS zap;Avo?aX-u?-^|a$%oA&GN%iU$0H>=TB?spCex04a%$lta?QlxEu(9fU3#(Er42K{X@nQWD->Ha8bJU0qmcl%s&EQCMdZIf!kK?R&6 z9P=Pzq0Y+ba8Ve@zYCIhAV=w`->%mwCo84bAl2N9^(ZKskd4o<0sUfBOta40YljWH zrh5T8c#jEP2r0`$TU8|R{|xV3QQ+?m-qU62t{j5yMbXf(xDWZX|_sKlQ+@uxKbjQr-E~xhPJ`wq3muX0mvpI2W-QW_! zV?la~%1Cz0-D6gI6MLI`v%Rmx0#kz;ssEv!^sSj8fvYDJ<|UM!rhF77B=&%R4&i|AX>ed3w?i>1d>WO`7FgmHb8!F-KZ) zqEFvVB~c!sa9zpExMe?omM=uP=%!%t)B3Ds=efT8Ys_0jUF#>#+m3B+US5ojpZf~< zMIR{?RN{3n4P>^AQU+)KMWZVMPF8|UO0NOm@6DrXvYcm3IY`X3Ag$+D_h9l)=$+%T zZs^wWG z9|=)-CsrtEvrGR$B>DX$&j#P5n`^QSwYs{Odr5;)Ju7#MaOq!6uC|t-%Bg+H9g0bA zr)bvqd5N*?2v(EGKS28A`R5bGtLIaeOps<=6IXyxoH2BYsX!#K1d#xRzxrswcKN)| zKh<}gu5-N2RMv%s-`Wca z2?fYM@_%tC?EC;+S7qkj=}To08|!8uexSJZod@5112w$Kxh3<3A*szUPb`}P?~N9A zvOodZORKVF1y;$zbuL!On%6Ue*D&W~L*uEb3P5;R#vqllY{IJN~Q8|o3g+D$_kw{b+pm3~X*!RII2TTLG@n&PXRAx|!yp5CE7 z`tqJk-)>Ij9)ZjQ0ov}dtoIK0zH;-r&=?~o%GV~^5C)fr@YZ!-;?T#i%jT_koJ@x$UY{T9JrY}Nn(aN0Z z6ce~^zb^N2#rtYwC*JS9U_{k+EG~+rdu_cIgD*CC_qXA$%8JH9zm!0#AO4r~nsf>v zz9Yr{^UARufsU=yOK`D%L1%c&#T!9~Ca&P-&ys2P%L-ZX(I62sBda|gD9RGrU1g(t zfXKNkaLYgP!fbLsyl5lDZD*x0ojwwN!V}&U#X^=Ok_|r$eJb<#$$9FVyl)<>e9wXh z_-14p1@~I^n6sYq#iLqz=+@jPo@POtac1@BGJJ10E7 zGQ$pVbBn3c2d!Q7j#$)V;<05007jKdM4**OZLw{I#g8Sea}>1`RPS@-{YOmo`ghoD z^k4hL%cVK@B~#q{<2d&TWWJ1i%%Fg3xxD(`6tW+q`lH#g`0xT;1E!?h=}i~+sqi>5 zvYW4`3JMBRV0=j2D*rSyT#DgoxVGmBJVx_PHd*_f{TTLcawZiH?O+8?f0fGHq{A+< zi#;y9uJS-*zT+r?9@K20d-_@B=u0i;GW(`9dgns2{0TM7TMlZI?|TNR%msmEpFg*|{n8y&=#uloF7T-}7=pk19}C(RR? zAf2p9hFovuF_tO|)Q`VsAo53?axnWVRtML{`!_hZiaA$Uv33YPh#0gt{*M3Vf5OS- zvCF-{a_<+Ap^ap;1pu7~1K_|xixVKs>Fyz*`3-7}(&}j9;w?KP+p&PN9NhduzI@3l zigvs|hzJsO?9LVLHKrl{;+p2et-iJ4M#>#=t>C8mUrFX>UeQ_y@igt_JH2w=nTj_o z>PT0*(_i2AL>W;+Kn(M<*-#o(z3Bb$|*>5Ktt6pTk4+#-`Jq!$UcVl_*yc0$f>4QL5V)$2rBZFHW`< zwKAZ34tXa}sYr$8TGH8#?d0dkiwHHpNobt%=BvMAqg2YRRKJ+AsY(6>2;aNfq~7<} z?@Ix^BY$kO*v1y$1H1kg>iB*;%(3+<1?g(>DZbgBQu@W9$_d~Fs$Tx<5nj9;q0fF} zv!K&*M>Jz^qc!^hPyA1p?OntMcRP7U)D4P}y`LWP$Zvv9=%=5)5QW=ZigC}j)g?aK zYo0al*f2K>Mdp!BYqF5ci$?{U5z44ME=);7)+u>y*OHC5`9irdgF1GEoXd45YG;A_b29Hrd*}U?a`CD=*!w&Y z%@@0!6L{#UTccIo$9QPf5WoW*VI03)I7^Z9{$~umz{U1HMG9M39+7;|dD|1yNYB8Y ze)wz8cF9jY@fYU;FaMETC?j?F<^re5R&pEItxq9aDd0QQVqzRyK%jyke<+(8m<;Mh zd(Q*>C%nr`VA5t6nl!u$MRnW$^jM%qrHcOMC*zko(ST#%7UPCgZX~<;rXc@bV9W~1 zIc0hxgjJxYbx(&|Y1Ps2NSWXBw-T1Ht28{iB1RK7iM2TdJEA3#?>ot&LH75f$rGyLJvgc(8`#ceq}+ybMt49vkVj zu##rB^{PEc+I)nlRGEpj*ri1~B>YxS54rWT*<*6g(eyl?B3gIzpzBIdrucUWf7C^` za-(MmcxAGC>fl|Qed*M+bmTTjyUFEaL;V5P$y+-8l#rP*fW;I*CIqC%e!^j8EXJ7H zhq6Un7)7gYIgo;6Pi)C6ZQXuVrzA=W8PKpklH z%GUdP+oF!3NXn56zs9*y7FLBX44C*-?Czz#((YvBjRNFsg||DqTC5rGYmA&8J3+of z1P@W=kZZefij%jOoX{&ScRT8{(Puftry#%1+$xP88rUm*)n!(( z;Kyhv<5QDp#*k?lVe@ml=HM7w-(_u!$WOl%NcXw8dCI}T_p%MxTv$QQiT=)f*r&fN z?3U9(xwrB@u-mi3__hV5rSBiR(ZZ-cETUlO^$Qo@GrUGyJcq>>5V^3>V~od%Nt2?M z0N2d_E1ysl{v7?X$?iJ--?W}xCvq6!pVEK5cgDfN@$TY+@54%Z&)-Ow)ReTI#RICL zrSzV~zxZ30`5GKkaEne*Qn|1BR@nRmVUmpgfiv%wnp<|<;YCb>Hrl4Pp!%g)tK;=# zKaqgJwl2dQ1~Js1j-(Hz3P0*DK3ZmxI3kMunXLYKc>YY@zB_*K-U`ubLg~)Nc5ct% zFI5=s$(G*JH`Ti_y|HOB=vNWYvgpid4UUy4{(XUW?+>uY3%N_vwW(GJ-3jI2Q;iAP zc0Yere`;w&XXE~jB>{c;yj|DMcmYGwo#kLZsu@-$0!`(~5~O<>ejK4E+xF6iO)u(6T4`qEqIL zOhrymYPEIRY6K; z^~SK0iQFKn2zqo{fH-APp95S_eF-@d_S-Y>YQ?^I8 zme2lmxQ;*PCy6MG?mk9SeVn6G8m(~f1R6qh5gbxz+;>2=0!L3NLNf3_1BZ_Af5w zAnYTV<(tj%kQ>6vj_*R_ag?8!Y(^1aIJ-42R3JD zl$`tk$u6cCo%mr}fDSo)8<0!{-2k>o$%fq!U2OgXv@gYy;bL3foB(uZGdV|WCt=@w zJ&Vz3Zbyds{(>qjkLt4>rJLNq1|>g+@8U`!#qVeTq6&)3-_q^wVWDS*PN(Iybh>V! zmSj(e%^Q!t(S6K}w3Lj#2^x!?QPgvoC#YGbiZ-c{EwQ#$Vr^LdDpv2rQT=Qskt_6* z-60kRe-;CX#H90Mn_I9gKNn^qa;D$uAt-ix;%kL zn20<2Uf2yxfY46_3#6e!x7d%n5i5v!KcPy8AWFC(rIx|tX__C7JmIzz^qwtGe*6G& zoHHEzn(w~vBJ{HcK`~R7pR3!ix+I;Y@B<5q8Qq(VXO`@6x`lf8#npA>2MW6Y7xPC3 z%PUci(LeSmtLfISlWg=#1QA&c>qU^godtC%v@_-#1v z`da}yV7K5*aAeE%o=xNpJ=AY+qX>rMu?Cq8FK|ND1Y!BEy_Jt073DN_?R{qd;G4~Z zOL~iTi?}}oF$Z~29hS3II&1}t-SOD&mo%JG>YWPzu&bzEts&8~KJBN7$wcSXJfw%m z?CI|bsM_?BGwms=u}-CwGLg2v%kH1p2z&3EGha>S_-}l$rW*mR_`Z%51A9W~IC=vQ z$;G;>#X;TvJ`D-=zI73c{oSohR^iU_jL7@(k+EOdoY!1Plbs(O;Qr@&G^VbvdBdPYC?GS#~52buWuwL;m2d5%`NcvS4>zm1ZxbcSw4jfY!*r{Dso@r7!1fD?ISK=T^*LpX)5HEvqx z-f;DS4;q9(qkQyES$mV z=fmnG>g6MEXd(-tef-$Z_wt|$Jc=xabG4?p31f?umtyS+FDc92m(O~85!uvJ8&oF@ z8I}Q`Z&F;xYt&_W$jSCql~0Dsb+V%;i@q;j=&DJ0WqgcI3lvF*8=pJD9)bUd0sbFQ z8dTd~M|HJa?kvPGWRR^R!94?38plcb?#~))&KLb3oh+Wom9%onRsmltwG&R9{hYnS zo2UEc&qC?b4fbfH z#XlvorN`d{P4{o#BrWEd(ADjh_tc|$)iXq{%xSM(L7{@-F<6lgG2>yz{=jPfiVG(; z&OHeFiNmCw0T*#x$L|j+CsCiolX2zff1r`L2Ijc=g7();;ra%xg&YmuUr&Q` zc$wvcZ4FTD6QYj8Ad+T+g>8cdsaID*XS=R|1$4b-3FxW~hn~rJm>kvqVK^ij5DKQj z8jz$~4_Wa6V;#)$)PYQ!Wj|$+~=8?Ls`9^!Y%K_7Oc^?0)OxIf5Z30 zO=hs6?4+gNJmvbt2pPz%?NPuRy)oFaIIZ#vR(4+%IqBk7wPU|qC2f(GeoJ!dyqt)9 zPPB+sI)m5Srd>D9-U7Hser8BLN-VCvjU*hy1P>r=gWBD?(xYDaW$z?nI1aadjhvKB z_tw$^ZI&js*qYq}2R>R2yW=&fT7=j6M}hal=y#srCzaX3ZHr7P?9Y3y+y1;ZNT(0- zg=?jkh>UtVIgJgK)O@#R$fxLQ9eyRNG$w}4XK+?NCzGjgppjPpl{Km)wFc2=06DMd z%%=2oS^gUAw8Is%)&<4GKryUj{R z7SYyRT;OZFbS8g=-Zlz`pZd%Wx2V$9JRpIMWnd}y1UIY$KH;7ntD7dcpm&GYfSqyk3v zSBqsLNh(<>2l%W?pA)RKt0%r`k1+LK<%TgtLXIavVw6QsHH z=*OlDXHd@-LNQnK6nc146qfzhGr5n7*eM>&o?T^vt3NbAf!BP))9TY`3K#Q(*Wt_* zq#?LYk^dQ`ZtSa-nqTu-h1pLxvaBh{he@(5d(#?si2yOX@BP4!Dht@;B{%bbHu=Uu zTU_dQFW=S^`9NFc9=>9m=8=>0`3luK2i5@Yko-|Yxo6=wy&>IbTqYpxBLpDiXjv>w ze#JGE9`3N5*VZ#!i`AK3#!E_#xwiXN71nu*B=?@XRY^g5${0D6v`lZBH^jV8s7Y-CSDZ||h73;UqSSdi zq`zqX&~j(9HlF}CSuZeU6j+7L+aEMmn!v{Iy9nE~zs{EC5|IzERnFVn*=H8bP_--e z26En>39B;MmcMw+PN+TwTF9L)f*$*rapBCt&rwkBXg0D;WuES9x8zss51`cf6vKjN z6%YdPedbR-T*t2??b~*VLK=f+CGx&i7)C!UQ$+<<-yPokeV#Y4>8X_Q+LLpCSQ6c@ zV{cyJ$74|rmW21OW|T!V*GtLTDZ8{rr)lBFk&2}cBf8PP+$BnsBV{l)+C_;-vT`V; z*b$KpLrA=su;@ow5o!#J?}~x!$}-)VX7Q$j@mVQIbLq7s<;QuJA3+2=t0agSVj(#dPUCqdq|DUuuQ8(mHzbBq9sY+NcQMy zF0wRI%)T+nUj^CFw+1@lL)33@pe5)+CD`b~G1>v;rQXSN2%SyoWnJ-m@JX6bGK$7U zlIGJ-3v)7(c)6%=+}9RV_)zhxnS{~J0V;l!-M4=>kDKe;qBQu^mT!>F;(VU1MT^*JeWZYbI#zkU8surSQIJ_}U6`83un#*pxfqae&+1O2+7IrVy>?KbfM{|=wh zw&%ZlWi`d%1Th3E4%E{CqWL`74AYIH`AP`G;V2Ema`yTr#mxWH0^nF_GYzS!4E7=U zaR+S9@}Qq~7^rB&GICz#esk=sN@*M~UgK{3(#kA#V*0 z7UcC^(rQYXI!=+*Or#Z1UQwKp4cZ!19<*?gP>K}pK&RV+aYyS%02e_-{d!r{C&0(Pyc?P4az_TBEntHScN+Mvc+mcL&pE%Dm|mQi0+JAv|5y=DLDblL8de{gs8U;FR&y_LJ8cY+sP z^LN5No2RSz+&MSQQF>&5ej#WmY?GoV?FhH@n-(bwd=GS^wc`YTMPkWg#xZrmW66bV zjTYNjGk%MFxdYeJcvI!Oe1AqXrZnZ>u2jsOSs68G(8a2Cj5`-5!f&C3t9SOJ^G-+f zj*X?43a&?m&(L?%HBCQW??Jpgq8X6X#usM}WWeUCiKMlhwYOUM-Pf%`ExlZZ|C%*7 z`VFrP$vW~vPKSFk_z%U%V3ly$VI&*+XSLt7Bu~^^hb`i);+WP1LX7gaM8Lv~3+ftE znG!ZY885^?-R?R9Cz4z2e8}SKK*H0cVMedzwk-V`!gi1wr-c#2>BZ|w9Q>v}Y=E<&&r|6D?Ds<|nRzCK z=M5=qM&u~q`WID~(Wja&}$j^AhA3wdKRk@E@; zNGwI8O2)>7n1r9ZM_Z-BhKPXlGIwM8(f`nMywl_mK;%Z-zs9)e(vJVk~G6#p{K?qoiT_NhRsx=r>G_4aMs zMA-`7mtHhx;QO(%w?|+j9<(!)=N*3|5$uu}jms=A>s-R{6aDG1z{Qj7x^B6k!ezC9-uHJ%V@D8FPYwpY22;f-bQT(>_*gDy~sAJ@-Xfdawn(;4nojzO<3$jQ1Qv$NLC9_>fTUNtl`w_k_|?!ua)iXz%O#+asdm%h>Vz7gs( zo#A4}V?RV>`&lfxV~>=PoWOx5IewE02X$N>6WOqeEDJ${-LJmi(HguRcf1qLnizLf z7q)84{(q#?qoRL$ccc@gyo9EY>pbrU*gSHPxm)Qjo7>q{3mK?j=qK4^Z|B|(eVJ*E z5wbY@KR0GoIJ#*C0NTfclk5wq3DT0sH~{Fv1{)=AcMOj3*O1H~3xZA3?FZ$;1+-UB z6Y;;AU{YdsmGLI>$KvfEAQ#voWBGH(>vy@gsJcdC{CS8`8O?8!pHGMyqB{0557SMI z)C793ty@w+LPE5&A2q%}X`8e+OfR$Ax3o(8@@U%>TBCYd`{qRo62ztqkvk}}KF0bn zNC(`AyToT-M=mws`|mBubI;BYK&mN5$mC=nZ$-%{3nG&Hf+uR00T1V7zD;~#zc(Xd z8RZ>9t5W9j)=$xEdOb{G#^WTfP5a&@qu`;s@Z)CS*5Yrf1WJ198Z7bq&+}ZOUCko? zH6Du$+T*$CqUG zabz-`CE?ESP!>Q~_jmh4CBKBcrU@Zq z&q|ifhFc4t_j^l|p+adJtuuuwNJ^KLQ~DQ&(EHosD8)sn zWr-)w9k%6ww0%fH5|qzIhaS$abtAG^-|?D$3#Xi)Q}nB{FIQ<&g=tehW)^rxMx!TS zG4vge^Dsjxm+xF2^%$DlF(b&gUAX^HhO;iB(74e2!)L0SN>v}H-4rb0Lx=HaVut92 zY6ULek?d0jmAmEtgfhAj3)F1$cTCZd3~;1nszE|mJ)&bvX;m!meyyH%w^QAJ+Cvkp zF(!;ojq$0#6t^mNw&TQ^DOqL)s~iuGQ$J%qKh zm^yfn-}#O+_*NQOwVBu&F^dHP)KTS|0qo{x+I))tu<;@=6NcZ}ShZ`P5|6(49K z_7*hDXj)Y*oRj81k7>;1chp3{IrE4KOUX9F9Q}SM0_rSQuK~9+XHRu!bX=d5wgvax zvP{|9jyp>0y|XNzNUgU{=_4U06q^R;4X}!EU=`FQ`Z(=THyPE6Dd=}j)oifQA9}b_ ztqQBu2%v^WN_T5hqWiuU;rVm22V&+v&Z*~Rv|Hex8DUH3l$UO^?`1eqO&@*hMLrj0 zFPjFta{f8MI&4h|zkxS8!R~x)!z0&~x41U}i9B0j-bGU+v3ilW6`TErFhhG_AH~9l z8Dd8(<|PXM8O8^Y^t5g2G;w)5GF8rTSU`F~Oc%J<`cd!&Qz%(QCAXqLk@;&DdCBa$ zcVQ_wC@+-jfXU9TK*ZipolUU@pARFv8$60$em{5x5cYJt9T+%rm}!~6pA89r_M0$P zXz{XVJHzU<7K~wSN&gz17EHa4Gz0!Zj4V_Fj0kfg44{E$1Y0bk+H3iRkc~kjr^r`P zEp(KZ+zrHIfmqTl5BBM?~Z$k1)N>%HvN`+H8#Lt~JG{!X*i5a9UdbIuI6A{U=F+RuZEU{;9; zAYQykICed&M|pr)Vi59h6LJSqF$3TWcRk(#d_L?5`C|mS`H9E2``4DnU=trEfBGLClVdC+nd8?hkARfb)KC7=8NZq`F%wRHH= z(-O)%_l-&`pNnXMm$2$Jr@cYh_E~BQt|r+{=H7 z8dXtTiNVu66_|+K_V) zaEH)7MDO1KKAy5A_OlIM&(oHa0Buit5M{N7-ab)JV>0O@v!qLCZJ!dYDKaslyGJ!z zBC`&}JG}^~3m{lId$ZKheM)d~Annf}H}<52050EMYe<(SRM2SZ?NT)2-{2z|tV;Yj zCm=35M)`E)=OA`C%fp_8&7~l$`#S zJqN=S2PPi%c%~n}D-u%pg0>M&lNF5TS$DvivaXCUEtvUmWp3v`AMT6aeq%RoIsVLd zEe&pB{u8IrRfIH_G6|!D=5?Azeq?gTQ%?&ivrN7m%lU6PhC!0X_I8?D^ur+(a2yR7 z0ENY-6ZE-eZ1elb7N%L2Zp1+v(|AMTp6ct|kHXU&Gc*b1OV2ZwX5;8^U(0u(#V)jB znHB4D>1r*a1X8yVN$)h-q%d8p?;@H%W;?7(6}3D=*Pcbk)MwdrCL8!=P5_-r6P^dP zA568DOrE1K zkoq`K0B&g|-(bv0dJ!s{@3^h-f7H@KM)?0xOD$e@8qv5*Rvo*uig$3#9)bcrUYQFp z=yv~eGRy&F0@h7D*1mhQ8u;fZ)IpkU3GGYwUeswT{5qU|6^9bHHoOA4Yb8h;~Aqwp*@lQ&i8wl|Mnb^v?VXXnxS>OJrb9MrpFF#BFK#)GubQgI2OoJhF z(-dEs{TQQ+`;tG6h*w z^HpT$PNP6}rm+^!t8u0A`52BB1u89@Y|K3qaTMo)KF=dWIWXy z26Xwf5H50-C1aaq*sY)*z$cn3#5_UOqoT0O0G-nugvUyN8Wqkb_CDdN>%N$R=% zH?VXs)UMeUdVpn<%m3Hjb$&G!J=+V2fOJt1q)1m0lqS6>O;kWoL^=wn)X+;HgeFCr zfb`J2G^tVoM3JUIKza*JAv8lZKuGc~zxCdF|H1q8K4s<8%sO|^>^-y2oMAzkz|iDq z16-Gj(%k~H3K3tSVQz}Yf|Ri0MTc8~v7v*k9QoK9EAf@7C7$bP4}02C8gQu1-EFx1 z>vXeWDt0XqO}ixffx=GLIpu^lyldS?oEE&0JmfLL2Oxd+WH8pGJL~A6B+#T?31x7R zKRJMT3q|ESAFHiFQrayL3|W3KM#nA5RYDa}TI*J&|E%y%MuD2SxnXZA4Kv1{>TYHE zG(np=NrouAF3nCX79Aj@J=vScl5K9n4r0xT_hsQiMU#1GwZUD6d;_Z<9{_9Oj z8VH59Sh-HP$U2(zxozx3n|<%YZbmsc>&{0twc@!vB<{3(P)h|{W3GZKd!1w9j{d93 z*t;ol?J46W?jC_w#abo*g#tC0a5%!VV= z`}_nRwb#H!6$!panUgs}T}XKQ&7m6kb}ep4`8N!(jQt_w@9+C0O(D_mN#6oO+jvpg zn`wV?ge5FnL(@9afw&lgXKnf7Tsk1uBi&ij)gz}q7PSuhgv4aPSc4J{OT?B!rI%R^_Q z_=+~(FG&kt5>iJ^IU^gM*M0fQpRZVdGPeC|R~Zvd@6M@FZogtOLp#a+oIs{keqTdo z4u5Wq$<7Wx(Vu+K;$y3PUqK>vR=!<##gtiUP759n2t7H`&oWP3`HWsOJ@baS^-)#l zkNdoJb&p}-!>|Ux)<1>aRF}=&@1?jazlp+ymvB+WD*}In_ivrV$Ub4xC?dO>A$0rh z;A)iIg$5h5;%CO+jCd|1z?Iwu>$8p7l?j|CR^~nyg?B}C)A81$Wg-*i7H(B&B?rW& z;>!_hpTLi*IPyMcl!lbeCn_*5_ZfoG1Vr0#+4A>YjTh+^;D%edYZXDVu66?LTKKyOC4MQ479AfZY7;G@K3wIW|Vd31HA3#WDagzChfY-!GZDf0CX zyHWG+`Ai*M!)Y#<{;#Y8y#u?>qgfRtBBva=z>tb>;o87OFh{_X*N6*L`y}b^vGc&7 z&l*c`%~1~dX|Cz_ra>peil5*jvDk(LK}p7>RX<~zU_aZ$n~X0dpZJL0ZnT$s@4qS% z=})d392TWd5+FBHKMMI|Dj!2C+7y|siuQbKX1%fU(7PxzHreOq|3cF=VMV!VY%^k@ft^S(g6s_FeuKM;s#fdTp@)f7O%?S1HrR62vzZqDd@f=1G}3kq zNj_@vF~7#rzTob;ZPco%=BnzQ_k*q&9`p1dO$tXQfTnpt(|KfMrwIm*j+$elhVM!2km)Fu*@=EJKcRs}4MO|q@K0DoU#;I-{^sRR zj}ry&vy_lAzdFQOGGvJOQxi>^8!!>;gE!3b%W5a`hCSI*9eIaULMxRV#Nq)0nI;{d zt6Kt25H~5QQK6CK)^~~>2Q6XgSrz#fO`Xxs!ZD;kp4ug(jp|i#C9!^#BEuya8ruDt z65D~v3?K}Diko$;9=oi9j zUT)JcCrv_Eic^}egL6pmzlG2Cu%J&cI$tx|kVns}7G6hPdvJ(^r{g;ji7$jSN^YJx z7D;j1gl;J|qC+cth&DM}s8^X?j{W3-G%3I`|0*)riW*F4Fbci-Wl)_uGo+5|{%G)O zO4hS|(#qP5O9(1fRfG2hC0#6HQfRuxpVt>RT`4c~R0I~J8&b{N_dxMQv_+d9M8%Rl z-LuaMRgO=k}JSeQTN92KXjt=w~djn%|(ohaS67|F8JHZWtNg+ zaQFO=4eFW3mvZbL=9&41)!gSkn+RVW(w2*@R<{xIwNh^Oqnfz=OiQI${6sp3m-djh zyWgClbAI^eitxB=98~=J0^50kWV;TluOk|>hBsdhEcUJbmW{lTW9$CwJ;TanV#bYY zCO|3quA-`r)DYK=eNyM7r6Ww5-oMW-Jre+kYedMtf;+h?Yj;|(O$TgbNR#vhf3HOe zGHLkcM7XChhFypijI|bhyEau;{aX-p|s}(w8m1PdtiJTGESZRmuRVh+QzMQ>g zUfeyc5>r@&@&F+r%L#v>I?Rye9CLfiulujb*zF%H5nHaZXpu#*5zn_S4~(l=$T+Dbt*|*-A+f!>{q-4&zd_%4e?YnqL}1{A@F5 zUSc{gs^V%6v{K>n+C%)a*@2nJCeCK+;EAAy`6EestsY&rRiZbiaYn3UTv-R82U%jZ zKYa+TKP*%bY^&Oo$AmdtL!Lr zybfZ-&mju=0%-ULX(UTTlD|!TWsM<~WL=}(S$-6~Pg?tmB!3c21M;T-&QYV0@J6Ga=#|LNKKb!x zgPj8Yrv++R?(vFiW&C@d-+grQ7Bpf*LPdP1Qhm0O&d=J-dPcft4JBS)3*%%kJuEs^ ziXqkM3=%e>ou+^MD`aEDHO=_(9S3I6Hi8rM}(liRaL zy-L(&I+Iw_!9keLzn&zv+?h!8CKq+v4|x1t{o&XTuf0yWqsLMg@0_1(aMgR*$u?`I zq6G(bP|G0Wd{RA7{d{G-s6ruXO|w_SqFSXqc4I0^(Zk_1T}mf=@w`o1*YzLNOm9{LrNBX7Y|+`c-9|n*K;`o8QmcS1jD_MdE`|nsKe`3}Xq+ zigdFVL)mi+I*}$x{9xLdgt`(owdcJbW0og<-9+{yZJk3v(JJ6+~(HiwzDP|%WY|%R2UoHOrv3w=#e*#ZB}miGaiMP zMnxkgv0TCHMc?4j3)()M;-fECyC)7Axu?H$&z99;AzsuS*{nAb6qs5-0W8L0QbU=X}a@>9r4#Yf>L|IqB=Qe;~1w zH7oc$B?yox$Hq{{Dn+-~KhB*WNHMQ;_uvpV<%xFn0`L7e$3OGbuZ9Mf?ba$iygvCO zh7>>|_>+aSjV;_Y)~yH2MW^-kt;#t6o0+a3ZWm3rIDi6Ve(U26T}wR+Lzkq*2k1ts z%-9dBt?R!FdwZERzwzb>S6gl_by~|uGfM=0JSa7g!dY{F;?NlO~ngg4$URCSZ0s}IUIMD?uH1| z&va%k;TH4}`Cq7Y=?IUnimD$F+_vsvAN3xw*3>krIebIH=~P1!bkwg(cUrXgSVDez z6VF_;bl`7?RJ8?W&S!wNWJf56m10T>7AFRRdcmHbEkj@GKcS@x7!VMIqcF879`gws zS5V7K#v2b-ZO9|)bilf+JM=a#8K_B z{z#oscA^$f%g-Zogmc~%XgQi?TiRsk1*A7+GF7e6bN|`^fq)(48a6u+8La8U5kxtm zo`*GP3%S*Jfv{~$mfhRGe)area9`b$vLa#3o0FVp`LJt5xB^vT-=$=5t4;FG1Ifya zilrRltYOS=aJaH5`7&s+R_54P=ff?=+LIU(cn(yXszn} z>byKeWMX~&)iT3?GwOd-oMeO5gH1IdvdvE)TE}KN-S!xSqtU(uh* zmvgAD=u9K@e$>g-7}=F{v}J$MvBQt?+vfqh6v{*PIU5p<`qdvFgIn8$&Uo1y2nZZ% zUr813!k=nn5336`;bryRwcxtkwQn0V3-gsOk19}qFcB?(PbxvFYweTzJ-1tz_`YBb z?jGo#Fs#bH{8ubi!>FgdjuDMJL3Zku*m=vgKJ`Z5wuG>*RijxWF$1-~_32xvD0Hq# zZcVSR692@=`)}|dl;AJt9>FsF$;(OOOeh!NrmAQMyqpH*%JPd#)Wk5Uc6m5i$ZW%`-k&1py6r)8SeRdKb-0@gT3i;9wgpfwGn06r~s_$JzqHMpy!$ zwzyAvCsF~RejZby=ta>>7%HXDPikl*#zy7&9T5U-7V-uj7g=U6a=TBle{>S@vNG%v zmWTbx9DSYFR1I0|*ij*essdl{yK!|$kFX7t=BSC>Mj67Cpkxu z1sG$gerKd8?@WS{kBcUquKi``=UQK(}2Z_Q6y7U>(x~9nekv zLcVj(dkV*6c=WOy>A<@7<-9Tzrh_K{G868SHEA<{pC`_YGtQCvkJPKUr#V{iTJz*j zUoX_~G|f|0Q&T|OJNP%K)rUtB)0eK4(y&8N5$`hBo>##xbG(JRyNEI~wtwF9`ZJS! zXo}E?iI04;>eS8)y|?gIr|0R=&{+kg5!uApC^y%{@%JlNAFW~{rTH=9Xz^`x1xvGi zLmUq%U3R7#-K-Apq+7o~O%JrTY=<(Y-L~#77Bg`_-n$GwI&e9y#-cltAzlpvg(lR%@nDPL#|ILQe1~O9ml0^rd)U^oOibW z@g@cEBMcW-!%Ddg4;>+Ek}OTawv~yBtGEIV8F=Zbsu%CKc7v;bjOfho4ls5Su!5hm zP}TH$(W7&3?hP{!m9(H&uNqfo@tumt?bslg8rVwnYbuE{&3T@#6tvJ|j-Qn3HIrQ_ zVkVLC9g{z`O7Ik?UcTjr9R4!;CCRt=s7|G0IRT5diLOY#e^leOydL~w-`0KiQiEBN zLLpRs$hI-4FyvmJSK{;nC03%5#ESY-*Q&}N&LUa^98m@bU{9kpC*bspF#w4N<<0p; zgOS!0y~ zIBTZZ0v${?E~{~@_aD?AF`dMdaiI1%t>En+xRVF~XL@WKAb~^SqQ9>3hgVt+<#*Pf zG=hYDjC{vm-~XL5_ygFX5(>7a`fOXk$acSP%Y%^!{RBR>%N8uLPq4EP;RS#$HTDDA zlLR_m>f!WemT(rSd1QQI7x}qwxwopdd!Hj%2&fl>#IC7!r#GKo)1jut>afT?hu{%r-0P;qieQyH)DCGegrC{%w(wtncaF(QWfGD@yE*?m# zX&YHdBNyNg zsF(5sLm2o3!JbEhf8)DrKJ@~C)WX{@$k5llAK*V}z15z08zAhx{j5Fh06#xJ0VlT? zUbfcmb^-`bhpZi0I&cu>?I1%ZZx?Wcowc{4ohQGSBf`(l)9dedjbv~D!|ebiA8$v5 zC-6AH+Kt5);o{_+#Mz4p7vDu&~}fRS;Gj zRQT=Fg&Y3=+kvEJy9D6OKQlgFe-2laKruP<5>Nwu->&+Th?0!Q8!_Pe`d=x-p6*J6 z!^CJmdOwC#lC+sZW{MC|fVY?vHB=Gz9Zd$-9s|k}>uj`VZ|xh`wu-WHK}0f^qVf`% z;=SyxP_-ijjypT5!spWfEr1RafC0@M4ul*W0V zazeRaA@~bbDT_k~7sd~im6Ztq{Fpe{NACjwl>|BCK{0NcWvdHgFQBo{F2PabV@)Dt zWb~A`2Vu6LS)6(e`~l?eEG$Tylun-Epl%2jEY%}54mZtd@naI8cLDIz6^s7-grQEA z>9^;Tv~CiziBQN$ao)X-mnQ1UHtdSGpGXLPq6pwxbkhy^Rv=u*hy{VOK;TA9M7FVJ z?rlnPgW>Hdcj1H?N}q=SNL;I%m*ao2+8Iy^&^_d@dKd@CJCfVa8UuIkzzsm&S@?ZB z;JYwj$wRLU7w|u=2{^UOK&a(OHq*ROz)U zur*UfevN&tM}hP2Zv`GKS}5@XAI=sp>Oy#qHIS_kCF(G+ur(=~k(upeH%!C_oRI@J z4+QRUuny4_0m;cvu-A4EZsclAF5e{%3G|s=8F<1s zkRuaU?J#H+(prptJPrhTKKDObH{QA3n*UsfU;SQoGjp2)Ffr2SS&x`bq<52yZf|Gx z_Vpd(^f`3kRSnh3oHV0y7|i*eS?#cQvH2z6%=^XaSX4ygd?^@>i-`4=Hmbx-(~)_QII*Nf9Dn`e$puA(48 zFQ5C1O>c#9atNs1+UyV(Xgl@588?9UPTvB7V0M%BhK-y1$rYqTKd8iAw1BY(n}(zZ zkHXqN8gTC~Em8}~%kQ97-g^+W;jLt0k&zwWZoF!0%0q56!S-?Mp7Sq#Bg)Iu?e2UP z2^&YpB&r8bM2d0R8WWD>qOl~VGc%nSLe_;6nURd6&IV(bPa%8L?45<|{XE*>E=Am0 z$D=Sz%p4kL@B8SRLLP!UjI!a=2&9ZO86yB?4}J!GKGj6l*1a?nPyarYo@C>)1v@w| z38G#@K!Q|SIYH2h{O?Ubhb6)~Ljdw$-+9)#PA8tWuC`1x`tSl#DXqOnH!qWpvRDaX zzmw&>+(?(aIMDA7IS94H(o7&q5~v zLZRltyl$yHeB*YRoAJJFSywhPA_Av7|cmF@nBL=PC4R?009j1ea>l!NNAp8eT=_AmabtsMaD%XJH;4}0=Naq; zYd|IBFjARy#&7q!Vf{xI&C=0PUT*g~Qn?1lpCBSk-qX0ojO}&la)x*8)kmQ$9mVJ{ zMMX6p1|?Z04M`fCjftKkG>-zLeW*}lzwb+~MZb;m@^96)eRaZQ5S&_v!lwK0=5%j%F4G581kXy_>L$#LNhyu9R|7O2= zam!yUCgqp6n8)qO{PBb1OwJH^eq+evL18bFmk_1jMJiN7n4qDbK16?C(yXahA%(MaA=%^Oz zmt7062MZ2T$X;Gm`!7bUUNhanE(Om=G=U0?M#3uQw=vO7^$fj`dPS+$etp_P$gEug zCxVtksTa3Pe!eJ*{r(-NZ?M1MS$XElm(`V?d7i-{9ft!vS6(h;3rzU@6-|txG_UHz z?^yQsB|=j7LzUYD@|nxT1%eCl?##Kv>*sx&PBw~=Tey4vpdHL<+JnK3(GyouxdZ$0 z`U*4T%vK?QEd>^GsKWfCHTaT_X`P<5mQIPQI{INPk($1bvMkJz#|(` zx|eo1E`xBO?7Z> zwEwLQ2MmI)023a3|DA)C6**?sK|ao3R)A@=DgJ1^!MQm0JmTqxA6T+Fg~423OdI`2 z5X}8?>SqZ9xRu9V4^1d2XU%6dImoo<{X@7ElY05=_EK}N=tZ<>zeL6TK!6a0QDLVA zAzJ-Eb3qwOOTo5dZbH5UNXy9VpAX81Cl2WFhq3#x4@^itx)Br*$OG-(i1MC=chbDw zuiOV;6#iN!U6o%AR72F?&rbq{7Afw~>uYS-#tVG^Ow8&sK?D|Nn_J<>*4F3oPJ0%- zZR0j!^#A+BAra8gEPzXZ2ynzphP&7TBEUZ!q}LDM=l(T!=VOlbPkB%d5VODcb@}Kz z9TYC5T<9jYUKHulzwM>$8yr+7uJl`V;Si*Ns=B<`8FmtH>vfzTbU{&f_&vb;Jv9Vw3ZSgr=L8yx;MKUj5qmtcltsQyTJpDPOB1y;a%S&t%Aw%@%_M=1OpW)^^;3r zYqY-Q(UcnNTb)*zs~Cm|RXVg=pr!6`)nFK11y$G-Z>&k9%%Ghe>U z0B%F7LPMovID*iCV=(E2_5jb4nmzlSoY1Kt3$oHDJ3C8BKDS%(2B0wV*c8v=0Z>o) z5}S4#@f5CxNUr2*{5erKjBSYXpB$h=DTCH_7THR)lYKES1Gqeka@Ou7gX%)g0;*dt za~^Ud!cJ%>QPhEI`g=o3K}04N`Rh1>?7<-y$dC}Gx6U|M(=Opz7WHv}Ux#IXn8IAi z(Pr_q##tSBK(1j&dP33tS@mob1l89LCwTM@*CQQ>W)FfSIZQ@+XV0=N7$t9X|1%3J z&@A++K@fo~v;aF{>K9F8_3FxC)^Z1RL(*0V4kT_NDbc+~!>rKbvLvm$5_@&^t^Mj7 zBR}*h;L1XxayV+W=rUQU3}WIPtED&e zAxoB;7;kyw_x`!WR@0N?(@8rOOvV6A#vX0&`%(5YdO4 zW<+3=4FeINnV_v35(I4<^Qq7g6~bPygj|(^`LwWv3fkZ%>JF3g0X8#K4+&Z+Db|}` zoS>a%>Yfi*Y9?{&^Y>4?plE$H9e%dxACx*^Kk-bbv)YUE>ku5mKRGV@VvPpKf3k9f;_z*Tj6RJhVvKpvgez{{+Yz^lFJO@J$_g- zyftYONT&tZXC@~jLNWcuxdiGrLcP^>-^}jJ-?X3BkZ=cRK-%WOC02)sJhd9LZuB2?dIgphrBso_~5t%_gJV`Kg`Gd}iJ0*k8)C8lZ6p5#> zL!+*rzfNxW&FTP%!;d~<1{F=mL*y)Gn|@c4zXSH-Lqj1mUZ*my`&XK|k2pRU-pk0! zrJg&K_$pk47uz-4lY6giKL%M1gPeCxm6CEZZJT;TX1a>EIY+DQ00z|b=dWPHlaqii zS62pchUNQLbb3SMT4wS?-I-~49Y*dWp{yoR%T=GJN5R8_#b7`-bb?a^O4zG3xNR zAMhAe(gZcq1XWT3oT{&1!@g_dW@_VVWfF;KJ(Em*+V$tpYbuLhd4~7O)s7iGx)~AO zO*_c?lTHrWYwr8liru8jLQ+!t=~t6ZA+et$!rJEFC}^F1NOhxz>I`2Vm)xv3iWIq2 z*PaMcK~L6^&)|xXI=9)xMAfKT66Z~d)3fEI#lA^Vro`qLc3@6KME2R?JEdHoX_5sv zY7^q}>cka)`3s>UQyT%w?ivv>r33hnp3&1XWuF*WPXX2Gf$xC89Nk_BcD(x$o##^Byq7{LYGj(zg!uK zRA@aY{_?C4`KiLq_1(L7P?n0)jH?96`bmJ@TpMH0kSf516gQWpx9SSWC9VvQe<~V{IX z5~(09)0E*oPMU$@qbJ3gd`rm?_?HIE^dn6yOOR3La_!v>K%tXosxrag)ToejW* zOPO`|Nkzg+L>MaCwO{&-{pyDUwxov@0oF}5 z04^c!3l+QwWZ7e=8OQ3!&U(mw37H|>ei_L9?v$A5OAl}+RWoR;aA$#2p_+n>p87Fh zW&;AD1U3zBWnt#uAIlyy-ovIIz5VqI2fZg)aPpS@X%DEeLlMFyCdb-@iW^PG#EgH_ddv6_4r2s8qQM6%irD)Bkb_t#sh`$d7TfDYe=4p`|KDEXHd^ zH?_InSd5;91*t#X*w|R*y6IbWM)5v%)R}Vr26I|j)4bwKuy=+HDB!rGF@A9Bu|q>b zAP3mH!8)7k*>m&=E@St|S0+GdL2tW3d-nlkC|AoYDGch~e}8;w%+&3>X+&?)n~ub6XmWN6R!n~U+Azj5>S@o7qJ zWJ#=dJvma)cG%6i>53sgTQ5vncx#e)zKunWN5Z6h@0?ZFM?~DffZSLX56yufb#C#_ z`F)*?Ms`Hyh0NhV=&4c~Qd!(7@C9SeSe9Bb%_jRjrIzv7Wp2w_T+ zKjEQ-YWnMKpZm+O2(nbvr>2Tcjp z3D;dUBHO35zv!9|klwG(_*-ECgEU#A!h&0k8)mxH5i6Lc9oQ zhz64p1oobs;}^2~@nBp!Z3eA1_*5>sdkLdCTgKxk@=@aTZ8DamuulUP{w6s&SFKBJ zlxHj4A-x_qL1yLZLc&rz%h(F)??unEuDjullG&W6FT>hmuI}3Supy(nl(554ZcSD< ziOTKT)TwCFdfQ8#Lt|9>;n89gGVJ5}KY0bu&_dh=OW^7pJi(OXiukT66#`To1z0mE zIg*^Knivd}*Uv?g-2Bt137iReFq_1)rxWzD~Pvm>URd z@*{rvi5%$b8_0Wnm#|j^_DBuI12aoEzH- zz+uoTzg>B#p>Kpk>ETho#C+Eva$j_neCZEPsCe9&1BgLri``#f!xY@M+Z+ywHx(O_ zGgd)ueR;BdHo}dh2YWaY%kM<~`29i?JhpTVDQzgtxu6W$>)b8Wt3>VupH|HJuN|T{ zpMIc-Q|s#=l+wPINzKuNOC~GKcPFw3k)G3cIZZVtOPVP77R|Dp)Bn}Ktk5`vZ{-pX zpLeZ3H!+c@L)cnIdr0fPsHJPY&PK52`i1y-fvFo1q_6rFX>W4`T%<0Zzs69_9AS4~BgT3WH^0w7kEA`sNdfl4471I0W#) zXhjMZG#E}4a^4Y>E#tS;Yk5T=j+p!U^0D7f%W`93v{2O6FM@+}+1O&TdvCwqm-bD2(ROsYp8@{b?92TOyg>FE?rZ}bJY z#P{X1m7U#fSbFX?8M!eIBtrZh!2^w{oyh#3Gk}PDPzGRD)eq;-6Pxy8bc4w9^ZrIV ze)cUwS5~6Fozs5){9ON|A{JxLZWu8>eAzMP!gzYg7gijg3*;%Dt0 zS6KF|_QDbpaoSwN&PscOKr0(s#eH1~bZ!7#kL~ZdL+USl%VCaUvAQ>7y9;TiZaBZq zy5IbKE+ibl^v#S(`KuQPaw?Kj+L)yw)tn`-pt_pgwBAi6&{Rfcte*_$_*dWR;gc2h z8{^A~6VvUYDc#!iT`%41p*)tn;qAiU6qbd}j%*2WQ|i~$Te&-;DJ<@6$cy*hDuIDA zzT3|oAZtR55>iqz8qDN^L?c5(q2;Fa*25)_);70QSm5w6>)Nm>UChJcu=DePzJY!# zn;@+3kF}^US-@TjSVwf5>Nj*^9l-kC9V-|G4`aUC z!+Y}V7XWnR3;FI$MYPYpT3cVQVtk<=qCY#P{9hR>^J;(pFeB%kdhOT<0%`=g!mp{v zS*gbrfPj7l0kgnX;;DDb9_WD6o!KZv(Di&_d3k@lX@c#?QD;e?4Gs~lxUgoO6_Vdw zTwDs&2d4~m8+o7Z)wxZ^X3O}tKex{w7?HvpJHMrBqnj>{|-#6B!Fu(V}p z8wOr(Odd2~F6j6E3Wj_S+Urp)OkAHi_sYo0p_r)DvCs}W(Jy?!prg8)(x$xxpNQ(T z-kQ4~9UA(mRdbfgVQid9;{N>@x++}dAmo#6>rDZ+Al7qHFtXOW1dt)_{G9Iie>qhe zdyp`=0-{;uiTv?yJq1t0)j%m~61y^UA>1LW!@2v%4-8*wMXj!`?*Gdw)h{=4x!hOx9pG4vSlhBr>F2sWUqVH#Ss88zA0y%`nJ# zjk0_KiofDjz+FX&MyXJ*9W|dKdHzM7oI~cFE%( z_!QryMNR!dlF6V4EUdD4jbJ<=<=mW70_+Wk+PfdbG*?wVA`)F7|59p`wsqX%^ z?s-l{d*E=XqJ2>3uXHo5KAJa0P@%xk!|ZL@kfJalB-tVH%#KvP!JGR)LbNd%Jvykf zqI;S-d192T0Pyi$FhefC`94dT=g;L)P`D@^8l8>UocELic^UG?M&sHr$vl456U*D? z>gPI^nB&m*kcPJ8hup)z)6R|~E_a%kb4A9=5$-km<;ETdD<03&W_GE^7}p82(TA{f zR-b{%>gU{CA@m*-s-aY0Kx+r7ky#nPO;L`Qfkf>Y7ei+xmMDmN*2-p#6I$O};zovU6N~yGxAgaZ8`Q z-pTuYBsf@LivF8O z#^I$UHl6oDEfuePxREHXK<aE^Ndk3+YqPwq{xd2rb?FE25Jl9dWoiSYDR|(quNz{k=z>m2F&E5D+t#-@BJd zZQSRUKBJ>x%3~>1TBzpU^Z}ohhFKwLncLz}5fgcn_w&xq3dvL?tV5uyLO($bdfdYd zk362Gu^>5Dh6pyFG1xmexNN^l87yXX-3CPs|I-4Hr=_P`KbyMfWHJ7vpc<@tj849K z6t%60S#ZJsJMyu$@IekMvf|5UZ@u+}KOco)FSKfj)hDz|qoa5560gby!~?&quaE$| zyuCTSpZ&I@^LQ?DKV5%>P=;D5UJ~-?8E%ur+Du)9n&$2mB0WM*{Df$_T3Z4=))rVf z$XaJelEhHVVAH6sF^!BG3Z9Jnp2J?iJktG?C1m%-yrNK#0O>+^RkoxD!)o^?^7Iv) zokZ7=JA5xP_k1~5_I4iu^R6Z!t%fBF?_<4Bl|Jp&7@zK&ufb~dk z%!+IHU~M)T!I-ck(>eB3c}0-YRXel7cw&KL83gwgM76`Z%SQiNFcJ*6njB%8l3^fi z<<0}XqV;%gDJ)INcuiH)4QU2^b~1c+27FdHks>hKi&qn^ z!AIS^FdV}@n{`Vs^KNGL;qX=(fIvpU`);)gR*c7-ojyxY&5&v=fl$%f)Y~TnzM)iung}g!lH&oh*%v_KO9BGFg$_(F~4NPr4?0 zHS~^yJm?2{VdSD)4>5*}ea~nHhWd6CF*OP5NC078BVk@ODXabl_=<=uL~WGF@Jg5BWdw8WeiyNpRQ=gJiR

>PSdp6jK}JhRi8c3x%8iHf~l09NMpn%B)v!tu!9K`f92g|n zr#bQ$0Z%NiIDyjuO@i53xw8%x%jXuSUf8ap5)!3yd;dte_6>BVJss6E=r|9o@j672 zQe@ev5gMDC_n)qr#ML(5^DI2jm0jZGN$>UFFQ!iU$wyaSB%?~M+qFTiTMb$0z*6cu ziizHHU1!jDFH$;D_pj<+O2r5I@&o?Bk38~Zp zTO;GxXEnL9B*7R~Z*OnO5*euhGl8g7?xesu647^*;Z9-IQBPL~UdOAs3B}nge}}@9 zGPr|&JrG$@n?&iI$WA`pE;BCk-`^wTp&-ykk2v42Ksu8Bz3dc-J(Kr*e?Se7wNUwV zv!!ZXo4YCasim&9;38_5zIu@7b?LMYd3N#&;{bd0wteb&&J`r@@kd2{uuC7Q$zfxv zv24SFd|CG4-iTt`1#X)D15z_pNU)is%XA$hSn&G=|DAv+0XOd-a^qduzxnTE6t3if zL6blIh$ftIv*2=}{(h&lUc2TXm%drr39Xu*1TK+K(fSm|ws2z4(P}Sfo0M+nk33G0 zlGA51l1N>FhTESIKiU(g?;Q&JdY6LGe{s+2gK_y~*SF^V4Vvu*wO|%5L4a|)@@J26VzQV z(KJ7EE4~!)=oxKhQ_)$dhMGRUX#*<2cB#5*n=x?XcQ51*$-3r#26VYo0-y=@1l?|LoFZ-XwE)91k_q!aD-)Fv}#t|FJEYg%Rde_&`n7IOr z9l>WDd(h9pAKUX;J1s$<5R1=FL{h|e;wa?1a;Q^%@>Re-I}4?Tl4>fT!68e#7p_!4 z`LHxP)JZ0zQSy{H%WWiPhg~$0-P?j8<58UsPZo_CA!uP+EkVq_I})#ncE^7P+AS*6)s5*cuwJMOdS zhP}bAkD(sO%(8xWbR?@S8~;c6zEsS35*ioad8?I{T1z)jm$ge+NMf~}?Ac*TRAu9J#3nEzeWMb_9hCj0Mmc|g!duP~B&ve*JEP<%3hz9Fcot(u24 ztn}o*i2B5XQa-NILIzUeU}Iy`Ffe%UdJAKo$dkA)Dhfbret=H%3b2UkzE9)3J(YmI zh(p`A?s2~p|GTV{8-kr`K%TStSM%gf_=en z+WbiK;;QOvh~>-*V*2rA-aF@=K`MuT)!npN5<0mZ=Z8N!p5zGMS!e_^-qmqejxnew zYhG=)#6sab^3ovJmjlVIJ*X`abyaKl1An3ujEQy*ojOH$!>oS>Qatra(3Gj@^^86t zNy5D^L0_p0tNN=~_Hg1UzpOXt_dB1t->#AHSX*8RxMNh0ZqN z^_yJ0zX;te)dF)$^)-)?Pw6`DQsU}=cFbS#y#1AtndvJ-BwL4dHV<5tkYw7 zv~?-DMtI|Q-G2Pg*;pECF_2|0!T-g?OoUtIFlhI$o_gluhiTULqQ?kvAT|u-#N9c< z961Qo^sUK+KD=BrrEz$rp|p_pW4zY53{(cPTlRq83m;oMXog=`s;{6y0i4U^*U=U^ z`iQIkd&8?OSR2_IsUuERRxYX@k^CP?8E6CXd3kw5NQP#?CTP+{KgW=4j{`79l^dx6 zvYDso`PKyz;uP%Q<60Bzm;5Ao58{2Xw4W%t%T88V|FZ62id;{2EGT5)-6mq}5PIhH zx{g#F29l>w6USht;ZBmOde*U5K4p);#qY;>n4-@)k&Q`xl^=9J51{DuNaMZ_%?`(f zVj$i=$1F^7(>?Q&RC!1`pTa+|*{G4U2jryp)0OloyOuqUPL6D*$F?WF+VM6vHId$l z_!eT*>%@6p_9<3QCH=vH9}Ci}^da%T@2fz4=X0~+Sgft-CX!Ip>S!PF74t!%(k9_u za8=BQ&JBLM7G^$wN43r5N~MH_6Ft9GMPXdRf23yRgyJ{F?>!;IR&k767Sos!5A1as zN=(70f3E9K|K!f>XHnga$ON@3`$zpkT4w4li<} zj=_~DY-h*q==g+nXf9DtNE4sBy5ry26y7XXSz+C}8MkF` zlLyeN5~-?(AUQ^Ns{|o#`)jd{Ty7_6D2Zd3%B)7btVp_V!!d&6Dk;vcEk!P%J^o6_ zI(E-d!#47+hI4p8w4eiIuIG)M~YiY$n>rV=NrC*E|z@)r)=y5k{o#fq$>yqM( zMIrA9D)z)*=HdeP_vC1V9?x@+27SRzmapMDlZww(0udH8l5nb84PUJ_n!r}D@W|O= z+L~y^24yzU>V^EAH|{*~!GWjW6-7vsJU@ z@`MLUR3{pcL?V+?l$pvV04~+{87yNT9{y1ts5&L)CV;^}r5Y6#_3zP1yG@en+%xek zAIPYH-ZoZPKAdcO8wL#nY3#;1W7@i0ESo~@Z(gYSs_3M7f(P;9q;SS+OB~0zdRC^S z!p8!()*ht1yu`uQ1EfQa6zS%^QeKYPZSeIwwWBLCiU$>@94_8s%4s^;dt zAj7gdHUQ>M8@7e0rWlDQt`#xZpiQ!{-JupVo$u#Us(p# z(BL8j#_DIH-{)t(ff;N*FQ=y2LWXIepQ}GB{HcGR`yfv&MbuhP+}N~V=r7;;$s--@ zj528Bqw~X}yDynoD2Vc#n($`L&BYHS2n!{=vlCtiSSzl2!=Zh&YNX7=cS?yEtPkAT z+O34ZW|6$hd>!NV_V!YrJZXYs+@}g9xXBAjn)ya|WjX!45BE;CzV+=s4BdTD zoVvT0s#G8a23prW0Ockax#&)VXjw0>5O1~(JU$*@Sc`EDP?7>{%zt}Nu*it!5O-3Q zyysHw(vV^vUep_S>l=N?nufm!C+rnmC}#dw(KOAXbU|C?(#5Nto5vr+Gbd@S=lvTg zvBIo)qAMzHNj-KNqRMcc= z^sdCKt_@b4wyCzmqnm^%H)TGmL$keu0}b~9? zY@YT-B>EXXiP{;Jc2C7C8B(4)2)B0BvDZH)#;aP3SFXeit7;hgBsQS?bHT^1IN@vY}XLhzjaUsZGTZ z$3An|Q!g?-r{*V~jqBO2AhN4RopEa4dBkt!;e^TQYfV=L<8@eDlAO)I1^K?CR3)3^ z&>M|GU9-(&$C(Z0PR7}&( zhB}EK{MmYy&vNndGP!rPs-dARDEtA`DA!qB{vZ2K=CJvr^B2Y)_vg;cIqjg>tc6}{ z3Gikxp`~o{+y1fHZRsbM$PmKZc6s$Y`3cC_Im)?(o#bhmd;9r-a+5C}wfT(GBS##u z7ZUxmhXAfeW<@RmJ%5Oj1^(2$0Xmz!94wN6Oih;5!qV~Gtxe2HNOPHLLS|JRe zAjAp&`^|IK_YX*4v6@qacUptd$J@^@zq&fLad6;uqb3E-3IelqVQX#8)FRgs{iC1` zlXuAmCjx0071Q)=IcY=2BP>N%DfPb*q17L;S-&>t zN#pRqR;LjdsWn>c|L@vm6=pm)%j<=EF=L%zM_uk2-2jpF5E+-tWHb~DYHdSi-eL+# zliSoI&Qg|7`nc|-t*dM6Yae;Bva?||Qz{`JDW;&1xMgBzioBV2IJNHo#R;lDC%G!t;AtE7#YW}WE56AgoORf9k|OD z=-j&#_RuzCI`d31y>rXNrI(@%L20CB;=pZFMI(}_mUTAW96%^7y%LBi=Rg)RmyHsH zLE^YF4!eBwCdIJA%w_z}NdbbIFE}9+FK}Zn4BC?dM^C?&Tq%uzdSM98P7#%UFK?VoE3{N6ay9cQ#2iavy3U)9Ww}VuS%O=xpIAHt&~l%P%d(J3Ks0JeMlE?OMkZCFLBvQ!4P#g#1

FQ{NH+`9huFFa5P~h1H)(kd)Vam43Jgr+N~3zDh8fM z@83S8EuHhn?oMJpJxp}(Rf2(f;&5$ye20PeY7P$EpqwanVBpTD!4D^rX;slXJ50kv zKc=VP2@@v4_8N!k99chP0-Zbme87ef_&U<}539mHC5Y3Hb)^WEOIJCmGpf3h)%3U# zvVf@!7}s3{;$~l|=s{i6R_22aBpRz8m$Se|-({~1JTQTWffO5fd=MZPx)*gD?e3gY zDZbb@2J{6ZFHd$PEbR`$zQrX&!ZU6q$u_y#KF7?U{InKYZp40EYb;iHH z^z}E5pZRZ%8h$1X6O(i=3mJJ(xEjpQ!7@a%LZtX&G2r6n7w?yv?^2I$bRX%4HJHWk zum``o;iK#A0Oq-EPQ(;houf7hhWW(VKS*i=Jb#OaJ# z{MGX<7}zvx5x)*o|BujI1+TRv-sT0fjo(j3tF45hTLMq>n|cGvxmnK%A7oMD>Lg}f zunI(RtD3I!-HjcFZLS6UsV8l0!`(7=>>W+NYmr>n+#C*ag1p6AZO2+58QQr)R+yLx)8ixlAkZ9{eIX`6nO3> z)h(vX^ZJA6ABXqxPAX9Afq*IXYiO-Aag?QWyhg}H52p%7%(<2h47mFTz1_(_pZA@) zONNzwY5akAPM_la4%>3^GpD_^3k;)-yjPrS$FbkG?*ldlxUlUSFANVbmMUhAy*Ygw1MsV2zDUa_3Tv z_`vSz?Y@FhlJTU&_Qq}YYybW1R)CwEo52O>e+1twV6RCnR1I~tN42!!laDb*vwTjt zuUEc|R>OyVm%Z+(6va>)s@ayQ1V?D>szhrc%b1hrEL(*G0b0Z^fzrYKQ zSjaaf#W>bnh$ExyqZ{nDv2xSQc{l3u0!+;}#z+Ivj-wAh1qih1?TZk`ZJvAKpGed& zhhB&N8|$SR>wEU>Oi8v@RzS=N6x3kA5ic}MWfMWAlcQK9Xh3>A(LwS_;Ongm)i;+r z;B^&a3M7yOI;EZ0f8ufG6K~D1*J`R`Rnda>?Qpx__&yU}%hR6ub8`_i|SI0PpAv z?rVS|INvHoik-uUgA5-`io7BQq@z_#A6~Co#B<(y!dQC~;QkHADl2Ez;)WeMqNYpQ zYE?8St$P+7fci67~rA+1P{w44_B1l2gO9m`KzkVD2@+i6AejmIlLG3s1BXNg8 z(n_md%BCeJudbp4b^q@#s^d6+UNWdL6=X3ccJaa6k6-LM-}12_#&DJCcivN#ILVZU zS_xO9+`hlVPyR2C1|MJBD(o2 zIgy-HDmmN16mWHQoj@ZLPPV6$JimpHTPh@$<_7#494Uf_!(|VKHS~C>JZ5Xhzdj#) z%%k#Kt+eU;>Lo)52v8#J$Rz`-Ir^aE7L9um%->=FODW+3qn8f!sU*5qLa_3i4UTWF3? zkH>?*j5YA2r->Lc6a;Cn!JZ_fK31wLh*n(WD$ZCQ?Vl6qmoxoiEx zzbu4$ft)odduV_D(+6e@YmGxoKW2^nzc4`CtzdX}7}`Kub^l+csQ*P~sr$~%H>29a zRfQW`f^%87#&xd98kd3bPbuFhr}VacT;gcC=VC9z*j`lkKJ|pOL`+0ZxJ2`p`Do8Z zgeEgcMNd7VR&~DLC)k%?+C<%Mr3iXi&%9#oGbKeK!nXIi44~*M|I?@ORnNJ154o4Y z?08^TUQ3MVF7PbMS5SSwPP|(T6ZTvo$vL@&v;-DWNv%L$@~f^VRqxI_9~gLp02F1e zePTM%;*j1uk(s+yPC1=A90&i=`86Z4y|5&>1S% z2s%HLfp=&=HpBr9$U*^1h3ig;(xCg47tVWoh+Da#TEK^xJ7K89mVxV#7*cA8ybrBi=5I_g2_4vBxXos3o5Bi8r-v;cXW$(umUog90h;0*u|ZI$G${iJ^NFJjPQ z%O%6BLOmm~lmSqIC~G37M9#yP zG?GI%5lnNc*iFUL9YvGBN?f5LgP*@Bis`vp53=j@t=qqSNEw%jL-KqO!Fe8tqwsdu zxX7c1d^=~RFtqG?{9y+nctDV&K_Kv*d5jDU7>amWZk+#(@Lf$!!dZ~p!etBvjyo-FnH>{@Gk$OryZ z_34rAKxeJa7{2uu5w$0-R-3N8Pc@TgDz?SFm9`eKgWtz}Co}%qJ8)d@%6Wc?4Pm`M zEo{rzP*hhZzB^Cxo60EF5dtoW_%>QAihaTd1gUMypOMEO)e7|qL9Nr^FiM0p)oclP zLDxay=BdgGGeJc1!(dqMSr;O|ygY#o7zi~;5hq(+T6;;q?eW#Te+13X>`)E*Q_z`& zLVT}4(~KV)Xp2eZ-usi1{^8|bm&^68Gy4Xfd>iZVn z^NB+#xidV+Q-7`RY=_yNCZtwrKgV8Gt*R2Z(#L&wpKm!`Oq||T6k3>Pmb*S0rpcu# zozis$?-;!52U{SDGf77tR@&znZZoh4f~6eQ@VtrNKnZeg%3Y>H=jZnOlB_lz^^m=X z&t9+Gh;a)x%=G|+vHGmLWGp5~5ClW9$hI2uFOobqGDpG9^TO9GI>j3$__qq$YOFgO z@z0_#IxYW)rmGHX>V5lTqg%Q`T2#6Oq*RbbR6x4BLmGyJASs9_2&0kihS4R`(hbtx zId~txzw5oOvH!;Qob#Of-1n!BGj2)KZPNbLncMk)dg&DUls_*9#EmlzEHdu3-y`@Y zX5DwQre2`=imk(q;Zp|h`Kqnl3^rO?c3%D`&@;jF%dgk2$F7VySO$iGfEqQQDWaSv(qzGp-EE}abEXu`#p7I(KVZ+*{O1nE=n%1lh zlf*r3hBZ81pfG;&Mwe6IV4ZZq3@R1{|C0|iVZ&o%kF9c9@5W?F(a!j?N4Z(<4T21m zGc?bpKpTZx>4ASI2l1nC)g_fj@x|N8;ivS%kr%vA%d>w#mgt7s+MuakYt$uU!oy&Q z|HyEq4>AMF2~`S`;AC{zh);d1#~{3*_0I{JrsmgvHDtb6UO~Q%52xB&GW0j(fzpsb zIPTlAwfjexbQFmcz}Axy(9y9W@UUd`PjfOh6%9>?_A)i zdjn58t#`f-EUqJ^Oxs)gzfz)CAIB2=(7dj0`O3FAimvR4x!dtF`5q)&;~%Y}r^|XN z0=s$gz|22CTG9dnQGtN8jsJGug5v|H^XWNm%>A2&EHJ-)15sQMggs=6iRG~}@3SA0f6)+`laqT;V2JvZ&C%vSi`?HgTA9CN zmR-jP^y{0Mj((z6m?1cXA{-C338`}oeZKkr&_2B(@@eYS4(_jPey|iup+rCIX#R}Z z?usp-jHUSZX9R4pkhJAa;lz@=(iG=IOtLYTur+)Ly76{~NAfKeM1$p`lP6&5+PyhC z8r}aqec^?gT1-l?)kn*>c*K)rpD_MGHP+S&x^JHAKi#Cg7MM{(J@`}&o%;2QAIQOe ze@xWoH{Sjo26>1EDfLNYhu$FBJ$DjJJyBH?C#YIc>)|lOwquT(7w!1-SOf)TY3`=` zVh!;Z{PZFvn%ZN{Z(Z@sGa9v%L)N}e6Tc>cWzeJVyd6y8qHDWFmqzjAC|rPygrXo0 zxqcsF2qusMu)18C)X?Ru9;Gl`34{${H(yN`P%$!|>tRXzQ0=)Q`~cA7*OiLF+%j2m z8Bvy}S@10>wd1BuB8hiMM8>G9%;M~AKLuvxy}l6*%&EdNKBoEB>6PEuU3q)cRkY`` zeld>s4k8S1p-NXKocP(FEYLPEHFTxUQ7epn#1MZ7vf#yWd)wWG`AR{nrI11xv$Oe6jAeklI8U0fg`M>(?L& z!HW6Z_BvheI#Qb?%e2ev;`&yf0su(X$I_lP<}%G-fI_-+sBCi}9_f_smpoEWFBi=1YI^h9d^x z7}#e2rsq#<5xW7u=wt9#6qHL1*BeK@t~}|Sou4NRy{JMLwj+_;CI+Pqve@&P#h|&* ze);x#zZ(RZ&|@Rqy~pJu^m>bpe3vB@vq$P9uXc^wf?ew#0if}Z4Z85XmdaiitIB?I ztm2l`r-_&O*LkmVd4+^}zdU4?`-EFm6n64mFhVpW%aO(H?1p&gXGKvIx_)pVjO{?! z;!)>vA=BcF?8G?GU`Aly*EZ0xVcw*jiBh@^J*eJVGsRqW60AbYtm@} zwC7mC(nW`tu|xnL2osTJdimtV_EGr)1rz&}0IyO!qjp)hEVP^KaY&3{)89=w||06#|R_8W)pi&sOytN|R!z@D1c z+{`#PHwV$SY{5qRl&$tdBCxab#b8ue_loCW10y^?9||Q7gAjjFT+NAVW^;=~z017n zYL}RDHRdYT=1Q-xwVMB6EQbnxCY)Iro55LoI<9+I(lXrij2%=-9G+ ztX}*D81egV$Rj#KuS5a;*3_g=nzEa;xU;z@_xnZ@Lq1a$K!ht-@;|z2606eR%T8@w zUJB-?MlT1>3*7&yS-|=CczV4K^qd$by_T$7>rPY0&w5&z{D zt3f+Gk2!bG!%oVlRaIaB+u5K3>iNtlN14hA%s~**)Z427hC2X?3HD!VaghU6!*qu+ z$e}SbMELuk-Y1FTx35~1z18d%%^D7N$h2JY;vm5kOy6!Ww%_;xb?IL{4tvf#tuqKN zP|W-2xa((yhuFG?yH9A_565Xd1hA3o8UC-; zwWn%J7+~B7#7OIR0ZRx3eDO?;{Xga0y$Ct`EUO?T#^E2o@t{}&nVbTo&)VD$x!a`E z3?Rh2@8BkA!(mPOV2!~_^bg1K5y$WT*Z;f)u?~9}2d_|$PALVZCSG3D^3imFvXCE| znTeQdcyBsUtS{=dm9@?f?z;LO^HDEZnVEbWRxq&H!IirFCF#kQ4Xt2}ki#o0R7u$oS1utEU%O;6Z-F6sg-wSm{^ z+s31<0)P#6p4rcodG%Siz7V-@>_QxYnbi4MDV1&$`py+?X+-$916|k`s}CF`;nOor zy$h5b^7AkMjC`7xzk~2LN>alm$Z(4;Uj_&P)!8IS69T{=G0w*)vZ2z%qW4&B+v9UP@1Vwzd)8t*N=8b# zRW((%+kcdf9Eov%iz7P3hzikyt5cE@0+wGT(l8f^(Ef9jhRmZ;5@bOR0>Muv5?pTf zYuX-B1P77GcM;|}qaq?s!u%LoUiUAEMdYS5=tY{`)T!LXX}iwQ`cK4EvyUh-cAC~5 z1@!4v%@f-?fEG#TDG@pW3PgOo`uQz`rX}RJOe!f*u)2aLxCDm!4ylJ*M8_ z<~0h{Du4KMGvXWq#ZoynWFl_4h0(6WuLTo>U?~QvyH1{iE?P%;v%Z;wHJgGhI<1U` z^^NAsdN&$FVfh@wA3BeQ`e5K(W8)6rao(k8>OHsy$q?NJkU_wS5*1f}qNP8gfCCQ# zKl)-H#!>EO8b}=zA&fLx?!gGNWHgCZf4%9$+YE~`C>NMDy8ENM$b7Y}gf>tj|;ZO8Sk9_;S>(!Ny`v_r3~s#V&hev3h=^#PzW$R+zXbboM{2w{=g5f3hK^V0=Oe^usRe*o2cQ6bt!L@Kl_e^Hzmr8`t-O4 zi!Kp+?H1gIxf1wSvZ&*Rn57{dCVV^deyxp<$}K>IkLO<9y3@(Vt2YndM!22cHN{F{ z*MtaMK6_J7bc9toL!Ipcm_gEXPIx|-IvH=xpk{ap>_U|*B~q}UB?|qLhAHHY*mvjw z5oGz)PAdrTdB7%OCHMhQB_MqWkZ>>Etol&ZfMleJVy1{V}F6SHQb=T^>3| z#vmMfUoU^kSghYJ#xnBg(4qT1tW&2am-ps`74qBH!Ykud)7$p~a;P)j=7477`sjL= zi4_V83XlR~`NTH0=foFMYH5Ydp_Zz>n{EOuvu#V*8K1F9=;^H-9iIu3ykI4f<0Rup zbmxy@b0v@{X7htX0p34#LS5=4fOZRnrmfdJr)llzKnF9S^KR9IlkJ1j<+xm@R{CkZ>pAX*`fH@H~7WrDP+P z(cDb5Z~^2f<^lGv?|6TZv|d~u|NMMK$+J7#81i_1{lSjxi9yZ<0ZEC-*goC(*~G*| zE)Nq6@qKT3;*7^nuJ0<&Q(t{$o%lYPczNWdRnlndnA6a0g6Sab5(*=JM%5PkhzyTO zMq`={=b6i<`rUK!&`p8rd z>Z{Mx)a)#C1zKZogf6O>b6?H5CUfO~hE#T}#XIzaP5+cp+<_O7k&(;4H%CRiPechBB|o%|q`}gl zi+D3{m|mEV#gOP0QE=-cBlmsJXFN~G_A^E|PqNPY82zcGu8!n`(|(=6VHCUXX(J+t zdf_OH1en-;ppBhD=l-%p<>$9q=zU{z)&m$trR+HKZ)dDwKmyQ>JPX#j5)?hprYsLz6D5N)<)a_K z?m08l5?yGJq~Ur8*M86+a$Gw}N-WXqGQ3$uEKOG}K7i-uupbsQA8m(qx}h=UO*bMv z_WK_;UlU-|2#&g>BgxOdPU*4~F# zQC?hgV8#{dJ67pvYiolzp^@TS+<&yTn;SX{3k#U9VsujI*+vDOSGrW}>`qqKP}dOu zzq3=vMAu;PijIB%EuIqXh68eFFqm#lgNdc&x%S;-DVy<@DVkCIJ~WA+Nde9?_qh(* zKq6XaKORml{cO-ef2AvG`=VswLa7l9MX9@&8}d@0i2M%Z$sp~;Ktz-~+O@p=!Rb*0 zQqhno<(lxsmxR1rwL81pOj;GHkw?}g2fX~>Zi3g_I`KSLqC~!!8dG6{2|o}V1eWj0 zP7~yTJY?UFXV6n%W?5yg*%(sZV-Y2Ob-KGnJ-(BORn0xPSW#n2?&ocYzv&d`i#;2@ z{cwuW7KDYTt*=iW_hC-8zSz{$6B|^oow?{5oX3wJ%YSE{jG0G22n0ppe3vZB4N?v&c^46q$7*Dwr_gqabS@c;T4dh5s zU7@@^iOe|D^2!DT2{~;&PkGgc;H<3_BO5O!6KTl$oW}h*@<5UVAsql91$gP z1!G*rdUew=2rDGv;^_K7YQRbJ>5ia0v{5eP{j z5)eZ)qg10k+zmY=EqcooZR#ECbSNgnEQ3UCO!HO29p!@GX#-R6;=$6hi#8s=Uhlu0 z8%wG>d-DxS0Uy4$XpfZSdh`DD;ea)PB1lPn(s@WnLnFtX=rO=E8HAou(s%>}6q>P$ z*-l%3OhsKTu<=<`*_v7tVd84Mi8VO&1-r85$@2+0*D+4={f}YZNJQJj-0peN zc@jSH2RE^(vNC6n2GN78)!mtzVVf$aiv#1myg-}ZWD4*$Oo@0%x4-X>tab8yIM)6A zl|p-3TH0FgQ=35qaZa3q#Bs4DaZGnzwW8m~hJ8YcDrJqsT-(vnt)b9+J3IXNc+#O$ zlvIpv!(3~24fnfve)}l@wSVi>l5(3?JE?y8ucoJ>A3TQC8AJyxhjScu^7!Jgw6lO@ zc*A=qO|5!p_>!%YQj<#jZ#ZID;S(jJyV|YKN1j25WJ}BH0$scWphF+?`ivHyJ!Ktg zxGwfc%bobr{eWT=*qe=O^m~}N&e~KMFZm2IcrJTg_${Z~7dkWoPy`FWeC(N_VQQGI~%J<45Nzb^4B#bUZ>deebHS zWfNyj&|g8OdS){N7*{@ru`sBWt~@d}FU<|XrFzq9K)B-|ZO-p&WN7eCw#h&XYUspI zjK7goEHmK-NveX!SZB=a>W~uNV_=$Vsa@4iZ>-eOexkHV3wvKV(l$i|4%It>` z%?C;s>5ODrcLa^wlpl~>MkInsDvusXNH-bCS4=%`n3K&N1r;^0x8I9MyE(keBvfe3 zN+QsVIdEi==~GC?bu_+)(fLa#T}pxiL_B&+$s9OthiyQB+|V2c#E8n7DijSaJ?c)y z2TRIv4sdD-CMHeF5x>U1ui5;KUD+gKbaxsxCRZ>eTQOE?Ah}IB)F6#mp!flylW^+s z^4;AseL=FQm|CcUd$8Jq_^S8RqQjVqZzGBDX%0(HH*rKKx)aEm338~3aG2mKx~4q)`ypMS>V28Ix2<;aE`D|dGS5r^5y>IQ$6QQjwfpuw^J zd-TLifZB}zof*G%mIig!s6v*8bw`vaO+V(=so2&jO@0*(Q0e85apfoA{z~8nN~1C) z68YF0>WIii3ntd9b0JUo^)(Lc9M7d$1tpB||JMRc9Y5uHQH!u@F2Oip9EM%QL`5x$ z1fXj4@M;d=>@Z|$KhBP@Jlvup(C2*5*xbHT$L0;;vc^!>dg!#IhV6 z48|526`ijA=+mO=V8P_thNUAJ?wi8-9}0JZqxLY(~tkqy8p2iT6dUW+9$B| z<)7<_Py<0>vCw!9wdKjO6;Y=}h(_-hKa~fcGiP>$b6!8l(s=L%Q&I8Vs9;%LUAXwM zhtqKv!ECLUY2Ry^<3Cn{PMag<|EZ;2G91_E?6W|>`a?9jX1=~MAv<^hwnzlIvA7eQ zX{dBWK!DQC7pLWp;)rZjVsx9&JAf?`QjyE&>|{Zsxqc>2DK$#zJ~Z5bDRs zj0%Fjw0H_*a}?i}|KfOp{9M3bqUdjsB>m3`+n;0Gn$YEB{NSPp6!RZrGzCdR;;Z@z z`MWl%R_~y&2(ymEJ;^%VNHQhJ4?K)~cFjfbW;{0Yq-9c>5F+T-Bi# z71X_$`0XvP)t-ifIT+k|&TTWOSmPDI`11OnD)V2|x-z@1-_r6rhem^*LUPQbqU4&LtwEm7(+?zb6w8+29JoqvUp2-HgFmy!4pIn#}#L7%D-K zfnjQ*k_R_f2M?QX9KQ%SVL+uQ)f-X>|5X_9Keub_HVLOF4=&Sapf~17ZO-M3GSVKi zielE5x*Y!6z+P|2xIN8J>@tSO-S{1*e8j$d6gmz60+m4#yTRh}Jc;7MZN5|;lW)*gReb{1#~YL{S5)Ey z9Rscnoeqm4{Jv+GzM*LWpy%1?mo_j`QF7bmHG@WN7TgrB1#(Xrvi%YvlRPMWOkS$h zT2csyJ3TzH(5hygy1qX@Jl zVZwT^x;AiQPjx8jrvI_ndPX>#Ynw-Y+sRiNN4pnpo%D0O@P*NI5e3XYuiu3~4eFAh z0+dRT(NPcLIqOySEJJ1X_(t3artH2ph2>jmNx@S$v%Xpj(r_k<3_H~5r%{i~L(Y1< zb94XF)6ldjN!`dupiTr{cC2JH`COsbsMsVrj$k4rV}1LlqQT zthl1@cQ%mzPfDlkmfJ!i>?ATqo25L?ZnUm{j-+OyzCmsnA-Bn#-S`9qOHwztcV>gq zYdI|q{<5Xz<&oPW#ZwXQcuNF-z3Yk2SXuvJ`; z{+lG6$ZzAB=T6yZ*k*lA(={8KMg#f*QEUP!ChkQe!SPX)H~qr(?fF0O3@b`*AAvNl zc=top+1Ie-L22E)uCW3wpQnuki@=XE*gPela7MOU@L!{jWn6?qe>J7W|Gk6pW$ zHR$7)>!3ZW>0OHX#se@2f5O9NV7sVrB>`0nXqc=)n4VT_jmc-y>fjY2tI0=>8Aw=* zqX+pPCbcY-fv+Q*U>XQSG&P+|U5BS=F}(KvfzSs>8T5TG2?%d4cjQE++j=`zFae6B zD`F74%Q#fwAN>{It)E+1MC4V;gzvR1L^_;7(l_k#vf-B>Doy)$OSjaS^GyquKcv36 z;aacUV~|z)^WO_Ld!z^Iemn#M%Fsna^PRX}EwSIGmExuw4TmaSP0rW(+)5C!)Nt;7 z{R_RNxkZP^=)9P?DvIoSORM?nr~3xp6ygT;06tvWZIu+daQkb2v8-G5!Fj=+!G%4D z!**;uq)F=UD#i$qg}={@>I}hp$r|6Y>f#U!8B2MO4`B3CBr;kq6S=FD^DC_y)rcK? z@<3iJMUFce>pr_q|JQo8I@7;m*(T}mtV(aj3H9+oi3?Z$Jhy3Rfc$T~jn4OZ=4~7> zuC#OLGXiP;?@+;hFOe2%wUC9MoXPzea}Md6+jo^sVCK9C2|z`7E+29fswuqVsNI

K35|IsY+(I%c&*uc^9w5Vce(+&K_Koe(3XqglO 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

  • NnU*6Kv5~Hyu;EjK`hl33$K_I}E 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 literal 0 HcmV?d00001 diff --git a/examples/positioning/weatherinfo/icons/weather-snow.png b/examples/positioning/weatherinfo/icons/weather-snow.png new file mode 100644 index 0000000000000000000000000000000000000000..e7426e3cddc7d6352a6a2483eb3bcc060d860c8a GIT binary patch literal 70939 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 VyzTpd{lCsvMr6R@QFW__#k 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%0DYr^=+;A+oR!^FXBhMu4`BG6 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 literal 0 HcmV?d00001 diff --git a/examples/positioning/weatherinfo/icons/weather-sunny-very-few-clouds.png b/examples/positioning/weatherinfo/icons/weather-sunny-very-few-clouds.png new file mode 100644 index 0000000000000000000000000000000000000000..133bcb29f734312d7a3e686acc0ec90fd1274210 GIT binary patch literal 65731 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 literal 0 HcmV?d00001 diff --git a/examples/positioning/weatherinfo/icons/weather-sunny.png b/examples/positioning/weatherinfo/icons/weather-sunny.png new file mode 100644 index 0000000000000000000000000000000000000000..0fac921d7bd2d314d127bba8ea93e8aa01de0845 GIT binary patch literal 59084 zcmb4qcRZV4^l-$kt!mGrC~EIrYFDXRtxc$Ji`W!NP*}bH+@rq-0)a?0)Ky-BKw#i27({>ve4Kj!xdJ|L z+@5N@A^`sU2yDZEYeJ~Hi5m#?Irr`#Yozk98yKW>e`f5i?`-GpW#tM1d3kv~ad3L) zW@80~JaKllPurKJ2Y~M10lji?e-A)FtlZy1T={L@L#$k7UW+6Ew;AtlD|xuTb#?{4 z^s#bcRd#;=-T~su$_;gcc-T7g=$fdj0&w(qaLTU03=Ymttgjp#As|gFCl4!EA68*O zL2;Vxza9V->;GWn;c5c`y>)koN(%_wq4hsg**M$&Lri%}qaOeTX{ac@@=o7v_Dbh4 z^7wlzXTW6VX=gw7{^i7D`w}qa@qfOP2-_~E;KU=}4K=|>vVqYoFj+zI-fn~tslx0q zmUemc&HIvXduE&*!_ z+zsrL8#e$!Ism~wJ{973IYSSgrO4f3R~0QI&>Pg`Q?0nLo5&L(oTdv9V@IdcaPsgg zzwV!nBw#G^-x!1S#2Us$pPw(zLtLH?$ zM8?!Zu2BG{oc|UfU_YX`)wsL%`F9;Gb5AQk|2~J#83Y)^{WpeFr)|Y&3tZCu? zTuvBZI5EKR1QSIEH>+Ueh=VIwElHflP+KJn05$!03sHX-yUP%7Kn1zF%Ubv`AZIR(!Huj zLvpysr~xT~JgW2=f6D5^f4h?I#o_j4@k4hpyL|-jlh^5+^k>IOte0ArF0L&s*t}E5 z>hl`9dI?*>ci44s7nCeI*JX%C^(G>#Ecq2(?b#Zt zkgwyman&95^%5LH?;Z}~b~bf;T+XNWT_(1=q&<>?>Yzy`YvNS6)e)eQ;NJ{SixbSy z(jvd@*e=I&J&udA2aoq_Mr#3*@7+q2@PQ1o4WF|eaezp0`OL6$#Gd8nK|J$*BY&)8dWtn%Y z4RBiMZZXjw{)Qy*QU8fB_(Viq9(Jm*g4=7ch6}c%EWmnyn*tzZb4P$fj7I<1Vg@!Z zQA6TU?rmTEo2_5@-dHcQ3c33d)b0dzXim@gUiugAD#_3_9W{;@KkyZg)%Qy?l7w=*H^Pn;DWF z)utiU7biDZ^jVlB?&@wH(SP&gRX3DBUHYq%%?XhAD6H#m>TNv_=(fe!JcIKW9s)U% zHyHe&q%|BcRsnb0_?&_Zg0ok2IwspEiIcnU zx>7T)X#wopr+;A4zNG`;B#u75Ujbq)yX%DPut^2${n`Fz@t1*E0a|Ezz&u9Zna6y( zA zIW}tti!-afVc?bLjm22TuTc6muLYnZ?;jmRX-51wpC;!7h64O?)MX0Y(0N(DYN_ef zzK;0^*SFdv{3E`{SWfTP2xJp#fMY!c%7wOw7Pfl`#;nL z%uX;vmOiSabV>CxZt-mY;DgufTtB~Ti2rZFXKn$6X_zvf~>x=bo{uZJWke_h){K3?E@&?Xl2&L zT;m8Nxgf%kwT~-UY`rtHW{LGo@6m%h6}Y#$E3pYavBt&u3*aS;EP36?vW_LVK53nH zywB+k(x!tp+j|u3lS>XCGy-}vOtJ4->1 zye3rf>vP`})Bvu749>PFAV!co160`e|19Wc-ge@UEvnf?aOnJx8oj;mfG|>Vc63Xw zO2&hJ$$G!BsU+UWkP5Q$k8|8Z$fakBP2ieCPfS&kuFQY2$@d6KZ({xb4*mVP%mm)! zTp$hjwaDs-kpTiL+&h1o|M(HatNEE!IZp92?>0CgGss>bd09GBYPbK9HwfBf|9a&t zJJAaWjz)E{EoG;gel(k!`VZN9DW`Pb#VE!pfc3@RI?OqK4#{kB{pfnz*tiX==3;n& zy~NUel*z-iH3ljlh0{E9h?$-AOcF1XbEs?-x~~UJR)2nt9cX$$MFih?Z|YB>?%GtH z^AXBabuY|9HL1oDMqP0;0#BdK5>Eak@uQ5!zRZZLRPNTtS|IS!q)gyw3e(7~X)|Xi z+0H{Pziqs~2M{wIFiS) z>C{`R_-v}U{hH>SsdTZ@zVce^K820;&xBE`q5SqFp>*Nb*5)+Dvn8XM2bGxc@6L|T z0)R-2DHN`qAXP;4bAxUAKApk&2)sWZ{mB_e6d^|Ti;=-iUlo=*S_M4>p?58<-D(GY z8}jR7xK$vvFBe`(KDU$$A;RwJ_PhW|oaPHy>CvmofmXxciL)MWjet#@1X)~#BNv{e z89Gny1@*n^~)vo}^o*ZcZ4N$jPCJy-&Nql&_eoUSnw>YxQ&%j*W;SJsMUoXG)kY#{r?bDwJ zTXY{*P*uR#=Q3RSpOs!K1+~0}JkonyYG$>Jxeb5H+i0mn^GPb_JhM<7FRBrLfE|!U5(y8j z!~|B<>k+GQ_T5vcm^{NQ}d^2imVIEV$cVzKXxe@FKhskCA)~&uKxRXbuJc*-~K% zK~ldMo?V&6Unx0Bl6U40BJlli%y^W;xBKeAkd~e&qDJ7BC zFu=D}&uq-+5hX+No)!fgbp3-Zh*jSuHc2~hP5=F6cXEmNhGD5MD-1;qv2y{qdA{w; z$`_I7P{n4?KAkJOPG-Oi`z**rZb8k_cNK?B`9p1wtN&qK8cRlCz-3H>!rfI(x1d@eykHV7N9<_j`MdZ%Yp7A5~cjI^!L4Gbe*3j5I=PGh^s3v zJj9+fX87YFeZo`5m(E!lZiHFlF)kpn>0>ivuApRnIV^4k!kNQ7DhFGBE3~o{B+5(B z4rr45{JH?;-bd9i)p*qt&OH>VvluNA_=1wktb{^=y-5@(j*47q8EEYb3b zqpQX@Uazn%^D7H~@6o!+nKYQPiaocb3Y2|^dVjNJ?Df3xSpn!#MB|e^cg+b2^_jUm z`Ak8mR(^ret)d+z$~46z2c935(cnp zQ;Srg1HW{7TMLonZ-EOs#jW#kE@SHwwOy?r*ea~wxX%9i0U0PS(j&q~%RzTWJc#6-C z(ti6>lsTWFiB1;`NDvO%K5om}LK~|6pH9;HMJS@{=YA;V+z-{k(iR-bA7|w}I=PPdkdLM5$%7)DfUAABcrF4cJrN>JFkPH<@SxV~tT`4X_swCHi{K2TEm%8M zC)M2?_4AP_gLNUrCCiZR;%uHV_iN4_<_YH!(T*_?my92wLm!JQHd69Io)h^rnVrN` zBgyX2)VVoLNK`+$;KyIY^m9s|2T38(B4O2JR*=x%48hLS;a8!%&dpLihi*P|FPTN+ z1j6SZ<-Ig7pNVs2h@**Y!o93q<)bnt9rTEzu&$tl#zDQ()5}`#FLXq*l7!pl z_*T*zKP1wckTEKl$5IUJ{Xlb316YG~Dh>%Po9fi%IG~}49tTT+7{s1bB~U}fsWG(6 zySgsZ`umj(HlB3x^C|D0yy$$rx^2Birkg{>770FlcyvYP^9M11wa*uda!<;u<_M~) zH>Ee{>*iCnB~0#OjU*%!Cyt#3jMf4U%EaL#iM?I=ZcYuJ6IGbHj8r~jxmF&k9MhIi z^R3S~2M@`iJ7a2W)xc5UYxW5xT)M}|JUBJM7^J$kKTu9(()tYbC-Xl)H^urhXwRr=5b#A zZm=#^IYlomNB-V+G;tGo>kAx&C1!eiIzQ$15v>^L5n+LGJ)NY0A*)Rr?g`Sx>Y99) zG0>H$mDh^4!^^RF_V09M(Pan_e8*Ewde%!$(yq~Ozj$pJ35s-uzG4`tST5`+7ZBnr z93CQT4&{aYSv(a<%m+-yCm==k&kyrs)BAhjX}n)R!p$&DFeQ(Z_eW}igORwo_MPKW z2!C1aD3HyFAV}WPBvcJaE0yBRq&cY_$|kAR`phb0pUND*J$|Iuo%}|4((yVeKdVSI znjZ8ytg7%3LR`>!Z;fBFjObS;D8)u7Xts$MDidmT%}QKjOF+96TKdlEmwD@6gCYH0 z7V^@xv9qlKE56P;wSNZRn?Z$tu!F*<*F=3orI#^?4$X{iB0J|8IwqdwJOQM4%uJUH z3*Zigz1#9bQ~3;T_ow)LgEj(J3W|7jaCW;x7&O!pCDurnL0kijeEzd4#rnl`jCFvWU~S42ys#ptPO51C>W#L#pdA{HqUb525F=H ztWoNI&?u05Yd?Ot-`&1QE7a2&Q9TRYH? zgl{2H{#qM9`N`_0?lQut(Y<#%m3&TMH;NKIdNyEe2=&Tk&3-*WiABMOKIQ+z|GR#ykz zO!e4vLFQivi?E^J#grA7eAqXruSf8i{}h`>2siUM^QH4!PXwjUzEpM;XG}qDTy{O3 zG4x@_3{)6c?_(Bw7-7WBI&?P-CuY|_9e)*NC%8N#LHq)O{n_aQKU`alV(kJFdoMP( z2`i5l8TT>Vu~@nbQm0#{rTt)R$S^zfYt2W5s<(luQw@#r8?nK1AgxnLA1qq@*#AL2 zOyozXR4vU&D>uF&^)H(8jZ?)90^|=$u{?Vy${%dbc({_VB2>HX7x%99_O+J3{_CeC z2p+^6Q~w&Jg)DCOZqww&@aDfPZQI3vPK7&`!KBHQc2I_Wq}kFgq66X$5p*QfVM;c$ zUh~{RoH3czm2m4PlSr88*lqqt;Uo%u{~;J>V+Q)FvF?k-Ok8b^@t#pYGr52}5_|-| zs>jFBj-j#+50#*l?ve2=3EuT@D9g~$*sR~_+d4@>jnfhOW~A)2DqJ@ua1 zLOo19?4BXMHk}BXd>=Wl3+sv`@%H!~`QYu|WV1zF4kYnlW9ER%{u~JYSL!$WietZASej@#4)!BQ_I@W~Z7I$y239vesMrA4ChgNjmQz^v2`um5IC|$TGI(<={oJVY zI2&2Z#Y0lH%~x+cvKf$}3?9lPG9zj-231DIpv@kVV3`ga-cJtj65=(2zlAf&DoV+?`PjBRvxZ+v7?*Y{)6t{*nIB`OM*?DI~N^-#K;_ciAFtn9`32| zV*X=C%3UNSx0pMhTId+3aBNul&esmt{xO1R19swvuIcRZ5-jMO3L{Y!^gk}cO_}&} z-td=P z7-dT0p5NEQrdXF%Q#OxR#NcoX;qYQpCoOb6!5>z}3#ekSKTxysZI!sqLR4JXZpe-s05byPW$IdorF+V7S=k-k0t8 zf8WCJ+C7mJBFevIzSvTtjk4h9>m@QAo`@B{A?-^ZYvL@7BC54@^5V8oonvX-8D3;*y)SZV z!{H?#&2y{!`)f7(rB?jhW%6>Cb&MbJmvu`o!dfzC>gByzbEz+@&pz7I4Wg;hDX%8Tc0uN-*uKjrjRkeGP69a~-G%y%R}@Z_ z@kDPq-&x4V)eKk0?P@HbTbij3c)N|J@W}5=l2tET#D=}vr)x`=A6TMx4%Q1iMs-Fz zp=7#r$&gf?)72YZLUta-2u^65?6*DRVxKH)(WNr;bruR);|BW>p*$|3j@Tey+Vd|> zoO1jHbQ?UTRc$S>*%z-J--@qL5vgGn=$Fg|^}*<>%d@p^>XFPpq67ObNtZO192yIZ z?yHF@H5eQ7K<`8Cv`A{+ETAzfjOuv5GNaZE1I1cDP_dA1M7p1-`GCuhyP<_8e=FPr3$Bw|g&Vytj_N7{e2}-CxNtj)jP!l-1AwjJ&=5nF!Z?vi3mm ziEevBW>-Sy;TSPlN0Bo0dreyws{ri)|GCX9?5hRdh-6ng{w z-{TM|(|IixSRKH;x1q0xF@5`6H#%Ae`B74UBh*W`Uamr3R)FaOPBz&t@5h(MQq~a* znq1<`VOc4EeYgo&G%L+p+jN>ogn9(_Rwz3j^Qmf=r-k?&M zd3thf#m>s}Q=y%Jpchbl?c2S>svw8!*xd>(XY`|;tmGQ_ran-C%I98x{%(le=94~z zd6HDE+RDQpN4O^@`|S%!#@3) zwF=D9AJ!=U@~G}D4sDR{l3V#LiT6V(nq*7N$o6z?VIJyLH!gr0kPx&d38qu?##Jq$ zpKy3dk6u3MHoP?YL=fD3?@7J0W#4dMl`r2IF$=l!$bSq!(|BQ`xvIfB70`RXS6NCN zyQHh=K>PUnIQx~pA1pG~Fj4#{Q`sJ-0SkwZ%%4VK1#Bz!d+$z6F{1&tMhJgV^^h5< zYwqBMR0_)wmhRJe@C#5&whb?NCY-uHhs6e`s}U38)k+s8dP1kn;Vq@$ml> zx$F4)(x^WLrwrL!<4dj&8xg_5V{-OrFI_eqBSz6Jmqs}{u59z;zj1h5dB}de*!g`_ z!Nb*8)X|4(n701@Mfm*K?C8uN4V(W>UwaJ{)yDRW!Sg1=)dkAOx+IGoHT%Yoh8v)vfi52eec{R-AGQ^a!LZnlk`K*pos0#Y>KuW zyV2sUsuurqkWNJ$_QVPg`rXmPlf()e8mL635HxFkOy&&rb?VjMCmmH4iK#@Sn9!~3riG#tpx1(^YD-2Vne7ZQUq@_)B zS|-2$^VUY;Qowz5xrkFwu%d@mvOmjY%fybKSvjY?NTgwvJ@$fLV+591r>o>9*(kHo zHVOV~-O3Ld*rHB;+7m>Xr7jC6E7bVb%1vpquLkJz86(AzvjMyTrN8z@inDR%u+i9u zdxd}Lpb_s3gDz=H)?WV#tApK!$=dQp+!B=k{LZ}B6SWqksstspfZ{9IOXQ!@CRjX^ zo?vIc#0qrH?oXiLRWd19h5{H zGQLQ+wqiRpUkc*zu}=tJ;#?|1PVQzd@KlophrMY!H6l4)d@H)a{;ut~$!iKc;n!&n z77qQ(=@-p_)$KF?W_oq^I5S1+UeV2V*!fd^&^iskF%sAae&c;>&tNOxsSg4>b-VBW z&LiODI*5Fzdg&YSGYar;$q0KL*MGUi0Y2rp2rfAV zhp~rQcl~wSG`;}SaX#MmW;w-4L5jyLv%|MhZ@~I*f?2R&(cjD%5`Xd8HUIvpv`b9gDk4$EIy8upVJm15aSCfhUnJrTUN@wAkvG9zy5 zl;o%8O?-EIw2{S6>?Ot!SC#~_w%=9*zu9PI!?qMYZpnZ`$(}lBmd^Q+Ufeq*0jwd) z)+=P?&6mamg+$tZeI>r$q2Mm9xYdn7#~$vh?{8t*Ga|ZDG*I)7gv=uGE8Z!s@|iLY zmpqWTqzv@B1$P+btM7XxDL!$1m&}$9tEJr4V;XUhW z85Akhk&KD9T*1TvjSEF#wV-ODA4?=h<^ge`mgP7CjNQavZbUms6Rxw}xfjUEr|s%C zZ&UmC9ed3LCji$dymW)9@O#SgWa6*Yblf|&pRtP6jKS_t&Ztys@9!d9v#5xZt)SPI z-U5sZG1Dme{e7b)_(nNnqP}CfxH{ zjh6m8bZK2aYPCPL&m0@GO#GqeJKGY$2E%8dJi|l%q>W*3QGK*_z$SRg(j-?w#K(47 z*tRWRebP0#inx*}<#i!;PK(G>h#h%|7jl^?8M|zGANJsU|38oLx4g2rXtqO^&r)(r z)JA0@y^TlJKME}O#CxlKZ}C*46r9%tYH@qqk!Zj6zq0SC>N8ioYJ)U1{y+wM{%d{s zN40ZQ!Ct^=v9(iI-_;S@d1tlVyadt$(Ra3U z(1_qfWD=q-W+$kacxqZNxYd$pB9J}ar{8MGwU__6HLT#F2RG)MuzujE)8E;E>Dznz z{V$XkKbCFy5?=sb990?)g9aaOw5M3F!L1-Pmh0eqi$8=wV^q}` z*{+uMpiJPJK;S?{_Bd9JY6urS9n+NT@{)gSH`I^fz*;z{1 zNdNBq+Y{648;v76nXYyh|<@c?)cw zon+TI3r>Tup(5gBL+K5dVNcmraH8H%<-yW`d=?R?!gI)8ym&eF!!NDdOI`9)5^KIR zrcmq%I7c+}t9JeD0;b!D%SMD12>o!-%!h2<9~kBz2WMf)1l6EbY=8X{M0uCbbf3Dc z_IUGPl0WQQNuv4edPM34Zmn~`#0MQBILw)0p$12HO^DIU)^Lc0RL*V*G9C6^yWC2N zIcDc@KZUY;o@k$q{v}%elgup9IY|^+Z43{e@|IXGjP6Rn`>&#=4%x}~`Kmos+6Bkm z@gT%jH&bw4Oo!1SMmpCdGjw&6_0r;LC6{62&ASyWQ|&DmZV4F(7m{*~lZNyQIv(!* z&0-Hjvr0<0?7Uahdei)fWkyt|;x{Rp`moi3lg0M|5KQ#3fg3!F*>zNZz6gx5;gT{g z(|crmr1mAr>JT!JnYG$-D$2-+T3@TfYIR1P%8M)5VB{B)cJVMu2DIX|43iysHZp4)5=pm z;`&6*^6eaTm?W|eF+3yJED$rPoWniuKYg!1@kL+_-!BktIMLtUwg_2OPaj?(dX>t8 zi`?7q=~yiktkiaY3(UVBvg2EEk7!*)&5vCl<(r%TGyEaM&W{=G888vwq;_&=mXbX= zZnfg1QuXTKuQK8I`X9jQ86>CeU@x9yKSB}R;|G2z7d~+OC96jNt*oOj@YLVE0zl35 z-o^c;C?SX6LrS%SZU@+vfNiLtK{n~i)5JU&@LZO-mQWHLlCXn<#I$^?v}+Fmh^%V96SC3>M?)wKEn7AJT`dZ zZ9^T0@B8pFsI#id_HKsr7tQ{;?|h(Q2M2Am8wW%oFHcw-kwmA2c6d=UoWB7%EcK!n z#U7$uYhWADl@jfXWxiqf^}f&aY^bc`>QNv`Y6lOsPUrPQi&mF$Gm&pyWTjc&Di$C`d#rx>n2@l>CksU6 z#*dPRk>MG%z7ok-o+Ia!{w1uitt`0t2YQiHN!btNcq_N>am_0~)cSwaFkLMa1V+y|}N{lECf$UWRcxWK96em$0T02QLZ?RGmIA9Abs%X1r1-_`Hf8giuU!K7+{$BQ$hPGBwK_!-T|%2U z8%zDrA=?(ga7U03f@FfRd=oLQQAjZ9lc`NW5`RI=uyl;Q;6wh@jBC^xdC z**H^!X892J@n}HlZOAey1g(5$8@YZIwk%S zP!sMk5;%(#2KNbAhp>e9kUq7r)(K$pblAE!f%kv(IZvoi2BP4o){$O&%3z&jY^@W3 z;8Zn73`Wxl4o|V%D*UVW(KbGS^AHW{*3iZ5t6xKk=Pag-oM4D>ear!V3$dtz=y0`C z?Y!0~HXwZ=j2>)js}eShUg<%Zjg8g#nvGwZ80IXOj8ZAdEGFOKh(ZObK*EvnyVkvY z=TTtZtlx%35DMY81K~QdldD*07CIc|?7BOJMuuZ=8L;M&m9mxGvZBr%$3_T)DHaulm<;;tUK4QAq})2y7$%|c%%(DMZj zrcoHBJw)IT9;?n)}vJNE#nk8>ZDV>UP z#*4>L1AP)>x?T^&p1|z8gLZkJ2LlQj-^LSd0%Zu41$;H(7h*Y|8~#xEES9qgK8r$V zT5ytG+QiMd#3@WPGQaC$$!{V$nG>I*>E?U0r?lh+MEhk?20<(`Qy;rXRHKao{ z79R3;A7-m$k4al8kpexRq)YMo_!ok-za;<-M#zx)3|;J?**DNd`ZxRKCvFNIvrhB! zLuHZrd}yzwvH)BXquTe*omoe@S5=NvTxP`cyggndt!@M%=EsQ`olo|v+q!`UPFHbk zTGd3Ph0zjD0`or$YM9F%Dzx~o+uQ>M!OJmeNvKaWDyhR7;y_)utA+p z)KQn2w0OJL@1fdln>XTEP0vl)&0X$$7`Jygd4z3kli@92xtnv;KX|~{PK*YJmB=eU>zDgHhuly`53gfoA4rvgQc8vPRtY-Ftne}evyC*EwnCj_ zajwP-72nO=dweVEtayJRFkBS;*fv&WW*C7!vRb~7_V8hQy#B-vrChHtkLT21H0_Bc z0YoN?&NbO8;$?6C8VA9JR|vyQnU)x^%%mi(oVYK_#IF_;GDBLL5C3N41W^&^CvQ!k z*SPpAK<J^{fccJFGFN{YojqZpDT5?{6{H?UibI znZuhPS1p2-(yWh}k;f9KEvKMdnTN7iUuaH5>W61fxf~k4CHwAq%wp`C7SvLVfpkja zJvz@P#Uy=NVu*Zn+@wIuWk>4+yp_`_pw(d&6|Xi zBy?Dqn(^=fDW;9|fTV?5lP;FWZs)7=myQGHM!DB|3eCu9z**Hlk54=>@k%vdMk}*> zzA2%x6jfF141u^=nn==?ukqjbXS0~UnA2~o`Q?YmyyoH{{3MJ46r}?y{y-j~VNB13 z^f_V8dREEx0n#0C6Lh#@r9G~$V2k6eb=#+oIRShxOxWo_mu~s`OyOJ_%3FuX@2O<4%25 z?87#tI~n%~=t1nvpTxhNFfNk!ess#OrZci5-o2(9Vt@1IIWPL5IdsXUMD)`~w@on` z#Vr)F@09>yHElFSIYmWNA>z_8Hy6I?K|4c4RuF{|n%&KGgiD(reASj6bL?>C#GU;$ z7qdAQ+YKrs0h4ymM@ppfxApty0(xKgA{rlQxsGWcK9qQJVRF;*g1yh8Qy+cVus~z} zDAa$bjJ04v(6bMI;W6dQ#W=UY3qQ)#T0a!>e)8Y_rcIVaS7pV`-nVZk{~#zY1Xx4G z9@`WAAv|Yye}Wl~Xd+B77$?LKXQ|CG2yuXK!V_O8uQ+C>KFX_Yv?F&rR1j*!2%O4= zx8ArTevrvE3?z@BUPHytQ@tLlF~7Mj`s2!Yhz%q`6^W{!%F9|-*`jV}XeU*if3G`V zJDPeokl|^0kHM6e_CxoS9a1QxZ7QsquFAAYx3Q7~fE2$Ko3fQICb-oAN*bTLOoK0k z9iXCM;C7n}vEIbMWF^jlAX;Me+0|qge6mlmg>nw3^1`@Szm&F-;P%*D5-mvVT=06! zAdXfp=^I*m0Pn=ne7grgTf>;Cj@92e_f$FTQYbGKnT)rE+! zsGR&+Ot=_Lim&{Dl+c`|dzw<&)ATt^7GREW`<}npVpwtm#FRyJR*`-I?`a-c$bD?w zW>8pkHDqbcht>yM^Llw1TUY0*>Pyy?+?!iy(^bj;l2z-=tD!m5m9@iblj+NZm$PwQ zq(=5I#W}j_7|vB5teVi8!N7F(M*Q3)$tuHTY>FGcDahytI$v0h#uTYG1W@<_b^3O8 zat(FEhchCt7EwFVA8?+cikh$zeV4NyU_Uw)F_fR%Og>3{Ls(hisA|@udlD5Up01pH z^F~OQFS}GGtw2q%~&&EY8L3c2(DN{1MHiIZ9j5D=<+F&yn-)i z5hE-C@h)!@oPVt4HeqR;prXCOE3()wkh9)LrjzTqhc8x|KyAmS62d6(TbUoV=C3W< zA^V{H`(9S&&OC~T0zi-yTQjN%e%Z3xw@5Cu(=G%u=sCnPmR;k&=H=W_@NNehhij5& z1}Q4QQA}l_-jZ-I1bY_0jbFL2%&t?x)slJ0^z6u45Y~5lBbtMLCY#h+hK4M=?h-9Z z;q@&BW4M;0XPf_GDAq0LQm!|}*k``!!R0aoBvS~0p5gUU|EnGXV~PcYt}pw6N=7E) z+p%s6VV2rR6b)+*-;sq@=istjvfN_#ZUar9|5@}!cznf7#ZU|BPIc91>G*Ev*jz58 zZE3^bceE$awMBQ%R<*4-8nT39w9wzb@OwyvR-PNsw9m z;jtH+fWOaKOY>7l6bD?Xl#+VBEd&na;6|6H3;vLJlX36+>|8>xQRI!(g|OXZ$#0Y! zy&>8FLhU;6dRO8`cwAqAL?}iTX&>bXX(8T^dA)wnewZB>%1f`5Ie2=Care9Wt}Xr_ zXuvWm`6|bAURW~7US^IoyUaoM*Mxe`P=l%&LQY4z!% zP3Ez{0p!)u$3&6r6Qsusg5Z_4j!g;_P-Kf1 z#4|@|HpoOx*O63h&SD~BRH@+K-?ybq1v7jt*}CEA9ww;Y9_lECw6NAIixD>W`Fn_2 zX=g05^;N3?4qY3FBh_%W`~Mj2m!;lHl%mbfj|YdN)mnl6vP3{@fq8V?FW+DODova) znmGiYqn{Syao@Go!TfI&ckcbok5ob`k1_PY&(&*3*bDcP_56&xjgH0o@$MJkJa!!U zN)EbK@zAcK=#VXu}G}Isai1w7>sBhS6>=3bC7rl5_6c zzb$+{tsA6o;C^Ku9s=IO&fQcyY<6ZB6JZ2b=5PF%d?++hHfTb#t+rRw?p*1+x zSU`kCAr7w#Zip%WQ*L+lK+o8Oe@~k*ox6!YrnFlON;X3&N;QQlV|OFw!~GYcjd@cs zby7~MhijUoW2&5Knh`8)8+bq8C%s z_Gy^mla|nKzY|J}OL!u1%po*sO${l%;a)7Vu+Xk5k9U>TxjRqHsle%*b8P?Wk-<@@ zZ4s4Uy7dc@dyicW|4O;sgnP##6BG^Re@1~GUVZjtb}`gc4g;9z$ z5ZDlv&u;jUsn52I3AHD6+oZiHF!m-R;klK{JjO4!8B)}#rx$Scxt~nAfkX+0!UJEa z$`|%26u;#~Q389d_BDhmm(`GzvKgx_5e&p&ZE7LW_nuZdu%p6$Qhq6_<99$3kw2R| zU|U9sT+rL~FzM#}On#y`?6TwSaot0q=!GmE@)*?jU=_V;aURGj)0P6{ zk;K%+OXH*Ur!dQw_=eiL7Dy1u5*5)T_N{tzp8|s}rst}!bsC;O#5T~25$1f;IS~_*XY|W53W7?`mJN7 zftW_ir3dupPyS|UqBM(71T_59Xyuowh;A*a5?6aoXRkj37;vzMJltVn)cK@mhX-sh zWwF%`Giy_DMrf~ik^O9b1uS2mFC7=;XcD?agDW6Dcj zrShfH3watLL_S`i9>l3@UPnP*s->xG`Kr0zGq6kJjB+* zC>TGA9CGX;xQNi_tFE8RqXt%cq*B{8w_i0Avd_F@|5*P%kfIXp)DhFK4M8iAk)FPo zvYsm!pkzQWh4UvVk85^6j~BPtX88T{OETH$X}EDwTH6(H4<)&s`pLCpXrwsNLAy$U^=D{0ra1&+#&dY(>4SXB#J` zp3eR|f!jbAPuP1)Ikv3mY3)4O#b@fo)<~&S<$-K`I=X=1M0>M7g+q}_S*0yCdOQ!t zif+Xy(`aH*N1RSP+>ix@## zK5TKRuBW~ti8c+p3rdj#n*o-pUzfF025qhwA)3J9!}oV5I!NYrEa*N8jJd)6wQv|# zhXX81wJLMa4lZj4q&~SAOu=`*&#|y9{0Ek~eF~;r-=OajV1GmvXRHje3L4PwuI3^r z6u+9E6^Oes%RC^LkNsIHt1xB$QT_eAU0hiHwwNfTZ79mBc99TUX4?qeRvBz>V^RJ% zd$A%NpJQlN;@R*`8M|Lf-z)*D5;hwk1dkDh0A02(@f6}xfGRM;k0P6kBa~K zvC1*m>2~$CE@u5^fwDDwPw1ZDeiQS$1&`KCG+WY5Z^;NnZJW%0rnl_l4~c{5m63ta z#a!^^it3q|DuCpFvWgcXQ5OS114;2G&DVt|-5ydUnIb?1no06hTcsxljH}#@Y!^ut z1H;CFUr)_zcX8ZyolX7UvjDpu&18fdiwIXdCzhS_#KLGNd|4tF;6TQ#Tr(MR3Uds2 zUP?=%IIRamYd0JUmQ23Xt}9}qdFW7!j@?!kZlma9wS+f`qeW`JxDsOLA|InL635}) zLusLhyipNph#|pog`(OE5}veP-{s@CCi(`cWpM0yN36wDf~ZqU=FomP{V^T>D5w3(yu_ySGo+O>V%NChhlRom3bT0&sF! z-I*RsRJ6HjgeWE)3&TWQLKN#|%`1#%BE>mW`=39*ChW7Yj*9pWn`pBCv=aLtmumh7 zd&w99Eq;e0wLui(`@x8<4N|uC0ctv{r?+zy-_#mthGjl3tg8pVdn4$TABjHD^~hXPi{&Ajc=z8f+w+Os+Qva0Y#c#nSseV#dr6ee!afX}4e8m>vE)Pzhide}8SVHYbBB@}Yi6e}uExZ)pb%Q-9KAjGv53x7_V*;fH zbE!TRriZpP6cGo0&Cz~L#24HhB&EB%Vc&gz@ArOx z!MW$m%yrGonVI_?6~@wbO@mfS8FB#8200aQi+cLm9c%H9px-**6UCDB)pOU{EYHKq zmBRB&1!DixR?*iBxOsA9sB*)o3l-#7ic8q64krQP>>!S!*r17PHku%c-cnY22m@*9 zA$z#$6~=3yG1Sq|60JY@z6O}M4=dueYai>zM8D21IsQ1y{CT;84A_k~!1@e$FM6H5 zjPB9<85x`CBWN@P z_oT}Rv}}nqrG-!O^1v$qi#oD{=R@1i;k{ogzhb^!nK(N{|LRPNboS75KDCNDGKRYlVS+U*Uax! zsuH&XCjE(B9$&^v+IY2|T*uFqzW(*nuo$P(QE@riH9fv5i9ROub)!$}dEDY7v!;YO zM9!OzE~T}P45)QNa%^EKCY4iPGo<(rn*wCEKVH;r;4TcMU&uH~W4!9f5aAKxS;D#l{kf6-o?hS zuNMrQl&)i_(Uk8xzAT8f+x`oe-WU_|r3rdIn%+X=iBV%6icIe*3XApFCPH>EniZ!K zBUe|HLUYW!2V9@ZTd9wzAf}6RI|#P9t0U}J_q7sZM;e)P+)ET&1ZmN4eYE%{=#?yF z_|_(F;p;K4zt%q~_eoc@6U!Eu(Zk^N`Y0ba+bOO<(^Z&oI`vk;qFV#a`z!EA@GDCm z9S4e$I1IB}(^cb~b^H`%^oONJDFXu(YGxEaF1$rC#z>}Ae)8)?Bd`Be&99^p%ErDG zS*F<<*86axTKgSac$+g;vg(9@%ui5;6?JrD8&L_@a!V|liE8_MWfE~-Gs%^DuGtr8 z%U8f63z>^B{h)*BJ)t;uy}iJiB~MKr@+rKK+sZGqPd<(hqWc!16H<`(i?tC0*+n2J zXA~t#M(j0$Ha@SJ1i^M>Gqr2PdvDEwypF5$MXCE;89N0-#K#(x%0wGw6j^Rlyr-s) z#K1WzxWJ2~6swr?FV0VcOO>|VR?~TII`2W#DcbC$Q}J2T-F3}a!T$LE3Nk-PyQB_x zD4Q90up-5%vu6OY;EDH>ifL|=UyOiwV)z(&rG=$IN!`ZYHj^PmugPy^ic?eOBPjqX zY)EURJ8gDTK1+Ur{ahE@)zjJ`dgSgpHxB&R>4O!g6Jmu${}+CbTzCN&{G5f!dN&U5 z*p@tt?+W}9E=8{(wXP$Tq6NP4j{Oz;*r~7P2T{3a16vFxbe~7|(k2a$6k@AQj7MnH$jMX|RBuQdhe}un{V=c3gEsRSGxBahJHUDRS2%)_XAuEB)?imgEW#I#PC!2 z@6#e-Towy0WgfYkMuqSMBkoP=d8(xS(GZrd0w#lBU8;w)Nr-Qz99xmKfTGeD=GgjT zoG{oR^!;6Y(?hT8!&;~$R2K3VUeI!s)gi0X0o(V)^f#`X0u@G2+rTOwl&#i0vMaJb zHKRrEAAQ_?pCzq%^QY_LMjR!?_bJ*3go5kRH_sDjJSp2th2qzg_~O?ga<361VY%LJ zLEe#qB60S~ed7+QLz!P4IjO6uYIO{%oB`ciZK3%$BfBe-UsF4|iFA(Zegk_e(amFE z6!g$RVwM`*W!hwa`8jKlsbH^X0VjBnJ&{Ez%C>h!3hEl2@%eQ$1}iWm~W(-1UX z8M6;XHp6LF2(yT)1j`8U59&1K;Xox1JrXO`6pjygGQi7VRVhOG<5!r$0Fo{)AUL|e z?uPff_>Gzd?a8}RM1}5&mDSdrvHRG>)ju!McbT1H`@4?;7wL+b-~PB6BJ-9Y3jtUS z4Odz2b~73=xs|E%E3KD|cf;jh0#|x|YDlTvFDqJL-!INjKBBJtbEqR4e*q&AeAMky zH)hxJ8x(6kke)MXVd+g18J=efma9^kWKmj)- z1sM}a&AcJ|MZFbmU5-V31^V0`!0TgKlvd{9I$-7HdlCM6rb^kf17Cd~JAY7WaGP=S zmuCcX5GCprUOyfh6@%PR<6Pn=G^uEc`qI0nu9Z`YwD!NtIP1^xL_Y_-ohY~@iL@$3 z%zW)-&UwT4+P!BKPQoaaj4BZNb_$)AIl7#=Z*wQ=R8DRG+4b4Aq7(TAn_@2VNb!g@ zNPiGGB!@FPTCCTazC2%wU;7nB5$qXKV6cqFuVn&w=KfxKCu%|XV~!koJT}p@i2PW0 zZ2b))ZV-p|-dVu1^g!~XzTD4Qbo1(K8UMadSYI#esW%;|KrHrl`RIa|&#^_aUt)gE zq`m|S-onLBxv-xgd^M(H-6W;>$`X;9P75mMLY+YGe8?FG<2cm25uv*D!rG>jXuJpZ z9`K_AVvr@NQg@I~$r$u!6zcF?OGvParsv;)Wbmxe-hQJ4=Sre0J8xX9`4+x{9Lj=i zQL(?1IuRf{_pg6z8tiK5(A^5xD-c66?KCJ=zk#yk{5lnC34z{6yzUoP2QfF8qvt<5 zqx=q9RSLpxQ+e5(Pdn!9gAv7cHqV6_sGJUdKR^Xxah+JVp46j8IU4p}B+8lY~ zfR-Rz^sa^1*-DOI(*iyf-TZYo*Hv1%41!FBODLpwvT6|3W$qvE_3XEM1(~TWLz8l@ zoL-XYBhV-GxC83jbZ{BR+F^$N*tBQ(C_w4^;~Pa{70{!KS}=)_gg$WGJQVo}f~%cK zRddcRN%T!bV3~}Gxjp^1+i!88v~3qOs2SZr(~LAS0aVo6GblpoG&a@ zFK$4cn*toepAcq!Yc6z2j_r~OM$*R5K6+L%4XN!Umr}Si+%S87h!Zx3IITq{6R#<} zCh>=Uc*t$67IZmSiIXN|CLvtO4kcXB1s27uMJsGDLxUt^_u zBS(ETf6KCbEPXwGfosJT-~LXH#WYm6=X?6lM4#Ocxk`)mZzjVL0zLr@Z06wq<*w?}yce)Wn8e83So%>R1Qq)F@x zwsj*J*t2typFvw1^!QVAgG7i6X_TN<$(w9qIfl4@c)lxdFwa6b8t#oAq>EDp=wUHe zBJY#*CRGKv(oV&%1S!tYDl#l^BTm^ zsNBYLJBwe&Ten`~fnJpf|`uD6jrA?ln81^InZ7l6&ms@xJ>Uev7Gt>Gf3& zw9)fH{qLb{8%eJ(zy7vWOJH)()PhcE1Ea z+PxMO7xqdCvF<`SAFTaXGsqO&+5m%8&oK4 zLa`3*{^e6}^@E0hC)|VxrJlm-C1}!5%muNdvkfAfB4^uZFC)>#m>R*Yt=Ud+d>Y?C ztB9JowAXS4KC3-^SEluGZ#g3jI+yM-yW11~q(`42Y)MG;ltw9Z73O`m z^2Dm!-A2xQ{QY_Xsh2IEG>2A5t=k%y+E~gis(V6vv8v<9WdR}To6WOzM4jPJjk4w1 z_(Jq(&--qvcFyfcXb~kfYYzmj4{`ETIttm6qMZM_87NnmV&1#$3%8KXnO={v{Fa4N zGb^|Bm5h9*^O03NP*_ZNs`7eNnAC@2(f#XHGsT>BpigqT&rfCyJR*`_0HfhD6w6KN zIDTPghx?=I#dBd7i4YZ14=Des8%2WLHh<|sdTV3@ZAjN_qjY_4VHNFi`uF9)fFZ?s z)l!9ZCXJrcuIZO@6k@aWvQ`ThNu0;|F9&n$EwbzY52gTu-Mc@iZoW>ZMqZJfeOVnIA9!OK(PcH}*hj04y*WUwxp}Ns16;r-}IEa#H+jiL&kO)yArQ;`P0K#FL9XCPaKZDhAHiv%h{NTZ9 z5X^utCklGez$mOO`_Jm8by0Tv9M3^4bY0WgHJrFVywU77PgsgwsAr4|qE3y>|Lh%^ zLA}-JMZP79<9rJ$1b9E=?$&HDJl6>f8{PC|UkEx54X_xKC#hYQxT%4wfm$Ohcayql zAukB)S2|(yd42k=qFHNP_p(u8^9ktL(yvtCaO@LW*J$rpI7L*pLr7-!nc%Bl*XHYX ztgjn;Ht6=4B?JvN$R?1hnfr8xZ(tuQ!u4+_heM%l_tA3S+0FaSm$7S~D!l3K(V7$W{VRJInNbo>l} z_Tks>-Z{3t9b>R|r6y9KLB$SYwb*%!%l~&uCy+mM{NPKuP)@6yR0U~Qy_)7%tG`^o zxs(QOWMijJlzW@ryne_EKO#bX!rg2ZN|nmqW$)?h&Nf$h@@jZR+ZIsUe-)2(G6>Si zT494BM2CT`F6+nkB<%>pNLf+5BX^O>h58IP0hl}AY63&7>K%>;2?4}D)bR@Ni5(Pf z^{zpr{+QI1RM32rpl->)p9{HUuY~t)^9hNwAnUgV&w$o~00JJ6%a*)fpQOx3?h!P6 z6e>{M;7nQn)qlXj@yHuhC&As9xCr?tOEzIyd+qOQUFuCw8~Z)sPePvpw0H&}C~ayh z_R~B}VRf_+kZjZ}0-1#yF#G)0@O+pRL-C>J*|g{oW6_sGfvJ7wq&bg zYQ~i$vy}gV>?wa?_`2rZJ6_Kdkkq%%Pea|4uaymQII9(oe_r-lB&T-?lWQ{DD&Bn? z_HLkQmf5y|@;o{!orXbJ0xc#RxtOcqKyCMf_UEq;CbkVsdU8qbG(oJLW{*FNLlj;q zRIN5kK*sVXWowPKPau~qg&$TjIsXK9IsRQ&kV5)8CNj*w0hrM~&Hco%cI;7nM01(* zlm9+Z7^>bvH7*=ylo#i_wC^n*E1a7+RO-mmMGs<=DWDK4^ZAW~SfB#!-;t%i*ms{Q z++3qXcy>3m*PfC4E=M!f5rwn=}-1kRfl zFQjF%nZ1qXFH5|x)%Qs~{4L>5UP@&3MDf+b$J^qZn5q67K`mbc_tp8;?OWnc`DlXb zgehb_mp%*M$_WdfK1b`G|KwE09vzH0i1w;^Ipn$qf*D0XaUG6VkgTa_Pd~SQFNeHP z0P;d*yA*?Cs^tlYCd`RF+B$c8FmX(JEwaIz_UtR4g zN>iN8hJ(JkvxReB`uwdK^y=jBC`K$ABFcQNiCqLg?S&ZLEi%x6zIGhxiQ_-TN^PWU zUT0glAm&g7Y@Krpk&J{A7`5KIV$9IETzvH82n}GcOgD8BY$N%rz*j58mdaxTkC5Vq zeZi<+$n0@Tn!QMzdDS9h%78lUm8_=;Um-DR#beuwj#|}P1B+H@-8bXc9eN7Poe#(* zkec4Y`_;920knCwDxBZwk%804)jCaC=iey3^cZL96_?K_N@6}_T{_Y@HZwl@K0l*) zDhA}E0AJp`&^fQg3X1~s_}%Lw$i+eTU|{OK&XA|U5O7lRr5&An?4zQ`+wW5+ zW?M8F$04?KKQ$v*V9th&Y{BEz*%C&KuiZwcGhaD*pRbb--Cy}#d7?;Nf)(*Xt)H&# zYyuFM0;mB`XE2TU#wLnSH~t;(Py6%da_uiAKpYA18=315rb#siIy_5@4~38o|A+!n zJBaP(*WBO0%N2M{0dX_5+e{=xD*93+*XCq~ zi0vQ>rh%>9E8fz|e4Snn`#x$V9JqYQ#{Yiv9EJu~0pQifdF z;q;9Q@;M;qTgdt4T|y4pDbH!^)O%+q9)#+;>xC!ub>g+J$(nZO(VRe4zXvjW(Iv|F z_GHc$e&k6)%eVvcy!{oav}{|Ef)O+i?%mwA4_N1}yJH)|;kP;9A^7=bQqWgc?}H-r z6+z+R%TP{&Ua&asMvG3IYW-{&j0qIJqd-u=NGbb!N)^Ji{_UAPq5o=H4CF)8qS$4o zdqG@L9@ms`fcYXHRo(4N_HVW-h^ja5Nq=*))9v2`IrtL>vWskn{S-<81}JlBs6wA# zoofF5_gKmCM>5t@I5*cfE8f5G8cE-0Eg3KDN((_N zw06y%kC!JEB?pG z_dcdgV9d}&yBTA|sUfMuo7qldVM_AheZ=nr!6>-_x;_!C32pqVW&0aziN*P3$0iQ% zvnoESt?t8)j4H}Z3`m%!D8JFF7HYgTWhL^91Lc|4<#sZ+PEPl}Sj+y%03ag{COZk< zK+hz8?!Gf`prZfvhomZ0^~D={uSb(Z)^x1-$iUF&?6h!+E^JMDKaKcK5++uo(_XHQ z511)-b`;h;gaPFCbiSRKfEh~DCSf80M-u7x&Ufj~7G|2*-@_Jt5Q0TQ4HNY~CnY4L zVXVRs4?OAOl&M#NXg*lz*|l}({J@pF{SoOTsk{e!N_CZ$J+-V3Y<%^6Voqbzyk6zNjO^yP?|>XTSFtu;SE(ZF55>-kfC?h?Ld-F zd%2|bH#&tgyqL+t&1iYl3Nx#FvkZw*?%#EH&H_;^<$+5vX6F27=?Hz)pxDRHgDQH} zH#OI%GfrsI$B2cn`H0HoH?yj+4QV$WPVJ>yC@ zP(XJIUDR(+N$X=eyCxr9W#B~0Y#k$m`7OH||LD+>5`a3Ny|@>-B|{Ui6U1=p`TvlW z!ODCPHzD3}L2;HpbelqMRdL?L$A^Ri)!J{v+W^I%k`q{z_0P}&#dm<>JSXzD^~*A6Vm!<9=%8y41-BjO)kGpbc9{xVr_K zVa}c0p02F83*;J|8!gnFPFOgb>i>EHR^31V*0tLg@QPe`xR<8NovC0#U|8whM{tgk z=bwOB$+HuXgw$kD#Cw2R)YxOQ<+y>jl2UUQ@Q%RLQ~QHz-*qeEGZBC{ht$l)^y{*R zH}J=@kMIDudr(0nx)nF3lapSeZ9r17kaG4m6Tbi({f8S90nd+#bd;?1-z^LyT~Mje#wWuA!yDY&iHKeBC;i7Dvw~lWWS@4-D84()x>Ykj6;hhixRDBr|uc=m160ssWhYlN$e$ ze%ncqaxe@nHnhBkQc!|0O@OrrWX9_l3FJ7zf`q)A7M}o2*Pu`0PXvw!cc0pCe)$1r zOQeiY$=Dn&(e>=uW^G%!#NUVtMRB0;E~WwWhx1iLzHF0EK6pMKsC*uN6FNs>7s|Yx zo_az^_mz(T^!%7J%xxkSiU~knAA)XXOB=*e4I|SiHPUpGvuu%lroLY!v8RWM=ig<9 z^8ci5YpCcQTMlCdJb#&OC_}D1s0P%oxk@%mlK$Hu6vh`Tz=1mA%7K(IloFDrj30PH zv0(H6`p*kqa8zq~gqvsU;}#niRn9uR5b_4%h9{>;(6__dyKG!mx9 z$#i~>)~BU`D4DK`zzcr?fQbfuL4_%O)J^y>5tJaU3TE#90}6nuUYLMYFC@{^k{JxP ziA>^d&F4pD*a13z+h_At+5V{AVnr7L34a3E&A7sf#a~ZFsD=bzPJK}FC6-H)mXPwu z^dap+)>J((DT|cimcHhAw?I>00>05yVF8T+ALHj_p6@jQ4TS)CRFa~3LMU)Sm!^KKG9KMFx6BZhC+q}RRBW-weM3wWBD8n^y z_u9TR=wPH=PF)SS{i!H2pZM>D*(~VWEe||XVX-I^HSTg%58tB{26Gjkn+YiqqI;4dC&hLD zI6g!JSd)E;p@wwcP-V-NwVY2q*gR2u-bnnh>OHI2`HGLmf_Tx7=U*37BTW z&+<7mKC|>CFzR!RYV^kaD(?4X;ho15|EA;N)8O;k(ES%4BMnQ53CyOk*uQ2R8C8|i zF>B#cS6Vg3RMC1SKU=g`b~1hDz9%30vv0U)b(Wyy(p+AX+M6_>w@bA7;{D~*+w0xK z)pP9AhQK1gM}mD!hnhf0-)iK>2{;KY?vFn)Jd5N&UXi#Yums~FyispNx*h}SA?rgh zzema)_{9CnVysuasjBDW((&JPG+`e*?Gqq)6|F*@!AthD~Vk>8(#?uZxvlb%sY*#hZ}ET%Ic?^PG}7$b^b+)xczyV+nc=J zZJ#>&f*B98#1T}I7q3=d%>G?8V8Voxw7hT7MYU!lwP!xR>{&cAuRFr<^>o*b_f8u&8a zCuKu{y-3oQ8u-pYTSK1!O(8fv?=@rm>T>5!?gHTr0`>0h)lA8|9EK;kU{jV&!M_CJ zQ*ki;vf*nVfZpv#|GdXJ?*xok(m4$1c7e8=f@J{tb#6p^rIV0T2Y$KG+DMu6i%HZ9 zN7)>gqKC*W>Yuo0vD|>X$1lC!g0JH&rRk3C9T;E8Z@3p|Cp;I@lz{p;N)*Av(5Uza zTo}$~ZR5&p2ZqV#*0AONJmPK|z{J1UOrDDHt%HP*=pb#SnSl^#O|sH+#*U0afNVgE zfsVQf5zy8_#mG+<^z$_l@ft6$;h6HtRpp?^c{;tQc>9*g)$fouz6Ro{KYp6g>QWGa zj@YgkzkTJM$#~B*$7T-kwMhDd>nXt~_oAK44&A%E{00DsG-ZqkN&Sjd%DI%nLmxz( zNE3X?k^sH9aN2!EWf=0{(K}yA_IlP)K{Ra+F-0L z6UU8oC33kDfyI^Lp-C1ABU);9KI{bvVdF{e0p7OqM@45Eg`xL zJ#Lt>!-yvASC2}Qvw42nG7BihA2jW?NGMwj);zEZIheT=!xtD)dDL%9ef%OpsGAz> zdqm^$?jm#GsWbOe&MxQO;;8>x_5lqFnro{JniRcizANi{@cB{yN^)U9w(zt9lQnp+ zI$;^h1vj&%Yoc*=NMTJ`yd8@@yT4=~9dc<8XZ)=!bk|PlPPtC02=F`m*fM{=k3le^ zeP}#mpK|S4>h!|^jIaZhBrFH-E`f%-dH0gn4yCVVZbeFzvIzk}lp)wkp5*(M6&9?3 zm>hfhbMPlQhg|R{6(R_S0%}}PTj6>cErA~tq{6C+oTY>>GoXfVdG*6o0nG)Nc%)}2 zFXKZ}9}m_Gt{uG*Y>nfCZ~{8(w>l0cb`o_stuGUb-_#0lL9hYM&2M(ggOZz#Hg|%( zstNF6#KSgbS1YP>mp^ic4S91GyhVqihenl~wiuR{B4XRXAf$|PJ=UiM#9|q^=be7V z4zhm!*f0|k^V8?M8@M5trrPm1>359?V^D-I+S$n-05q9RkShwIKCc{MflGq7ZUalt zXr37u=!7-+t2Ht(OlPUwK{LE|q4n&jME=M*{)C2@8$2|xZ4@0jKt?C2=9gC+#(@M( z5pPX7h%e6q1*(03S!87LnSC+%Wj6z<5pO-h<_4!FdG8FAhneJXc1C9_%+BpOTJ`F9 zO3BWwMN^!<8kx|7oJ@6xW>J81$f^Ua5@|Z~Jg_QME%Y_J>sNa?chR_LR?(jM8%+;jh}7M zrAJWy5qs&>WKcD`GE07)5Dyi(k@CTS<5}DU2uH76^LO1yybz>pNrK?RGT1%wT)}~n ze$|98x6v?(ao(HXY}r7Jk}=7sRiZJ+hdYss_GkjVUswX9_)WW(=CwdD6of=gcIE{@CMh|S-zA`sBEuj z(M+I=O*pm2kwo!Jo&|$jZ>IN6kJpqfJ*?D^CB#{?yJ@2SuS zM~Na-*seSlY!PSb2kem$FDvb4S;}+NoWwBIPeh@zWDN^XM@1BmBgLkStZ4A7tt4{f{DC57h@)rNI zgnrI=yubItc{WLd80T7p!HnfQ`br)CMfk3yTZ1<@;5_zBl>K0Ad0OxdCX$LzpmHZq z2_{l_0dTQHsqLK_96+tIV;K?QT+dZ*atHqU6QeOI-(XFr(%d485ml!tdb zGF*^5K>w|(YsLZb_nY|3D5Lv6j-m%VxtrYaX50sy0O4nEUn!EIB}9GX=rj$${5@a< zM;ySl&OOGS4B#vg7i7V!E-?$?Tq(q?m}|mZAudbqoSYJxPa`$GLMOJG4h5)?vxQ#C zeW}5IE2=Y!+h_O81$xNaI#y1>iYG}W9=%Du@JtJM=0-xU+;zUJM<@Q^-zZ^F zxloi@@Qii%1>+L-lCQX(te-t5j3~Z5#I;QWQyAZs7AU;9OS`T1HfE2OvvK2yxMal_ z-)-gn+>i@8BBkQ0!lk4Q^G-McMXv0mrJRxU0}%+~MFg2xw0BTb+sTx`C}7Yrfr5pg zJezZ=TvDh~sgFj#A7YkalYb$oY?fc(z$sDc^~rjIko`D(=(AQ9*3gPAt;%ZBnT6Iq zA8wW#!WT^jK9^Y{5$D;P5hks--;jOKkI~@=U&Pt{6jO&qShwc4*(_(x4)K5{p-j^R z4}S)u0dp4#a$CWz66hhMiR~wlk9iRW5B>W=NDY^UQHoknu2-@F?lr`&rRe&_;2SdF zEB6=Nwu@&k3P1k`fwJh^c0F| zkyF=}3V=IR+7@yEwhdpON;XfxsBCxfd4)9+(4FLhNHGeQzf3$aJ)`M6r?`iXbcP_) zOA$q3-STIitiwoLURV^V?cC>dB{?qXZ?XY2nYqE1v~1XAfuBjCZdRC^Gm%4*&+Y@N@g0|frl8gq;)KKa%nt`%3Zip|0JsByX z7P&HCO29Mj0C4e`e-C8><=S+eI&DpNFO-ri+0SBJm0Lojk@|gXE;D*;e~sm$>DOI3 zP)s4b2o@tYrE@wR!^n_9jX@1(16`_6wgKr+qzg+&Ov77cvLzM zcc@eVO+6liB6yD%tR4y6>V}icotPoZh;*I7OMWCGD^kb)cyr%QwoM$g`h5_8i!~majHZ0w@MVSJW$>Cu8$a4zIcTyNo*56ZmN2Y(S-QKY zIyLI|Qt5~k;Eje}L;qbU%EH9TKd&^iUDqZV%Ou3u!HnwSQtjF&gI&V_xv&Y~?_#?5 z^3)rpV4t8s-|B<1BOxkp!Y{-h=d->VD&OH1Bkqqm<3}=avZMZ6lq2Q{`>!FZA+noj zLdr=kY;$G%zP#T(bDynz{qPhNL#T)?ZswK2-{F_mN{%56my`|!M?UrOma`kR2jz)( zHk$*piT`T43$^s*QxnvclvCg#0Jh9CRK3|yAybvy2#RrRjE++3Y8x058MJ5#-Ob0ngHVP-tD7_#Lm zal=&Vt^P_7tkPetIVaN<$OIHF!`7UwunGdcEn~aKH`pLZUQOy$zNZfeX%Db7T>mJF zhpYYObFYv#r0GUd%M#V@PA#ER)o$+97=ylzi`t6gVvc*GeKS=72tM#%FI8pWX?{c_74hkS(`YwwE0CE_`lkT}wb@wD@z#&L(sa>qfg7kdzZgd6xV zO6^X1!qW+4wlnn5|E0r+h%3LQq=J=A!M>A(U{A}CNA8Qh$Hs(pxrOFO0g2~Wg*!7J z;OTD}92${l8mJzCCJbrF>g}-NBQM`tX0*3*C+%o^V}4|zEt&FDefcljAD{6PLv-N_ z=d+kAQ~;>=ZjDLUd?Bx8h@ipP?D57mA8+iXU12tOU>w zjGB6jc~0=F(|FP|r?UMl3PVbRA9@$RZ_82>ZZj+a_W5a1|DzQ5-t#eMUld@QI(``I zKHgj8_$TcPg(z~aXJ8!I@T@hx|^y@4Zmo-}irv7NY#1oIrO68N#;?X?9PGLj)1$?Ak3qa^&B~e!V$gd+KWO zST()FBXDOB<=OI_8;HHVTJ3@`2+@n$60%TS4C ztfhSPH}iI&hKhGl>uo8mTGpD5r|8}0xuY`cGgt5mD2uv$ZfGr{^0bG-kmGtOT{mrr z`|*X#(MpJ$8dD}kk*0TRK#S7@yyB{UmQ}4f9%?r&C1Sjr;Q4PMr%6fX`D9-41RJ*i$E^F^G5k@X8RXG+{!1o%re-Na&+LHo+Z!Z?PeQDHM~6 zb5vo!8}#d4D5rT(`0~3rc``BtYVOrPI^L=Emi}p3KyN(6FP|tLk$fog98ls%)fd6* zH!nkaUWl}RYqDb>3SdUP2x|;>JM-Y4;BxuKX04ih4`B0V!K{q23L(&sNNKH>g z2n^oa3_+d$x$r)TM~BDWs(bu0Ba#Ic58LwgqWI7x?TG_@Ue9c}I7r#Q>Eih|`Q6`M+*Ebx?k^*)r5b3O|*ksay1!nEL>o*3(-zB#BW7yDf9sd9zTJb{s-8&_p0* z<+&7=afb*qUM#1LN=Ty}p=l&ew}2Sdc&LW>&%7S(E^WVK=9?Y2cvUDVtWhWlk&0vd zfj!_s#K{fC`&JS%8%Uaj(kl)KD4Ij9uz?$&g>{YQLb!X8w1N+Nd@HrS#I5k%^?R4# z3EC<&KzwfDp>a;1Uu#mt_o=KFK)^#sn~kZ-4&KE*#RAsCgZOBF#*~>tf?Z+)iAhu* z%UaNqB*}(Wv}Ox#96*E~{p9vkCq8b{z*?j0-TBvY(wbfyn=3ei8ZF{ESaLH)Cg?10 z8_dxFutP+J9(vOmMuOuXJ}KPUH9jdKiMt0KxPgDrv%JlhDg;bN+&5did@UL)ezdjODB*&eFR!;`!|UH?W3gzkR`a!I3IC z|Kji7a<94=yVLp%2vFW%*H~1_!QAr!INljOynW4~T|7%H=+k2F`rNAg6dsNFF<~$$%pupU&&g1|0s({_? z%rJFwlA-!~mOJ1JnFH1%n+rmDGnF~&tYEnpzATI8tXt5*5*%qv#*2~b3*Mvd<>Xii zKBSr{4e{0zBIUYn)CI(RC6ao487w5fE64_210d!2 zQ=hgZYQWIP=OsyMw`CtCVp-I9kcj9YeR9}mYb&Cbq6{&wO~u+B#?90*Xv>Z4Njoxu z{bcSVgF7rCS5G;)2$hT&8N-XfJiqr9qGJKR9Y3++|F`{l6SiEWr%zg#Sj>g4CT2vT z5DB1N%kIIUlZvf`0}l7tNQ)QeN7)ZgNKjJmg$qbO4w+k)6T$UMBjGEgj&RC<12#h| zE*eEYnj-CRy+zT2xE)+TWLFFJWAb>hwhY{{+gEHaF^Wh9)Abw_aGCStc?Ans)UA^{ z$b6;U9V;|VmgOGEG}Ae4DhZGb7!S0U2suPx4mGY@NU^;SqQ2*aP$S8%4P^{8cAlw- zh_70FLKc}(@u%dT?g^LYSOQv;%+FU7cT8t5lqNVOu$TNv-H!8GDnw8j9th0wCMEbf zacttGAe?#V&}1etz*;s|Y=^|5O-d=ulH~mAp4LJ6+dv^;Wr)NMd(Ds$)6-&e-OlF=NaLn! z7u&tmlWSN0r31W=z8A(f%%zqNw9mqAEyj=6B~EVW z-nWZG%brq)3U0J4ZJ@n6xSBxv+xf++cS6z(t_e*y++dU`TIE;EI1Pt2>vpv>3cU00 zFabo}@iEAE;?Y4NETn?P=m2B{-(8g;3@e1_+^`9BReJ8;6+3QbpH_GfxqL#Po4#(d z{@nZP=V~k8U$toDh^t83c%#@=&cJjw5%IR~E58|>L6A1}I;mBgMZzGZ__qliKDaG| zT^j?9GCivTmIz@>!~+0Rs*w9p0Nvo+7p6En~bCHE|6Eq)JVYo(_fagV89u&CP zOcT+qAl?|Z=bZM5+dNW0jwE_EwEjrrE(b4JLa-b4ZUb zW~7Fyj7Mrhdbku;5cDTpf#`jNh1pxpGYZU!P=J^CoXZoRv^X}47{Itl=utGvH4+f} zA0U-}(w2t_NKSc|zWvqgP#k`6Gj6?tDucQFl1An)+udN2FT4qk#XtQ!ZB%KO;N~|Y zN$~y-Px7xZFX{z>U1nQ-o>qkY0_z)qnm8(8Sna0}_W&aJV|>bMrREn;=~28-k7~~N zqu0^P&+`EnfdNi$dt4OjN0`%1?8R<4d#<1W;!BCM(sY;!#@QIg|F($W`w`D=sF4L5 zl59wp3l%1Jt#MINLvC_|V#^cv)cfxdmn4TS?{o`aOvpM{UBI(^5;Vk76S%lcex+@5 z6FF&YrvBFVJ^lonGTP9loJdE%_XCKx+LHoJjqvoMlC=u;{TKuDV<{X|;~6DTp`5x( zv$Iy8>mum_wbMm-)Ta~RuR=f6GL1FwjGl>B30r)G9NYsF&KgM%2&~WQxNon!noG z<#{*Kig5lbe90532apK0E2xwh`n(8sy>#C^PL|R?8V=87o zN^KSwm&U`{%S8Nh^l-o{X$a${BLt&H`kE<$)Z&gkTi!A7yYr-p;^{k6;eYvJy*=8x zHnV@Nov5(^X!-ZYkgyt{$wstfo#^7OKdK(!%y_+3&wY0hOndaw7w6I#V}i4TG+_yK z*jPgW)Phya%Px#V>HR`V#oKNFp2RBz0PJG4#{?Yi4BMej$XaErevJhDZkEDgA0FJs z)wC;GdT!9IIa6S*r7~y+_HpBw6`rVTQu8cvMO6PxuV%=8HTrVxCF(OrjC03U;Lq1F z&P9|G!7M_-LR9vqkF+kV_>mJUxszsWy&_oGo#ZbG&%X~HGFUQ8Uq^p<*qS`4o{J;R zlXjx}C-5Nf+}F9dovYu7Cln6QJgciQR$lXjt*rh3B1oj40sWoH@aopj^~M@)cu+6p z(CBA~b(Hv*WcvQIU90!T`*|AMcv*x1vkL&ae?m5~s)HD=SBo#zAr(k-gP1@w@_T(} zS~r*|5>P?-oiSJClYk>YUqm~D;#+zo)UOhmIsw<1ifWcfb&Yv($cM|8*X_mqbeBRI z950D8LL<{Uqo>_PbtQcCb_u0mSEl$scfWkBB@^SHpsqb}l)ZsOewOUIxk7Q_4U zh>}1X)rJlKH%e)ZKBCK&k(3&MirZ%HT=i9~RV)d_#3H))N9lyIkfzea$KUpF6R=YO zilWfkxCoy&Zr|~m&)+9VN;O;SezcJfDBQyMEu=Ifz}zBeH5Fi~TTtvEX=!7oEqhYL zfI5}Ysmiju7nAWGb$5+KrqLT^+9zxB9a|Yvs^z~6gD+5M`PC^-cCXSiq8_Pjij5G9 zGY_7n7wnTH>3b*SGd-8Wc%?*QSOs9VP zNX#z6V+umeo=f8zUCfHDto2JTlY3?G)I0aSdHBjER_%6c|0Xx?na{~$)(k^?Ad&K- z9eqKWKvIV_yTDO_#CI+I$P8y(APc#j-3_7C5ToMJ|Il<5eob&+e{{EWcb7;=mxOc( z3P`G?geWm$qgy~ykp^i7(h{RvO6l(I+F)$&e4o$nFSz%fKIfkEJ)m7o;PyxcXI))V zE?$d;={+LpYTQj9De1$=<;WGN{lnbxACIy*0gpa{PRe#ecAuWZPzo{w_u)yGY)Y8A zP;AkZzPl-OJ(l*)lkD@m8b|6FIw0=jL7=G_Ui?7cJO2x+061*7*>~$!{`Oul+@nsSsFr|EGMCaB@134p*(_1)60YAJJD3VOhvT0& zwW%53j`7+^Xn8~=tj+i@+}KQaOKw@TqhnU6`s=YDgvX}{KxemJR*AKW-7%DsIQk|` zfmLG=hl{lnwQj8>#og9ogz(|^e1rS>O;T0&cH5p&N!^~D@Ab3bN}@l6U{A8nerk02 zP+s_ruTmvMZV5qo*$cG;yH0ne#{o>5p9zKIV|e=Em~t{n>2Va1NKmwKAE%Q;DHSQ; zmeGII|E7rvCznOpjPz(+oRojo6>PtJ+?k?m;J^{2BV}K`R6m+zHURwl^_-o4e30C@fCZPAkSG9@$4bwH2(kJ(TFw;=N@iFXWVN#@^Dy(dI=$( z6jDtM4d3wa_AG|QV1Hrhv%*mM@}3DR_{rOt^;V2(Yjq~VBZh0zt~>rvDa^-x5kP`b z$zmQ%W#^Y@Bp=hqH6=}i7=0b+=9ul`bO+UVndsw6I;}Ac#N8+0h^#AP_d>(LRLCC0vyL5fNY&cvN2 zrRJjaOkg+M%}cO1ibIke%%{`nE@)jC%!HN-hIxh)?2Y^p*cmADlH6016Moojw$sOo z?zkZ@ief}Bu{t7tDU7`0SKl5!vRA$$6|C(!axJC<-o^UYWOQ0B-f`*WX=PyPUjXILHDwQrE#R=A5z z*rcB$tcVU5GXTHP(t1P&CZqI!I)`2C!W82!)ejJ5-x>x?Ke=1vGS9t5t zL1HlwEKG}GD2`8wq2B9%^a8!^N|zcdvYvWCS*<-drLJq{k7!t6XlMFOc@p%aY2P|M zU+a252hY(%t09eJd6WK{aUA6BVA>GIX$CXUR4*h(lNN18qRg{O8LoHRnP0OlqLuvCls%#krz^AxO z_{55?Mi!EwtEdq~-9v*dI21XPQ>^{EGOcrAEBTKpS!_j-Fm1lP0 z8;7vnKwpzd=ESYAZ!It~< zK~t4eL&@iK2IVR#u>jlpSLD@#=P>}w4d-xWWm&4S``CDPpk;7Z4^g$q5!Zfw^UI5- zdF+n`0|TVvVEymFrPOxN<`_uC%T7QI{z@9fk78zsZcjf4nIx}oq9&pNXXJ`vc$}Ivmj^7?j;ty=ZGdA=PCoSZt2dt?$IA@sTooVtZ%RT+!nlyp4!o*P(sK1 zrw_>nZK&O5R$9Z>vh9B4{3`yQB;%UH^EMArLFHE!1$pf$T$Qi8jJ4MMn}P~pV?dj+ zN+{X5QJ2WfMX<}z((|8Mclw~0LG4eIB9z#W+iP`+?P^>?U>Kk6^&-hngj2~^VVPWX zwOLu5qQ;k4@H|5cI5CeEHZq%sKS4!087-~cAU+=vi(&b#O2ZZsWY0(o$J=66KZ*3) zkCvXN-z=UVqjB8utGcz$o)@in3m;B;9$Yv|lBE2FZRf_~@I}{M+6I=bom20_DzCyf z*v%>W&z5>9=48V}&gGv+HUqc`79Am gvT@w*0M|;(`oV}RQWLRkszv!Y`Z3zP` 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 +#include +#include +#include +#include +#include + +//! [0] +#include "appmodel.h" + +int main(int argc, char *argv[]) +{ + QLoggingCategory::setFilterRules("wapp.*.debug=false"); + QGuiApplication application(argc, argv); + + qmlRegisterType("WeatherInfo", 1, 0, "WeatherData"); + qmlRegisterType("WeatherInfo", 1, 0, "AppModel"); + +//! [0] + qRegisterMetaType(); +//! [1] + const QString mainQmlApp = QStringLiteral("qrc:///weatherinfo.qml"); + QQuickView view; + view.setSource(QUrl(mainQmlApp)); + view.setResizeMode(QQuickView::SizeRootObjectToView); + + QObject::connect(view.engine(), SIGNAL(quit()), qApp, SLOT(quit())); + view.setGeometry(QRect(100, 100, 360, 640)); + view.show(); + return application.exec(); +} +//! [1] diff --git a/examples/positioning/weatherinfo/weatherinfo.pro b/examples/positioning/weatherinfo/weatherinfo.pro new file mode 100644 index 0000000..d8f9675 --- /dev/null +++ b/examples/positioning/weatherinfo/weatherinfo.pro @@ -0,0 +1,22 @@ +TEMPLATE = app +TARGET = weatherinfo + +QT += core network positioning qml quick +requires(qtConfig(bearermanagement)) + +SOURCES += main.cpp \ + appmodel.cpp + +OTHER_FILES += weatherinfo.qml \ + components/WeatherIcon.qml \ + components/ForecastIcon.qml \ + components/BigForecastIcon.qml \ + icons/* + + +RESOURCES += weatherinfo.qrc + +HEADERS += appmodel.h + +target.path = $$[QT_INSTALL_EXAMPLES]/positioning/weatherinfo +INSTALLS += target diff --git a/examples/positioning/weatherinfo/weatherinfo.qml b/examples/positioning/weatherinfo/weatherinfo.qml new file mode 100644 index 0000000..730ae75 --- /dev/null +++ b/examples/positioning/weatherinfo/weatherinfo.qml @@ -0,0 +1,240 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import "components" +//! [0] +import WeatherInfo 1.0 + +Item { + id: window +//! [0] + width: 360 + height: 640 + + state: "loading" + + states: [ + State { + name: "loading" + PropertyChanges { target: main; opacity: 0 } + PropertyChanges { target: wait; opacity: 1 } + }, + State { + name: "ready" + PropertyChanges { target: main; opacity: 1 } + PropertyChanges { target: wait; opacity: 0 } + } + ] +//! [1] + AppModel { + id: model + onReadyChanged: { + if (model.ready) + window.state = "ready" + else + window.state = "loading" + } + } +//! [1] + Item { + id: wait + anchors.fill: parent + + Text { + text: "Loading weather data..." + anchors.centerIn: parent + font.pointSize: 18 + } + } + + Item { + id: main + anchors.fill: parent + + Column { + spacing: 6 + + anchors { + fill: parent + topMargin: 6; bottomMargin: 6; leftMargin: 6; rightMargin: 6 + } + + Rectangle { + width: parent.width + height: 25 + color: "lightgrey" + + Text { + text: (model.hasValidCity ? model.city : "Unknown location") + (model.useGps ? " (GPS)" : "") + anchors.fill: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (model.useGps) { + model.useGps = false + model.city = "Brisbane" + } else { + switch (model.city) { + case "Brisbane": + model.city = "Oslo" + break + case "Oslo": + model.city = "Helsinki" + break + case "Helsinki": + model.city = "New York" + break + case "New York": + model.useGps = true + break + } + } + } + } + } + +//! [3] + BigForecastIcon { + id: current + + width: main.width -12 + height: 2 * (main.height - 25 - 12) / 3 + + weatherIcon: (model.hasValidWeather + ? model.weather.weatherIcon + : "01d") +//! [3] + topText: (model.hasValidWeather + ? model.weather.temperature + : "??") + bottomText: (model.hasValidWeather + ? model.weather.weatherDescription + : "No weather data") + + MouseArea { + anchors.fill: parent + onClicked: { + model.refreshWeather() + } + } +//! [4] + } +//! [4] + + Row { + id: iconRow + spacing: 6 + + width: main.width - 12 + height: (main.height - 25 - 24) / 3 + + property real iconWidth: iconRow.width / 4 - 10 + property real iconHeight: iconRow.height + + ForecastIcon { + id: forecast1 + width: iconRow.iconWidth + height: iconRow.iconHeight + + topText: (model.hasValidWeather ? + model.forecast[0].dayOfWeek : "??") + bottomText: (model.hasValidWeather ? + model.forecast[0].temperature : "??/??") + weatherIcon: (model.hasValidWeather ? + model.forecast[0].weatherIcon : "01d") + } + ForecastIcon { + id: forecast2 + width: iconRow.iconWidth + height: iconRow.iconHeight + + topText: (model.hasValidWeather ? + model.forecast[1].dayOfWeek : "??") + bottomText: (model.hasValidWeather ? + model.forecast[1].temperature : "??/??") + weatherIcon: (model.hasValidWeather ? + model.forecast[1].weatherIcon : "01d") + } + ForecastIcon { + id: forecast3 + width: iconRow.iconWidth + height: iconRow.iconHeight + + topText: (model.hasValidWeather ? + model.forecast[2].dayOfWeek : "??") + bottomText: (model.hasValidWeather ? + model.forecast[2].temperature : "??/??") + weatherIcon: (model.hasValidWeather ? + model.forecast[2].weatherIcon : "01d") + } + ForecastIcon { + id: forecast4 + width: iconRow.iconWidth + height: iconRow.iconHeight + + topText: (model.hasValidWeather ? + model.forecast[3].dayOfWeek : "??") + bottomText: (model.hasValidWeather ? + model.forecast[3].temperature : "??/??") + weatherIcon: (model.hasValidWeather ? + model.forecast[3].weatherIcon : "01d") + } + + } + } + } +//! [2] +} +//! [2] diff --git a/examples/positioning/weatherinfo/weatherinfo.qrc b/examples/positioning/weatherinfo/weatherinfo.qrc new file mode 100644 index 0000000..7b79dbe --- /dev/null +++ b/examples/positioning/weatherinfo/weatherinfo.qrc @@ -0,0 +1,20 @@ + + + weatherinfo.qml + components/BigForecastIcon.qml + components/ForecastIcon.qml + components/WeatherIcon.qml + icons/weather-few-clouds.png + icons/weather-fog.png + icons/weather-haze.png + icons/weather-icy.png + icons/weather-overcast.png + icons/weather-showers.png + icons/weather-sleet.png + icons/weather-snow.png + icons/weather-storm.png + icons/weather-sunny-very-few-clouds.png + icons/weather-sunny.png + icons/weather-thundershower.png + + diff --git a/include/QtLocation/5.11.3/QtLocation/private/error_messages_p.h b/include/QtLocation/5.11.3/QtLocation/private/error_messages_p.h new file mode 100644 index 0000000..a324afd --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/error_messages_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/error_messages_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/locationvaluetypehelper_p.h b/include/QtLocation/5.11.3/QtLocation/private/locationvaluetypehelper_p.h new file mode 100644 index 0000000..231185a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/locationvaluetypehelper_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/locationvaluetypehelper_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/mapitemviewdelegateincubator_p.h b/include/QtLocation/5.11.3/QtLocation/private/mapitemviewdelegateincubator_p.h new file mode 100644 index 0000000..8ca4b0b --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/mapitemviewdelegateincubator_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/mapitemviewdelegateincubator_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qabstractgeotilecache_p.h b/include/QtLocation/5.11.3/QtLocation/private/qabstractgeotilecache_p.h new file mode 100644 index 0000000..c483a2f --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qabstractgeotilecache_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qabstractgeotilecache_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qcache3q_p.h b/include/QtLocation/5.11.3/QtLocation/private/qcache3q_p.h new file mode 100644 index 0000000..2f3ebc0 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qcache3q_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qcache3q_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecategory_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecategory_p.h new file mode 100644 index 0000000..c248e92 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecategory_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativecategory_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecirclemapitem_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecirclemapitem_p.h new file mode 100644 index 0000000..78e9551 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecirclemapitem_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativecirclemapitem_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecontactdetail_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecontactdetail_p.h new file mode 100644 index 0000000..fcb4404 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativecontactdetail_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativecontactdetail_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeocodemodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeocodemodel_p.h new file mode 100644 index 0000000..f5b5f6d --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeocodemodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeocodemodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaneuver_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaneuver_p.h new file mode 100644 index 0000000..5c465b2 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaneuver_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomaneuver_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomap_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomap_p.h new file mode 100644 index 0000000..aac9d61 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomap_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomap_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapcopyrightsnotice_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapcopyrightsnotice_p.h new file mode 100644 index 0000000..d2b8ad5 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapcopyrightsnotice_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitembase_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitembase_p.h new file mode 100644 index 0000000..4e7f2cc --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitembase_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomapitembase_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemgroup_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemgroup_p.h new file mode 100644 index 0000000..6eaad94 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemgroup_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomapitemgroup_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemview_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemview_p.h new file mode 100644 index 0000000..2b4c7b7 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapitemview_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomapitemview_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapparameter_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapparameter_p.h new file mode 100644 index 0000000..3c13672 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapparameter_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomapparameter_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapquickitem_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapquickitem_p.h new file mode 100644 index 0000000..8a09296 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomapquickitem_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomapquickitem_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaptype_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaptype_p.h new file mode 100644 index 0000000..7091377 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeomaptype_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeomaptype_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroute_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroute_p.h new file mode 100644 index 0000000..eea7f78 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroute_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeoroute_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutemodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutemodel_p.h new file mode 100644 index 0000000..137ec32 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutemodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeoroutemodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutesegment_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutesegment_p.h new file mode 100644 index 0000000..7b5811f --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoroutesegment_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeoroutesegment_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoserviceprovider_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoserviceprovider_p.h new file mode 100644 index 0000000..cce0215 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativegeoserviceprovider_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p.h new file mode 100644 index 0000000..62409a4 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qdeclarativenavigator_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p_p.h new file mode 100644 index 0000000..00dc4b9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativenavigator_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qdeclarativenavigator_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeperiod_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeperiod_p.h new file mode 100644 index 0000000..df1f3b2 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeperiod_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeperiod_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplace_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplace_p.h new file mode 100644 index 0000000..027d4f1 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplace_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeplace_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceattribute_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceattribute_p.h new file mode 100644 index 0000000..e39588a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceattribute_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeplaceattribute_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplacecontentmodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplacecontentmodel_p.h new file mode 100644 index 0000000..2eda492 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplacecontentmodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceeditorialmodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceeditorialmodel_p.h new file mode 100644 index 0000000..a97da4b --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceeditorialmodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceicon_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceicon_p.h new file mode 100644 index 0000000..a3522c6 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceicon_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeplaceicon_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceimagemodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceimagemodel_p.h new file mode 100644 index 0000000..169a543 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceimagemodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceuser_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceuser_p.h new file mode 100644 index 0000000..5633b5f --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeplaceuser_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeplaceuser_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolygonmapitem_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolygonmapitem_p.h new file mode 100644 index 0000000..3b850bc --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolygonmapitem_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativepolygonmapitem_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolylinemapitem_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolylinemapitem_p.h new file mode 100644 index 0000000..c34c4a7 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativepolylinemapitem_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativepolylinemapitem_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeratings_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeratings_p.h new file mode 100644 index 0000000..6d4d38b --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeratings_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativeratings_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativerectanglemapitem_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativerectanglemapitem_p.h new file mode 100644 index 0000000..8fab63a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativerectanglemapitem_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativerectanglemapitem_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativereviewmodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativereviewmodel_p.h new file mode 100644 index 0000000..33dbb08 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativereviewmodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativereviewmodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeroutemapitem_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeroutemapitem_p.h new file mode 100644 index 0000000..c9107a1 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativeroutemapitem_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qdeclarativeroutemapitem_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchmodelbase_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchmodelbase_p.h new file mode 100644 index 0000000..fae2832 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchmodelbase_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchresultmodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchresultmodel_p.h new file mode 100644 index 0000000..0401828 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchresultmodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchsuggestionmodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchsuggestionmodel_p.h new file mode 100644 index 0000000..26275c5 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesearchsuggestionmodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupplier_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupplier_p.h new file mode 100644 index 0000000..ef78d04 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupplier_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativesupplier_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupportedcategoriesmodel_p.h b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupportedcategoriesmodel_p.h new file mode 100644 index 0000000..3f7f6ec --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qdeclarativesupportedcategoriesmodel_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeocameracapabilities_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeocameracapabilities_p.h new file mode 100644 index 0000000..c237979 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeocameracapabilities_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeocameracapabilities_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeocameradata_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeocameradata_p.h new file mode 100644 index 0000000..0639753 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeocameradata_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeocameradata_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeocameratiles_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeocameratiles_p.h new file mode 100644 index 0000000..80f6aaf --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeocameratiles_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeocameratiles_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeocodereply_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeocodereply_p.h new file mode 100644 index 0000000..2524050 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeocodereply_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeocodereply_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanager_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanager_p.h new file mode 100644 index 0000000..043ed36 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanager_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeocodingmanager_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanagerengine_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanagerengine_p.h new file mode 100644 index 0000000..5ce24ee --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeocodingmanagerengine_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeocodingmanagerengine_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeofiletilecache_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeofiletilecache_p.h new file mode 100644 index 0000000..9fbe35a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeofiletilecache_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeofiletilecache_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomaneuver_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomaneuver_p.h new file mode 100644 index 0000000..cd5008e --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomaneuver_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomaneuver_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomap_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomap_p.h new file mode 100644 index 0000000..b7327e4 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomap_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomap_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomap_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomap_p_p.h new file mode 100644 index 0000000..3407c69 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomap_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomap_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomapitemgeometry_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomapitemgeometry_p.h new file mode 100644 index 0000000..7b50bc0 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomapitemgeometry_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qgeomapitemgeometry_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p.h new file mode 100644 index 0000000..88e6e7b --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qgeomapobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p_p.h new file mode 100644 index 0000000..7624327 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomapobject_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qgeomapobject_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomapobjectqsgsupport_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomapobjectqsgsupport_p.h new file mode 100644 index 0000000..4d3fdd8 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomapobjectqsgsupport_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qsg/qgeomapobjectqsgsupport_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomapparameter_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomapparameter_p.h new file mode 100644 index 0000000..007128a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomapparameter_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomapparameter_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p.h new file mode 100644 index 0000000..9b03ed7 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomappingmanager_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p_p.h new file mode 100644 index 0000000..a9d965a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanager_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomappingmanager_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p.h new file mode 100644 index 0000000..a1dd77b --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomappingmanagerengine_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p_p.h new file mode 100644 index 0000000..7d99d28 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomappingmanagerengine_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomappingmanagerengine_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p.h new file mode 100644 index 0000000..d6f8348 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomaptype_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p_p.h new file mode 100644 index 0000000..93fb3ba --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeomaptype_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeomaptype_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeoprojection_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeoprojection_p.h new file mode 100644 index 0000000..3a8abf9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeoprojection_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeoprojection_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeoroute_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeoroute_p.h new file mode 100644 index 0000000..aed5df1 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeoroute_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeoroute_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p.h new file mode 100644 index 0000000..75a676d --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeorouteparser_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p_p.h new file mode 100644 index 0000000..a40608f --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparser_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeorouteparser_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv4_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv4_p.h new file mode 100644 index 0000000..0882302 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv4_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeorouteparserosrmv4_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv5_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv5_p.h new file mode 100644 index 0000000..d16d23f --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeorouteparserosrmv5_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeorouteparserosrmv5_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeoroutereply_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutereply_p.h new file mode 100644 index 0000000..73e5640 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutereply_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeoroutereply_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeorouterequest_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeorouterequest_p.h new file mode 100644 index 0000000..00f7941 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeorouterequest_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeorouterequest_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeoroutesegment_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutesegment_p.h new file mode 100644 index 0000000..77cc84a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutesegment_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeoroutesegment_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanager_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanager_p.h new file mode 100644 index 0000000..1824922 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanager_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeoroutingmanager_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanagerengine_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanagerengine_p.h new file mode 100644 index 0000000..6f11de9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeoroutingmanagerengine_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeoroutingmanagerengine_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeoserviceprovider_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeoserviceprovider_p.h new file mode 100644 index 0000000..88f9999 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeoserviceprovider_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeoserviceprovider_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p.h new file mode 100644 index 0000000..718b0a3 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotiledmap_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p_p.h new file mode 100644 index 0000000..10bf8bb --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmap_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotiledmap_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmaplabs_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmaplabs_p.h new file mode 100644 index 0000000..2da4a5d --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmaplabs_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qgeotiledmaplabs_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p.h new file mode 100644 index 0000000..8cda0fe --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotiledmappingmanagerengine_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p_p.h new file mode 100644 index 0000000..1ad7d38 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmappingmanagerengine_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotiledmappingmanagerengine_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p.h new file mode 100644 index 0000000..90e0c73 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotiledmapreply_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p_p.h new file mode 100644 index 0000000..6563bf2 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapreply_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotiledmapreply_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapscene_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapscene_p.h new file mode 100644 index 0000000..edf39c9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotiledmapscene_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotiledmapscene_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p.h new file mode 100644 index 0000000..5e921b5 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotilefetcher_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p_p.h new file mode 100644 index 0000000..c183b71 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotilefetcher_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotilefetcher_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotilerequestmanager_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotilerequestmanager_p.h new file mode 100644 index 0000000..8872823 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotilerequestmanager_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotilerequestmanager_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p.h new file mode 100644 index 0000000..7737cc7 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotilespec_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p_p.h new file mode 100644 index 0000000..8f318b6 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qgeotilespec_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qgeotilespec_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qlocationglobal_p.h b/include/QtLocation/5.11.3/QtLocation/private/qlocationglobal_p.h new file mode 100644 index 0000000..0592935 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qlocationglobal_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/qlocationglobal_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p.h new file mode 100644 index 0000000..a6ad414 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmapcircleobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p_p.h new file mode 100644 index 0000000..cce19aa --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobject_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmapcircleobject_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobjectqsg_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobjectqsg_p_p.h new file mode 100644 index 0000000..4978925 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapcircleobjectqsg_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qsg/qmapcircleobjectqsg_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p.h new file mode 100644 index 0000000..72676e9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmapiconobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p_p.h new file mode 100644 index 0000000..6b96f1b --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapiconobject_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmapiconobject_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapiconobjectqsg_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapiconobjectqsg_p_p.h new file mode 100644 index 0000000..e687bb9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapiconobjectqsg_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qsg/qmapiconobjectqsg_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p.h new file mode 100644 index 0000000..5a7ab25 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmapobjectview_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p_p.h new file mode 100644 index 0000000..3576f41 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmapobjectview_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmapobjectview_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p.h new file mode 100644 index 0000000..035e405 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmappolygonobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p_p.h new file mode 100644 index 0000000..21925a0 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobject_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmappolygonobject_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobjectqsg_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobjectqsg_p_p.h new file mode 100644 index 0000000..4cdd75a --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmappolygonobjectqsg_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qsg/qmappolygonobjectqsg_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p.h new file mode 100644 index 0000000..7caa6c5 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmappolylineobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p_p.h new file mode 100644 index 0000000..b036d83 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobject_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmappolylineobject_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobjectqsg_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobjectqsg_p_p.h new file mode 100644 index 0000000..3942003 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmappolylineobjectqsg_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qsg/qmappolylineobjectqsg_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p.h new file mode 100644 index 0000000..220827e --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmaprouteobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p_p.h new file mode 100644 index 0000000..092ddeb --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobject_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qmaprouteobject_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobjectqsg_p_p.h b/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobjectqsg_p_p.h new file mode 100644 index 0000000..e3bed34 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qmaprouteobjectqsg_p_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qsg/qmaprouteobjectqsg_p_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qnavigationmanager_p.h b/include/QtLocation/5.11.3/QtLocation/private/qnavigationmanager_p.h new file mode 100644 index 0000000..f919f75 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qnavigationmanager_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qnavigationmanager_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qnavigationmanagerengine_p.h b/include/QtLocation/5.11.3/QtLocation/private/qnavigationmanagerengine_p.h new file mode 100644 index 0000000..be6ece5 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qnavigationmanagerengine_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/maps/qnavigationmanagerengine_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qparameterizableobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qparameterizableobject_p.h new file mode 100644 index 0000000..906804f --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qparameterizableobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qparameterizableobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplace_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplace_p.h new file mode 100644 index 0000000..1aabde7 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplace_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplace_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceattribute_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceattribute_p.h new file mode 100644 index 0000000..ce8bd7c --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceattribute_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceattribute_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacecategory_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacecategory_p.h new file mode 100644 index 0000000..ed94a62 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacecategory_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacecategory_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacecontactdetail_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacecontactdetail_p.h new file mode 100644 index 0000000..c8833e9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacecontactdetail_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacecontactdetail_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacecontent_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacecontent_p.h new file mode 100644 index 0000000..4ccd7c8 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacecontent_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacecontent_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacecontentrequest_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacecontentrequest_p.h new file mode 100644 index 0000000..64ca72d --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacecontentrequest_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacecontentrequest_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceeditorial_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceeditorial_p.h new file mode 100644 index 0000000..c4caf2e --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceeditorial_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceeditorial_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceicon_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceicon_p.h new file mode 100644 index 0000000..37a951d --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceicon_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceicon_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceimage_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceimage_p.h new file mode 100644 index 0000000..9de1983 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceimage_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceimage_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacemanagerengine_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacemanagerengine_p.h new file mode 100644 index 0000000..2e1be79 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacemanagerengine_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacemanagerengine_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceproposedsearchresult_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceproposedsearchresult_p.h new file mode 100644 index 0000000..b9e81b0 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceproposedsearchresult_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceproposedsearchresult_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceratings_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceratings_p.h new file mode 100644 index 0000000..3904253 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceratings_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceratings_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacereply_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacereply_p.h new file mode 100644 index 0000000..fb0e273 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacereply_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacereply_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceresult_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceresult_p.h new file mode 100644 index 0000000..94132fe --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceresult_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceresult_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacereview_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacereview_p.h new file mode 100644 index 0000000..94552a3 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacereview_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacereview_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacesearchresult_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacesearchresult_p.h new file mode 100644 index 0000000..1b7404e --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacesearchresult_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacesearchresult_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplacesupplier_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplacesupplier_p.h new file mode 100644 index 0000000..718cc74 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplacesupplier_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplacesupplier_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qplaceuser_p.h b/include/QtLocation/5.11.3/QtLocation/private/qplaceuser_p.h new file mode 100644 index 0000000..0dc4089 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qplaceuser_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/qplaceuser_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qqsgmapobject_p.h b/include/QtLocation/5.11.3/QtLocation/private/qqsgmapobject_p.h new file mode 100644 index 0000000..b95fee4 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qqsgmapobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/labs/qsg/qqsgmapobject_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/qquickgeomapgesturearea_p.h b/include/QtLocation/5.11.3/QtLocation/private/qquickgeomapgesturearea_p.h new file mode 100644 index 0000000..04e7e71 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/qquickgeomapgesturearea_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/declarativemaps/qquickgeomapgesturearea_p.h" diff --git a/include/QtLocation/5.11.3/QtLocation/private/unsupportedreplies_p.h b/include/QtLocation/5.11.3/QtLocation/private/unsupportedreplies_p.h new file mode 100644 index 0000000..396fcf9 --- /dev/null +++ b/include/QtLocation/5.11.3/QtLocation/private/unsupportedreplies_p.h @@ -0,0 +1 @@ +#include "../../../../../src/location/places/unsupportedreplies_p.h" diff --git a/include/QtLocation/QGeoCodeReply b/include/QtLocation/QGeoCodeReply new file mode 100644 index 0000000..14193f8 --- /dev/null +++ b/include/QtLocation/QGeoCodeReply @@ -0,0 +1 @@ +#include "qgeocodereply.h" diff --git a/include/QtLocation/QGeoCodingManager b/include/QtLocation/QGeoCodingManager new file mode 100644 index 0000000..ed7d721 --- /dev/null +++ b/include/QtLocation/QGeoCodingManager @@ -0,0 +1 @@ +#include "qgeocodingmanager.h" diff --git a/include/QtLocation/QGeoCodingManagerEngine b/include/QtLocation/QGeoCodingManagerEngine new file mode 100644 index 0000000..69303d2 --- /dev/null +++ b/include/QtLocation/QGeoCodingManagerEngine @@ -0,0 +1 @@ +#include "qgeocodingmanagerengine.h" diff --git a/include/QtLocation/QGeoManeuver b/include/QtLocation/QGeoManeuver new file mode 100644 index 0000000..a3ff6a5 --- /dev/null +++ b/include/QtLocation/QGeoManeuver @@ -0,0 +1 @@ +#include "qgeomaneuver.h" diff --git a/include/QtLocation/QGeoRoute b/include/QtLocation/QGeoRoute new file mode 100644 index 0000000..fd6690a --- /dev/null +++ b/include/QtLocation/QGeoRoute @@ -0,0 +1 @@ +#include "qgeoroute.h" diff --git a/include/QtLocation/QGeoRouteReply b/include/QtLocation/QGeoRouteReply new file mode 100644 index 0000000..6109d67 --- /dev/null +++ b/include/QtLocation/QGeoRouteReply @@ -0,0 +1 @@ +#include "qgeoroutereply.h" diff --git a/include/QtLocation/QGeoRouteRequest b/include/QtLocation/QGeoRouteRequest new file mode 100644 index 0000000..221969b --- /dev/null +++ b/include/QtLocation/QGeoRouteRequest @@ -0,0 +1 @@ +#include "qgeorouterequest.h" diff --git a/include/QtLocation/QGeoRouteSegment b/include/QtLocation/QGeoRouteSegment new file mode 100644 index 0000000..74590d6 --- /dev/null +++ b/include/QtLocation/QGeoRouteSegment @@ -0,0 +1 @@ +#include "qgeoroutesegment.h" diff --git a/include/QtLocation/QGeoRoutingManager b/include/QtLocation/QGeoRoutingManager new file mode 100644 index 0000000..76e5224 --- /dev/null +++ b/include/QtLocation/QGeoRoutingManager @@ -0,0 +1 @@ +#include "qgeoroutingmanager.h" diff --git a/include/QtLocation/QGeoRoutingManagerEngine b/include/QtLocation/QGeoRoutingManagerEngine new file mode 100644 index 0000000..300a668 --- /dev/null +++ b/include/QtLocation/QGeoRoutingManagerEngine @@ -0,0 +1 @@ +#include "qgeoroutingmanagerengine.h" diff --git a/include/QtLocation/QGeoServiceProvider b/include/QtLocation/QGeoServiceProvider new file mode 100644 index 0000000..d5b38bc --- /dev/null +++ b/include/QtLocation/QGeoServiceProvider @@ -0,0 +1 @@ +#include "qgeoserviceprovider.h" diff --git a/include/QtLocation/QGeoServiceProviderFactory b/include/QtLocation/QGeoServiceProviderFactory new file mode 100644 index 0000000..9bee8fb --- /dev/null +++ b/include/QtLocation/QGeoServiceProviderFactory @@ -0,0 +1 @@ +#include "qgeoserviceproviderfactory.h" diff --git a/include/QtLocation/QLocation b/include/QtLocation/QLocation new file mode 100644 index 0000000..e433f1e --- /dev/null +++ b/include/QtLocation/QLocation @@ -0,0 +1 @@ +#include "qlocation.h" diff --git a/include/QtLocation/QPlace b/include/QtLocation/QPlace new file mode 100644 index 0000000..51b4bac --- /dev/null +++ b/include/QtLocation/QPlace @@ -0,0 +1 @@ +#include "qplace.h" diff --git a/include/QtLocation/QPlaceAttribute b/include/QtLocation/QPlaceAttribute new file mode 100644 index 0000000..05d36df --- /dev/null +++ b/include/QtLocation/QPlaceAttribute @@ -0,0 +1 @@ +#include "qplaceattribute.h" diff --git a/include/QtLocation/QPlaceCategory b/include/QtLocation/QPlaceCategory new file mode 100644 index 0000000..959738a --- /dev/null +++ b/include/QtLocation/QPlaceCategory @@ -0,0 +1 @@ +#include "qplacecategory.h" diff --git a/include/QtLocation/QPlaceContactDetail b/include/QtLocation/QPlaceContactDetail new file mode 100644 index 0000000..4ff2d2d --- /dev/null +++ b/include/QtLocation/QPlaceContactDetail @@ -0,0 +1 @@ +#include "qplacecontactdetail.h" diff --git a/include/QtLocation/QPlaceContent b/include/QtLocation/QPlaceContent new file mode 100644 index 0000000..7ebb145 --- /dev/null +++ b/include/QtLocation/QPlaceContent @@ -0,0 +1 @@ +#include "qplacecontent.h" diff --git a/include/QtLocation/QPlaceContentReply b/include/QtLocation/QPlaceContentReply new file mode 100644 index 0000000..67fc8a8 --- /dev/null +++ b/include/QtLocation/QPlaceContentReply @@ -0,0 +1 @@ +#include "qplacecontentreply.h" diff --git a/include/QtLocation/QPlaceContentRequest b/include/QtLocation/QPlaceContentRequest new file mode 100644 index 0000000..43459f4 --- /dev/null +++ b/include/QtLocation/QPlaceContentRequest @@ -0,0 +1 @@ +#include "qplacecontentrequest.h" diff --git a/include/QtLocation/QPlaceDetailsReply b/include/QtLocation/QPlaceDetailsReply new file mode 100644 index 0000000..1455310 --- /dev/null +++ b/include/QtLocation/QPlaceDetailsReply @@ -0,0 +1 @@ +#include "qplacedetailsreply.h" diff --git a/include/QtLocation/QPlaceEditorial b/include/QtLocation/QPlaceEditorial new file mode 100644 index 0000000..d1a232a --- /dev/null +++ b/include/QtLocation/QPlaceEditorial @@ -0,0 +1 @@ +#include "qplaceeditorial.h" diff --git a/include/QtLocation/QPlaceIcon b/include/QtLocation/QPlaceIcon new file mode 100644 index 0000000..fae7a4e --- /dev/null +++ b/include/QtLocation/QPlaceIcon @@ -0,0 +1 @@ +#include "qplaceicon.h" diff --git a/include/QtLocation/QPlaceIdReply b/include/QtLocation/QPlaceIdReply new file mode 100644 index 0000000..8709b65 --- /dev/null +++ b/include/QtLocation/QPlaceIdReply @@ -0,0 +1 @@ +#include "qplaceidreply.h" diff --git a/include/QtLocation/QPlaceImage b/include/QtLocation/QPlaceImage new file mode 100644 index 0000000..606c306 --- /dev/null +++ b/include/QtLocation/QPlaceImage @@ -0,0 +1 @@ +#include "qplaceimage.h" diff --git a/include/QtLocation/QPlaceManager b/include/QtLocation/QPlaceManager new file mode 100644 index 0000000..7973c48 --- /dev/null +++ b/include/QtLocation/QPlaceManager @@ -0,0 +1 @@ +#include "qplacemanager.h" diff --git a/include/QtLocation/QPlaceManagerEngine b/include/QtLocation/QPlaceManagerEngine new file mode 100644 index 0000000..27f07fc --- /dev/null +++ b/include/QtLocation/QPlaceManagerEngine @@ -0,0 +1 @@ +#include "qplacemanagerengine.h" diff --git a/include/QtLocation/QPlaceMatchReply b/include/QtLocation/QPlaceMatchReply new file mode 100644 index 0000000..37ffdf2 --- /dev/null +++ b/include/QtLocation/QPlaceMatchReply @@ -0,0 +1 @@ +#include "qplacematchreply.h" diff --git a/include/QtLocation/QPlaceMatchRequest b/include/QtLocation/QPlaceMatchRequest new file mode 100644 index 0000000..ceaeb96 --- /dev/null +++ b/include/QtLocation/QPlaceMatchRequest @@ -0,0 +1 @@ +#include "qplacematchrequest.h" diff --git a/include/QtLocation/QPlaceProposedSearchResult b/include/QtLocation/QPlaceProposedSearchResult new file mode 100644 index 0000000..c43f2a6 --- /dev/null +++ b/include/QtLocation/QPlaceProposedSearchResult @@ -0,0 +1 @@ +#include "qplaceproposedsearchresult.h" diff --git a/include/QtLocation/QPlaceRatings b/include/QtLocation/QPlaceRatings new file mode 100644 index 0000000..4b7f63c --- /dev/null +++ b/include/QtLocation/QPlaceRatings @@ -0,0 +1 @@ +#include "qplaceratings.h" diff --git a/include/QtLocation/QPlaceReply b/include/QtLocation/QPlaceReply new file mode 100644 index 0000000..f0654b8 --- /dev/null +++ b/include/QtLocation/QPlaceReply @@ -0,0 +1 @@ +#include "qplacereply.h" diff --git a/include/QtLocation/QPlaceResult b/include/QtLocation/QPlaceResult new file mode 100644 index 0000000..4f5c8db --- /dev/null +++ b/include/QtLocation/QPlaceResult @@ -0,0 +1 @@ +#include "qplaceresult.h" diff --git a/include/QtLocation/QPlaceReview b/include/QtLocation/QPlaceReview new file mode 100644 index 0000000..83cccec --- /dev/null +++ b/include/QtLocation/QPlaceReview @@ -0,0 +1 @@ +#include "qplacereview.h" diff --git a/include/QtLocation/QPlaceSearchReply b/include/QtLocation/QPlaceSearchReply new file mode 100644 index 0000000..395a61a --- /dev/null +++ b/include/QtLocation/QPlaceSearchReply @@ -0,0 +1 @@ +#include "qplacesearchreply.h" diff --git a/include/QtLocation/QPlaceSearchRequest b/include/QtLocation/QPlaceSearchRequest new file mode 100644 index 0000000..b247dab --- /dev/null +++ b/include/QtLocation/QPlaceSearchRequest @@ -0,0 +1 @@ +#include "qplacesearchrequest.h" diff --git a/include/QtLocation/QPlaceSearchResult b/include/QtLocation/QPlaceSearchResult new file mode 100644 index 0000000..2298271 --- /dev/null +++ b/include/QtLocation/QPlaceSearchResult @@ -0,0 +1 @@ +#include "qplacesearchresult.h" diff --git a/include/QtLocation/QPlaceSearchSuggestionReply b/include/QtLocation/QPlaceSearchSuggestionReply new file mode 100644 index 0000000..e7a4f91 --- /dev/null +++ b/include/QtLocation/QPlaceSearchSuggestionReply @@ -0,0 +1 @@ +#include "qplacesearchsuggestionreply.h" diff --git a/include/QtLocation/QPlaceSupplier b/include/QtLocation/QPlaceSupplier new file mode 100644 index 0000000..465b46d --- /dev/null +++ b/include/QtLocation/QPlaceSupplier @@ -0,0 +1 @@ +#include "qplacesupplier.h" diff --git a/include/QtLocation/QPlaceUser b/include/QtLocation/QPlaceUser new file mode 100644 index 0000000..5acba29 --- /dev/null +++ b/include/QtLocation/QPlaceUser @@ -0,0 +1 @@ +#include "qplaceuser.h" diff --git a/include/QtLocation/QtLocation b/include/QtLocation/QtLocation new file mode 100644 index 0000000..c5936ba --- /dev/null +++ b/include/QtLocation/QtLocation @@ -0,0 +1,47 @@ +#ifndef QT_QTLOCATION_MODULE_H +#define QT_QTLOCATION_MODULE_H +#include +#include "qlocationglobal.h" +#include "placemacro.h" +#include "qgeocodereply.h" +#include "qgeocodingmanager.h" +#include "qgeocodingmanagerengine.h" +#include "qgeomaneuver.h" +#include "qgeoroute.h" +#include "qgeoroutereply.h" +#include "qgeorouterequest.h" +#include "qgeoroutesegment.h" +#include "qgeoroutingmanager.h" +#include "qgeoroutingmanagerengine.h" +#include "qgeoserviceprovider.h" +#include "qgeoserviceproviderfactory.h" +#include "qlocation.h" +#include "qplace.h" +#include "qplaceattribute.h" +#include "qplacecategory.h" +#include "qplacecontactdetail.h" +#include "qplacecontent.h" +#include "qplacecontentreply.h" +#include "qplacecontentrequest.h" +#include "qplacedetailsreply.h" +#include "qplaceeditorial.h" +#include "qplaceicon.h" +#include "qplaceidreply.h" +#include "qplaceimage.h" +#include "qplacemanager.h" +#include "qplacemanagerengine.h" +#include "qplacematchreply.h" +#include "qplacematchrequest.h" +#include "qplaceproposedsearchresult.h" +#include "qplaceratings.h" +#include "qplacereply.h" +#include "qplaceresult.h" +#include "qplacereview.h" +#include "qplacesearchreply.h" +#include "qplacesearchrequest.h" +#include "qplacesearchresult.h" +#include "qplacesearchsuggestionreply.h" +#include "qplacesupplier.h" +#include "qplaceuser.h" +#include "qtlocationversion.h" +#endif diff --git a/include/QtLocation/QtLocationVersion b/include/QtLocation/QtLocationVersion new file mode 100644 index 0000000..8da2499 --- /dev/null +++ b/include/QtLocation/QtLocationVersion @@ -0,0 +1 @@ +#include "qtlocationversion.h" diff --git a/include/QtLocation/headers.pri b/include/QtLocation/headers.pri new file mode 100644 index 0000000..d75c3e1 --- /dev/null +++ b/include/QtLocation/headers.pri @@ -0,0 +1,6 @@ +SYNCQT.HEADER_FILES = qlocation.h qlocationglobal.h maps/qgeocodereply.h maps/qgeocodingmanager.h maps/qgeocodingmanagerengine.h maps/qgeomaneuver.h maps/qgeoroute.h maps/qgeoroutereply.h maps/qgeorouterequest.h maps/qgeoroutesegment.h maps/qgeoroutingmanager.h maps/qgeoroutingmanagerengine.h maps/qgeoserviceprovider.h maps/qgeoserviceproviderfactory.h places/placemacro.h places/qplace.h places/qplaceattribute.h places/qplacecategory.h places/qplacecontactdetail.h places/qplacecontent.h places/qplacecontentreply.h places/qplacecontentrequest.h places/qplacedetailsreply.h places/qplaceeditorial.h places/qplaceicon.h places/qplaceidreply.h places/qplaceimage.h places/qplacemanager.h places/qplacemanagerengine.h places/qplacematchreply.h places/qplacematchrequest.h places/qplaceproposedsearchresult.h places/qplaceratings.h places/qplacereply.h places/qplaceresult.h places/qplacereview.h places/qplacesearchreply.h places/qplacesearchrequest.h places/qplacesearchresult.h places/qplacesearchsuggestionreply.h places/qplacesupplier.h places/qplaceuser.h +SYNCQT.GENERATED_HEADER_FILES = QLocation QGeoCodeReply QGeoCodingManager QGeoCodingManagerEngine QGeoManeuver QGeoRoute QGeoRouteReply QGeoRouteRequest QGeoRouteSegment QGeoRoutingManager QGeoRoutingManagerEngine QGeoServiceProvider QGeoServiceProviderFactory QPlace QPlaceAttribute QPlaceCategory QPlaceContactDetail QPlaceContent QPlaceContentReply QPlaceContentRequest QPlaceDetailsReply QPlaceEditorial QPlaceIcon QPlaceIdReply QPlaceImage QPlaceManager QPlaceManagerEngine QPlaceMatchReply QPlaceMatchRequest QPlaceProposedSearchResult QPlaceRatings QPlaceReply QPlaceResult QPlaceReview QPlaceSearchReply QPlaceSearchRequest QPlaceSearchResult QPlaceSearchSuggestionReply QPlaceSupplier QPlaceUser qtlocationversion.h QtLocationVersion QtLocation +SYNCQT.PRIVATE_HEADER_FILES = qlocationglobal_p.h declarativemaps/error_messages_p.h declarativemaps/locationvaluetypehelper_p.h declarativemaps/mapitemviewdelegateincubator_p.h declarativemaps/qdeclarativecirclemapitem_p.h declarativemaps/qdeclarativegeocodemodel_p.h declarativemaps/qdeclarativegeomaneuver_p.h declarativemaps/qdeclarativegeomap_p.h declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h declarativemaps/qdeclarativegeomapitembase_p.h declarativemaps/qdeclarativegeomapitemgroup_p.h declarativemaps/qdeclarativegeomapitemview_p.h declarativemaps/qdeclarativegeomapparameter_p.h declarativemaps/qdeclarativegeomapquickitem_p.h declarativemaps/qdeclarativegeomaptype_p.h declarativemaps/qdeclarativegeoroute_p.h declarativemaps/qdeclarativegeoroutemodel_p.h declarativemaps/qdeclarativegeoroutesegment_p.h declarativemaps/qdeclarativegeoserviceprovider_p.h declarativemaps/qdeclarativepolygonmapitem_p.h declarativemaps/qdeclarativepolylinemapitem_p.h declarativemaps/qdeclarativerectanglemapitem_p.h declarativemaps/qdeclarativeroutemapitem_p.h declarativemaps/qgeomapitemgeometry_p.h declarativemaps/qgeomapobject_p.h declarativemaps/qgeomapobject_p_p.h declarativemaps/qparameterizableobject_p.h declarativemaps/qquickgeomapgesturearea_p.h declarativeplaces/qdeclarativecategory_p.h declarativeplaces/qdeclarativecontactdetail_p.h declarativeplaces/qdeclarativeperiod_p.h declarativeplaces/qdeclarativeplace_p.h declarativeplaces/qdeclarativeplaceattribute_p.h declarativeplaces/qdeclarativeplacecontentmodel_p.h declarativeplaces/qdeclarativeplaceeditorialmodel_p.h declarativeplaces/qdeclarativeplaceicon_p.h declarativeplaces/qdeclarativeplaceimagemodel_p.h declarativeplaces/qdeclarativeplaceuser_p.h declarativeplaces/qdeclarativeratings_p.h declarativeplaces/qdeclarativereviewmodel_p.h declarativeplaces/qdeclarativesearchmodelbase_p.h declarativeplaces/qdeclarativesearchresultmodel_p.h declarativeplaces/qdeclarativesearchsuggestionmodel_p.h declarativeplaces/qdeclarativesupplier_p.h declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h labs/qdeclarativenavigator_p.h labs/qdeclarativenavigator_p_p.h labs/qgeotiledmaplabs_p.h labs/qmapcircleobject_p.h labs/qmapcircleobject_p_p.h labs/qmapiconobject_p.h labs/qmapiconobject_p_p.h labs/qmapobjectview_p.h labs/qmapobjectview_p_p.h labs/qmappolygonobject_p.h labs/qmappolygonobject_p_p.h labs/qmappolylineobject_p.h labs/qmappolylineobject_p_p.h labs/qmaprouteobject_p.h labs/qmaprouteobject_p_p.h maps/qabstractgeotilecache_p.h maps/qcache3q_p.h maps/qgeocameracapabilities_p.h maps/qgeocameradata_p.h maps/qgeocameratiles_p.h maps/qgeocodereply_p.h maps/qgeocodingmanager_p.h maps/qgeocodingmanagerengine_p.h maps/qgeofiletilecache_p.h maps/qgeomaneuver_p.h maps/qgeomap_p.h maps/qgeomap_p_p.h maps/qgeomapparameter_p.h maps/qgeomappingmanager_p.h maps/qgeomappingmanager_p_p.h maps/qgeomappingmanagerengine_p.h maps/qgeomappingmanagerengine_p_p.h maps/qgeomaptype_p.h maps/qgeomaptype_p_p.h maps/qgeoprojection_p.h maps/qgeoroute_p.h maps/qgeorouteparser_p.h maps/qgeorouteparser_p_p.h maps/qgeorouteparserosrmv4_p.h maps/qgeorouteparserosrmv5_p.h maps/qgeoroutereply_p.h maps/qgeorouterequest_p.h maps/qgeoroutesegment_p.h maps/qgeoroutingmanager_p.h maps/qgeoroutingmanagerengine_p.h maps/qgeoserviceprovider_p.h maps/qgeotiledmap_p.h maps/qgeotiledmap_p_p.h maps/qgeotiledmappingmanagerengine_p.h maps/qgeotiledmappingmanagerengine_p_p.h maps/qgeotiledmapreply_p.h maps/qgeotiledmapreply_p_p.h maps/qgeotiledmapscene_p.h maps/qgeotilefetcher_p.h maps/qgeotilefetcher_p_p.h maps/qgeotilerequestmanager_p.h maps/qgeotilespec_p.h maps/qgeotilespec_p_p.h maps/qnavigationmanager_p.h maps/qnavigationmanagerengine_p.h places/qplace_p.h places/qplaceattribute_p.h places/qplacecategory_p.h places/qplacecontactdetail_p.h places/qplacecontent_p.h places/qplacecontentrequest_p.h places/qplaceeditorial_p.h places/qplaceicon_p.h places/qplaceimage_p.h places/qplacemanagerengine_p.h places/qplaceproposedsearchresult_p.h places/qplaceratings_p.h places/qplacereply_p.h places/qplaceresult_p.h places/qplacereview_p.h places/qplacesearchresult_p.h places/qplacesupplier_p.h places/qplaceuser_p.h places/unsupportedreplies_p.h labs/qsg/qgeomapobjectqsgsupport_p.h labs/qsg/qmapcircleobjectqsg_p_p.h labs/qsg/qmapiconobjectqsg_p_p.h labs/qsg/qmappolygonobjectqsg_p_p.h labs/qsg/qmappolylineobjectqsg_p_p.h labs/qsg/qmaprouteobjectqsg_p_p.h labs/qsg/qqsgmapobject_p.h +SYNCQT.QPA_HEADER_FILES = +SYNCQT.CLEAN_HEADER_FILES = qlocation.h qlocationglobal.h maps/qgeocodereply.h maps/qgeocodingmanager.h maps/qgeocodingmanagerengine.h maps/qgeomaneuver.h maps/qgeoroute.h maps/qgeoroutereply.h maps/qgeorouterequest.h maps/qgeoroutesegment.h maps/qgeoroutingmanager.h maps/qgeoroutingmanagerengine.h maps/qgeoserviceprovider.h maps/qgeoserviceproviderfactory.h places/placemacro.h places/qplace.h places/qplaceattribute.h places/qplacecategory.h places/qplacecontactdetail.h places/qplacecontent.h places/qplacecontentreply.h places/qplacecontentrequest.h places/qplacedetailsreply.h places/qplaceeditorial.h places/qplaceicon.h places/qplaceidreply.h places/qplaceimage.h places/qplacemanager.h places/qplacemanagerengine.h places/qplacematchreply.h places/qplacematchrequest.h places/qplaceproposedsearchresult.h places/qplaceratings.h places/qplacereply.h places/qplaceresult.h places/qplacereview.h places/qplacesearchreply.h places/qplacesearchrequest.h places/qplacesearchresult.h places/qplacesearchsuggestionreply.h places/qplacesupplier.h places/qplaceuser.h +SYNCQT.INJECTIONS = diff --git a/include/QtLocation/placemacro.h b/include/QtLocation/placemacro.h new file mode 100644 index 0000000..19404ab --- /dev/null +++ b/include/QtLocation/placemacro.h @@ -0,0 +1 @@ +#include "../../src/location/places/placemacro.h" diff --git a/include/QtLocation/qgeocodereply.h b/include/QtLocation/qgeocodereply.h new file mode 100644 index 0000000..ba6d712 --- /dev/null +++ b/include/QtLocation/qgeocodereply.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeocodereply.h" diff --git a/include/QtLocation/qgeocodingmanager.h b/include/QtLocation/qgeocodingmanager.h new file mode 100644 index 0000000..7ddf50f --- /dev/null +++ b/include/QtLocation/qgeocodingmanager.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeocodingmanager.h" diff --git a/include/QtLocation/qgeocodingmanagerengine.h b/include/QtLocation/qgeocodingmanagerengine.h new file mode 100644 index 0000000..18d06ef --- /dev/null +++ b/include/QtLocation/qgeocodingmanagerengine.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeocodingmanagerengine.h" diff --git a/include/QtLocation/qgeomaneuver.h b/include/QtLocation/qgeomaneuver.h new file mode 100644 index 0000000..72e145b --- /dev/null +++ b/include/QtLocation/qgeomaneuver.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeomaneuver.h" diff --git a/include/QtLocation/qgeoroute.h b/include/QtLocation/qgeoroute.h new file mode 100644 index 0000000..b0bdf34 --- /dev/null +++ b/include/QtLocation/qgeoroute.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeoroute.h" diff --git a/include/QtLocation/qgeoroutereply.h b/include/QtLocation/qgeoroutereply.h new file mode 100644 index 0000000..34d8e88 --- /dev/null +++ b/include/QtLocation/qgeoroutereply.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeoroutereply.h" diff --git a/include/QtLocation/qgeorouterequest.h b/include/QtLocation/qgeorouterequest.h new file mode 100644 index 0000000..33bf11d --- /dev/null +++ b/include/QtLocation/qgeorouterequest.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeorouterequest.h" diff --git a/include/QtLocation/qgeoroutesegment.h b/include/QtLocation/qgeoroutesegment.h new file mode 100644 index 0000000..7205732 --- /dev/null +++ b/include/QtLocation/qgeoroutesegment.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeoroutesegment.h" diff --git a/include/QtLocation/qgeoroutingmanager.h b/include/QtLocation/qgeoroutingmanager.h new file mode 100644 index 0000000..8ae46be --- /dev/null +++ b/include/QtLocation/qgeoroutingmanager.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeoroutingmanager.h" diff --git a/include/QtLocation/qgeoroutingmanagerengine.h b/include/QtLocation/qgeoroutingmanagerengine.h new file mode 100644 index 0000000..4b26f14 --- /dev/null +++ b/include/QtLocation/qgeoroutingmanagerengine.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeoroutingmanagerengine.h" diff --git a/include/QtLocation/qgeoserviceprovider.h b/include/QtLocation/qgeoserviceprovider.h new file mode 100644 index 0000000..4aca205 --- /dev/null +++ b/include/QtLocation/qgeoserviceprovider.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeoserviceprovider.h" diff --git a/include/QtLocation/qgeoserviceproviderfactory.h b/include/QtLocation/qgeoserviceproviderfactory.h new file mode 100644 index 0000000..39a5e4c --- /dev/null +++ b/include/QtLocation/qgeoserviceproviderfactory.h @@ -0,0 +1 @@ +#include "../../src/location/maps/qgeoserviceproviderfactory.h" diff --git a/include/QtLocation/qlocation.h b/include/QtLocation/qlocation.h new file mode 100644 index 0000000..4c266a1 --- /dev/null +++ b/include/QtLocation/qlocation.h @@ -0,0 +1 @@ +#include "../../src/location/qlocation.h" diff --git a/include/QtLocation/qlocationglobal.h b/include/QtLocation/qlocationglobal.h new file mode 100644 index 0000000..9959740 --- /dev/null +++ b/include/QtLocation/qlocationglobal.h @@ -0,0 +1 @@ +#include "../../src/location/qlocationglobal.h" diff --git a/include/QtLocation/qplace.h b/include/QtLocation/qplace.h new file mode 100644 index 0000000..7ce3cf4 --- /dev/null +++ b/include/QtLocation/qplace.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplace.h" diff --git a/include/QtLocation/qplaceattribute.h b/include/QtLocation/qplaceattribute.h new file mode 100644 index 0000000..9ade0c5 --- /dev/null +++ b/include/QtLocation/qplaceattribute.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceattribute.h" diff --git a/include/QtLocation/qplacecategory.h b/include/QtLocation/qplacecategory.h new file mode 100644 index 0000000..310d15c --- /dev/null +++ b/include/QtLocation/qplacecategory.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacecategory.h" diff --git a/include/QtLocation/qplacecontactdetail.h b/include/QtLocation/qplacecontactdetail.h new file mode 100644 index 0000000..179040c --- /dev/null +++ b/include/QtLocation/qplacecontactdetail.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacecontactdetail.h" diff --git a/include/QtLocation/qplacecontent.h b/include/QtLocation/qplacecontent.h new file mode 100644 index 0000000..675d5ee --- /dev/null +++ b/include/QtLocation/qplacecontent.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacecontent.h" diff --git a/include/QtLocation/qplacecontentreply.h b/include/QtLocation/qplacecontentreply.h new file mode 100644 index 0000000..3129a8f --- /dev/null +++ b/include/QtLocation/qplacecontentreply.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacecontentreply.h" diff --git a/include/QtLocation/qplacecontentrequest.h b/include/QtLocation/qplacecontentrequest.h new file mode 100644 index 0000000..1578507 --- /dev/null +++ b/include/QtLocation/qplacecontentrequest.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacecontentrequest.h" diff --git a/include/QtLocation/qplacedetailsreply.h b/include/QtLocation/qplacedetailsreply.h new file mode 100644 index 0000000..586e16a --- /dev/null +++ b/include/QtLocation/qplacedetailsreply.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacedetailsreply.h" diff --git a/include/QtLocation/qplaceeditorial.h b/include/QtLocation/qplaceeditorial.h new file mode 100644 index 0000000..9dac346 --- /dev/null +++ b/include/QtLocation/qplaceeditorial.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceeditorial.h" diff --git a/include/QtLocation/qplaceicon.h b/include/QtLocation/qplaceicon.h new file mode 100644 index 0000000..558ac04 --- /dev/null +++ b/include/QtLocation/qplaceicon.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceicon.h" diff --git a/include/QtLocation/qplaceidreply.h b/include/QtLocation/qplaceidreply.h new file mode 100644 index 0000000..8d19185 --- /dev/null +++ b/include/QtLocation/qplaceidreply.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceidreply.h" diff --git a/include/QtLocation/qplaceimage.h b/include/QtLocation/qplaceimage.h new file mode 100644 index 0000000..856deb1 --- /dev/null +++ b/include/QtLocation/qplaceimage.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceimage.h" diff --git a/include/QtLocation/qplacemanager.h b/include/QtLocation/qplacemanager.h new file mode 100644 index 0000000..689e043 --- /dev/null +++ b/include/QtLocation/qplacemanager.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacemanager.h" diff --git a/include/QtLocation/qplacemanagerengine.h b/include/QtLocation/qplacemanagerengine.h new file mode 100644 index 0000000..8966e6d --- /dev/null +++ b/include/QtLocation/qplacemanagerengine.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacemanagerengine.h" diff --git a/include/QtLocation/qplacematchreply.h b/include/QtLocation/qplacematchreply.h new file mode 100644 index 0000000..123bd8f --- /dev/null +++ b/include/QtLocation/qplacematchreply.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacematchreply.h" diff --git a/include/QtLocation/qplacematchrequest.h b/include/QtLocation/qplacematchrequest.h new file mode 100644 index 0000000..bca1b69 --- /dev/null +++ b/include/QtLocation/qplacematchrequest.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacematchrequest.h" diff --git a/include/QtLocation/qplaceproposedsearchresult.h b/include/QtLocation/qplaceproposedsearchresult.h new file mode 100644 index 0000000..9a609b4 --- /dev/null +++ b/include/QtLocation/qplaceproposedsearchresult.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceproposedsearchresult.h" diff --git a/include/QtLocation/qplaceratings.h b/include/QtLocation/qplaceratings.h new file mode 100644 index 0000000..dc6ad10 --- /dev/null +++ b/include/QtLocation/qplaceratings.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceratings.h" diff --git a/include/QtLocation/qplacereply.h b/include/QtLocation/qplacereply.h new file mode 100644 index 0000000..96a2fef --- /dev/null +++ b/include/QtLocation/qplacereply.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacereply.h" diff --git a/include/QtLocation/qplaceresult.h b/include/QtLocation/qplaceresult.h new file mode 100644 index 0000000..48e4bed --- /dev/null +++ b/include/QtLocation/qplaceresult.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceresult.h" diff --git a/include/QtLocation/qplacereview.h b/include/QtLocation/qplacereview.h new file mode 100644 index 0000000..19273d9 --- /dev/null +++ b/include/QtLocation/qplacereview.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacereview.h" diff --git a/include/QtLocation/qplacesearchreply.h b/include/QtLocation/qplacesearchreply.h new file mode 100644 index 0000000..5e45879 --- /dev/null +++ b/include/QtLocation/qplacesearchreply.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacesearchreply.h" diff --git a/include/QtLocation/qplacesearchrequest.h b/include/QtLocation/qplacesearchrequest.h new file mode 100644 index 0000000..794ac51 --- /dev/null +++ b/include/QtLocation/qplacesearchrequest.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacesearchrequest.h" diff --git a/include/QtLocation/qplacesearchresult.h b/include/QtLocation/qplacesearchresult.h new file mode 100644 index 0000000..102d1e8 --- /dev/null +++ b/include/QtLocation/qplacesearchresult.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacesearchresult.h" diff --git a/include/QtLocation/qplacesearchsuggestionreply.h b/include/QtLocation/qplacesearchsuggestionreply.h new file mode 100644 index 0000000..72cdc6a --- /dev/null +++ b/include/QtLocation/qplacesearchsuggestionreply.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacesearchsuggestionreply.h" diff --git a/include/QtLocation/qplacesupplier.h b/include/QtLocation/qplacesupplier.h new file mode 100644 index 0000000..071102d --- /dev/null +++ b/include/QtLocation/qplacesupplier.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplacesupplier.h" diff --git a/include/QtLocation/qplaceuser.h b/include/QtLocation/qplaceuser.h new file mode 100644 index 0000000..88d3b28 --- /dev/null +++ b/include/QtLocation/qplaceuser.h @@ -0,0 +1 @@ +#include "../../src/location/places/qplaceuser.h" diff --git a/include/QtLocation/qtlocationversion.h b/include/QtLocation/qtlocationversion.h new file mode 100644 index 0000000..d816d03 --- /dev/null +++ b/include/QtLocation/qtlocationversion.h @@ -0,0 +1,9 @@ +/* This file was generated by syncqt. */ +#ifndef QT_QTLOCATION_VERSION_H +#define QT_QTLOCATION_VERSION_H + +#define QTLOCATION_VERSION_STR "5.11.3" + +#define QTLOCATION_VERSION 0x050B03 + +#endif // QT_QTLOCATION_VERSION_H diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qclipperutils_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qclipperutils_p.h new file mode 100644 index 0000000..876b17c --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qclipperutils_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qclipperutils_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeoaddress_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeoaddress_p.h new file mode 100644 index 0000000..56562a7 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeoaddress_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qdeclarativegeoaddress_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeolocation_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeolocation_p.h new file mode 100644 index 0000000..ce5bbf3 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qdeclarativegeolocation_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qdeclarativegeolocation_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qdoublematrix4x4_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qdoublematrix4x4_p.h new file mode 100644 index 0000000..9f9622b --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qdoublematrix4x4_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qdoublematrix4x4_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector2d_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector2d_p.h new file mode 100644 index 0000000..a679f2f --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector2d_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qdoublevector2d_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector3d_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector3d_p.h new file mode 100644 index 0000000..6b1a13c --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qdoublevector3d_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qdoublevector3d_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeoaddress_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeoaddress_p.h new file mode 100644 index 0000000..b494007 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeoaddress_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeoaddress_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeocircle_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeocircle_p.h new file mode 100644 index 0000000..80a2f05 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeocircle_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeocircle_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinate_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinate_p.h new file mode 100644 index 0000000..53b5c3b --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinate_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeocoordinate_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinateobject_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinateobject_p.h new file mode 100644 index 0000000..11739db --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeocoordinateobject_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeocoordinateobject_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeolocation_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeolocation_p.h new file mode 100644 index 0000000..15bd5a1 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeolocation_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeolocation_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeopath_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeopath_p.h new file mode 100644 index 0000000..a41c689 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeopath_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeopath_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfo_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfo_p.h new file mode 100644 index 0000000..d85beda --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfo_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeopositioninfo_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfosource_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfosource_p.h new file mode 100644 index 0000000..a43d355 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeopositioninfosource_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeopositioninfosource_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeorectangle_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeorectangle_p.h new file mode 100644 index 0000000..e4d2ac1 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeorectangle_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeorectangle_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qgeoshape_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qgeoshape_p.h new file mode 100644 index 0000000..ef4a35e --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qgeoshape_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qgeoshape_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qlocationdata_simulator_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qlocationdata_simulator_p.h new file mode 100644 index 0000000..ed0b911 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qlocationdata_simulator_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qlocationdata_simulator_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qlocationutils_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qlocationutils_p.h new file mode 100644 index 0000000..0c3d9cd --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qlocationutils_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qlocationutils_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qnmeapositioninfosource_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qnmeapositioninfosource_p.h new file mode 100644 index 0000000..2fd5904 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qnmeapositioninfosource_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qnmeapositioninfosource_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qpositioningglobal_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qpositioningglobal_p.h new file mode 100644 index 0000000..657c470 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qpositioningglobal_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qpositioningglobal_p.h" diff --git a/include/QtPositioning/5.11.3/QtPositioning/private/qwebmercator_p.h b/include/QtPositioning/5.11.3/QtPositioning/private/qwebmercator_p.h new file mode 100644 index 0000000..f2abab1 --- /dev/null +++ b/include/QtPositioning/5.11.3/QtPositioning/private/qwebmercator_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioning/qwebmercator_p.h" diff --git a/include/QtPositioning/QGeoAddress b/include/QtPositioning/QGeoAddress new file mode 100644 index 0000000..708bcab --- /dev/null +++ b/include/QtPositioning/QGeoAddress @@ -0,0 +1 @@ +#include "qgeoaddress.h" diff --git a/include/QtPositioning/QGeoAreaMonitorInfo b/include/QtPositioning/QGeoAreaMonitorInfo new file mode 100644 index 0000000..7e137e6 --- /dev/null +++ b/include/QtPositioning/QGeoAreaMonitorInfo @@ -0,0 +1 @@ +#include "qgeoareamonitorinfo.h" diff --git a/include/QtPositioning/QGeoAreaMonitorSource b/include/QtPositioning/QGeoAreaMonitorSource new file mode 100644 index 0000000..942ccf6 --- /dev/null +++ b/include/QtPositioning/QGeoAreaMonitorSource @@ -0,0 +1 @@ +#include "qgeoareamonitorsource.h" diff --git a/include/QtPositioning/QGeoCircle b/include/QtPositioning/QGeoCircle new file mode 100644 index 0000000..b77d827 --- /dev/null +++ b/include/QtPositioning/QGeoCircle @@ -0,0 +1 @@ +#include "qgeocircle.h" diff --git a/include/QtPositioning/QGeoCoordinate b/include/QtPositioning/QGeoCoordinate new file mode 100644 index 0000000..812fb2a --- /dev/null +++ b/include/QtPositioning/QGeoCoordinate @@ -0,0 +1 @@ +#include "qgeocoordinate.h" diff --git a/include/QtPositioning/QGeoLocation b/include/QtPositioning/QGeoLocation new file mode 100644 index 0000000..bd38c75 --- /dev/null +++ b/include/QtPositioning/QGeoLocation @@ -0,0 +1 @@ +#include "qgeolocation.h" diff --git a/include/QtPositioning/QGeoPath b/include/QtPositioning/QGeoPath new file mode 100644 index 0000000..bde3300 --- /dev/null +++ b/include/QtPositioning/QGeoPath @@ -0,0 +1 @@ +#include "qgeopath.h" diff --git a/include/QtPositioning/QGeoPolygon b/include/QtPositioning/QGeoPolygon new file mode 100644 index 0000000..e02b134 --- /dev/null +++ b/include/QtPositioning/QGeoPolygon @@ -0,0 +1 @@ +#include "qgeopolygon.h" diff --git a/include/QtPositioning/QGeoPolygonPrivate b/include/QtPositioning/QGeoPolygonPrivate new file mode 100644 index 0000000..e02b134 --- /dev/null +++ b/include/QtPositioning/QGeoPolygonPrivate @@ -0,0 +1 @@ +#include "qgeopolygon.h" diff --git a/include/QtPositioning/QGeoPositionInfo b/include/QtPositioning/QGeoPositionInfo new file mode 100644 index 0000000..d6feec9 --- /dev/null +++ b/include/QtPositioning/QGeoPositionInfo @@ -0,0 +1 @@ +#include "qgeopositioninfo.h" diff --git a/include/QtPositioning/QGeoPositionInfoSource b/include/QtPositioning/QGeoPositionInfoSource new file mode 100644 index 0000000..b7cc338 --- /dev/null +++ b/include/QtPositioning/QGeoPositionInfoSource @@ -0,0 +1 @@ +#include "qgeopositioninfosource.h" diff --git a/include/QtPositioning/QGeoPositionInfoSourceFactory b/include/QtPositioning/QGeoPositionInfoSourceFactory new file mode 100644 index 0000000..7448436 --- /dev/null +++ b/include/QtPositioning/QGeoPositionInfoSourceFactory @@ -0,0 +1 @@ +#include "qgeopositioninfosourcefactory.h" diff --git a/include/QtPositioning/QGeoRectangle b/include/QtPositioning/QGeoRectangle new file mode 100644 index 0000000..9a7d48f --- /dev/null +++ b/include/QtPositioning/QGeoRectangle @@ -0,0 +1 @@ +#include "qgeorectangle.h" diff --git a/include/QtPositioning/QGeoSatelliteInfo b/include/QtPositioning/QGeoSatelliteInfo new file mode 100644 index 0000000..a3fd47d --- /dev/null +++ b/include/QtPositioning/QGeoSatelliteInfo @@ -0,0 +1 @@ +#include "qgeosatelliteinfo.h" diff --git a/include/QtPositioning/QGeoSatelliteInfoSource b/include/QtPositioning/QGeoSatelliteInfoSource new file mode 100644 index 0000000..cc6ba9d --- /dev/null +++ b/include/QtPositioning/QGeoSatelliteInfoSource @@ -0,0 +1 @@ +#include "qgeosatelliteinfosource.h" diff --git a/include/QtPositioning/QGeoShape b/include/QtPositioning/QGeoShape new file mode 100644 index 0000000..d86ddc3 --- /dev/null +++ b/include/QtPositioning/QGeoShape @@ -0,0 +1 @@ +#include "qgeoshape.h" diff --git a/include/QtPositioning/QNmeaPositionInfoSource b/include/QtPositioning/QNmeaPositionInfoSource new file mode 100644 index 0000000..5c261c9 --- /dev/null +++ b/include/QtPositioning/QNmeaPositionInfoSource @@ -0,0 +1 @@ +#include "qnmeapositioninfosource.h" diff --git a/include/QtPositioning/QtPositioning b/include/QtPositioning/QtPositioning new file mode 100644 index 0000000..ea25c25 --- /dev/null +++ b/include/QtPositioning/QtPositioning @@ -0,0 +1,22 @@ +#ifndef QT_QTPOSITIONING_MODULE_H +#define QT_QTPOSITIONING_MODULE_H +#include +#include "qpositioningglobal.h" +#include "qgeoaddress.h" +#include "qgeoareamonitorinfo.h" +#include "qgeoareamonitorsource.h" +#include "qgeocircle.h" +#include "qgeocoordinate.h" +#include "qgeolocation.h" +#include "qgeopath.h" +#include "qgeopolygon.h" +#include "qgeopositioninfo.h" +#include "qgeopositioninfosource.h" +#include "qgeopositioninfosourcefactory.h" +#include "qgeorectangle.h" +#include "qgeosatelliteinfo.h" +#include "qgeosatelliteinfosource.h" +#include "qgeoshape.h" +#include "qnmeapositioninfosource.h" +#include "qtpositioningversion.h" +#endif diff --git a/include/QtPositioning/QtPositioningVersion b/include/QtPositioning/QtPositioningVersion new file mode 100644 index 0000000..2f99398 --- /dev/null +++ b/include/QtPositioning/QtPositioningVersion @@ -0,0 +1 @@ +#include "qtpositioningversion.h" diff --git a/include/QtPositioning/headers.pri b/include/QtPositioning/headers.pri new file mode 100644 index 0000000..89b0e8b --- /dev/null +++ b/include/QtPositioning/headers.pri @@ -0,0 +1,6 @@ +SYNCQT.HEADER_FILES = qgeoaddress.h qgeoareamonitorinfo.h qgeoareamonitorsource.h qgeocircle.h qgeocoordinate.h qgeolocation.h qgeopath.h qgeopolygon.h qgeopositioninfo.h qgeopositioninfosource.h qgeopositioninfosourcefactory.h qgeorectangle.h qgeosatelliteinfo.h qgeosatelliteinfosource.h qgeoshape.h qnmeapositioninfosource.h qpositioningglobal.h +SYNCQT.GENERATED_HEADER_FILES = QGeoAddress QGeoAreaMonitorInfo QGeoAreaMonitorSource QGeoCircle QGeoCoordinate QGeoLocation QGeoPath QGeoPolygonPrivate QGeoPolygon QGeoPositionInfo QGeoPositionInfoSource QGeoPositionInfoSourceFactory QGeoRectangle QGeoSatelliteInfo QGeoSatelliteInfoSource QGeoShape QNmeaPositionInfoSource qtpositioningversion.h QtPositioningVersion QtPositioning +SYNCQT.PRIVATE_HEADER_FILES = qclipperutils_p.h qdeclarativegeoaddress_p.h qdeclarativegeolocation_p.h qdoublematrix4x4_p.h qdoublevector2d_p.h qdoublevector3d_p.h qgeoaddress_p.h qgeocircle_p.h qgeocoordinate_p.h qgeocoordinateobject_p.h qgeolocation_p.h qgeopath_p.h qgeopositioninfo_p.h qgeopositioninfosource_p.h qgeorectangle_p.h qgeoshape_p.h qlocationdata_simulator_p.h qlocationutils_p.h qnmeapositioninfosource_p.h qpositioningglobal_p.h qwebmercator_p.h +SYNCQT.QPA_HEADER_FILES = +SYNCQT.CLEAN_HEADER_FILES = qgeoaddress.h qgeoareamonitorinfo.h qgeoareamonitorsource.h qgeocircle.h qgeocoordinate.h qgeolocation.h qgeopath.h qgeopolygon.h qgeopositioninfo.h qgeopositioninfosource.h qgeopositioninfosourcefactory.h qgeorectangle.h qgeosatelliteinfo.h qgeosatelliteinfosource.h qgeoshape.h qnmeapositioninfosource.h qpositioningglobal.h +SYNCQT.INJECTIONS = diff --git a/include/QtPositioning/qgeoaddress.h b/include/QtPositioning/qgeoaddress.h new file mode 100644 index 0000000..b8e0a99 --- /dev/null +++ b/include/QtPositioning/qgeoaddress.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeoaddress.h" diff --git a/include/QtPositioning/qgeoareamonitorinfo.h b/include/QtPositioning/qgeoareamonitorinfo.h new file mode 100644 index 0000000..f9a334c --- /dev/null +++ b/include/QtPositioning/qgeoareamonitorinfo.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeoareamonitorinfo.h" diff --git a/include/QtPositioning/qgeoareamonitorsource.h b/include/QtPositioning/qgeoareamonitorsource.h new file mode 100644 index 0000000..4555de8 --- /dev/null +++ b/include/QtPositioning/qgeoareamonitorsource.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeoareamonitorsource.h" diff --git a/include/QtPositioning/qgeocircle.h b/include/QtPositioning/qgeocircle.h new file mode 100644 index 0000000..f598c2c --- /dev/null +++ b/include/QtPositioning/qgeocircle.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeocircle.h" diff --git a/include/QtPositioning/qgeocoordinate.h b/include/QtPositioning/qgeocoordinate.h new file mode 100644 index 0000000..93b16d9 --- /dev/null +++ b/include/QtPositioning/qgeocoordinate.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeocoordinate.h" diff --git a/include/QtPositioning/qgeolocation.h b/include/QtPositioning/qgeolocation.h new file mode 100644 index 0000000..59429ea --- /dev/null +++ b/include/QtPositioning/qgeolocation.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeolocation.h" diff --git a/include/QtPositioning/qgeopath.h b/include/QtPositioning/qgeopath.h new file mode 100644 index 0000000..8e61b7e --- /dev/null +++ b/include/QtPositioning/qgeopath.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeopath.h" diff --git a/include/QtPositioning/qgeopolygon.h b/include/QtPositioning/qgeopolygon.h new file mode 100644 index 0000000..c9e8296 --- /dev/null +++ b/include/QtPositioning/qgeopolygon.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeopolygon.h" diff --git a/include/QtPositioning/qgeopositioninfo.h b/include/QtPositioning/qgeopositioninfo.h new file mode 100644 index 0000000..213fbb2 --- /dev/null +++ b/include/QtPositioning/qgeopositioninfo.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeopositioninfo.h" diff --git a/include/QtPositioning/qgeopositioninfosource.h b/include/QtPositioning/qgeopositioninfosource.h new file mode 100644 index 0000000..8f7082d --- /dev/null +++ b/include/QtPositioning/qgeopositioninfosource.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeopositioninfosource.h" diff --git a/include/QtPositioning/qgeopositioninfosourcefactory.h b/include/QtPositioning/qgeopositioninfosourcefactory.h new file mode 100644 index 0000000..e59bdd9 --- /dev/null +++ b/include/QtPositioning/qgeopositioninfosourcefactory.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeopositioninfosourcefactory.h" diff --git a/include/QtPositioning/qgeorectangle.h b/include/QtPositioning/qgeorectangle.h new file mode 100644 index 0000000..f4a6526 --- /dev/null +++ b/include/QtPositioning/qgeorectangle.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeorectangle.h" diff --git a/include/QtPositioning/qgeosatelliteinfo.h b/include/QtPositioning/qgeosatelliteinfo.h new file mode 100644 index 0000000..1c1d631 --- /dev/null +++ b/include/QtPositioning/qgeosatelliteinfo.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeosatelliteinfo.h" diff --git a/include/QtPositioning/qgeosatelliteinfosource.h b/include/QtPositioning/qgeosatelliteinfosource.h new file mode 100644 index 0000000..a3115b1 --- /dev/null +++ b/include/QtPositioning/qgeosatelliteinfosource.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeosatelliteinfosource.h" diff --git a/include/QtPositioning/qgeoshape.h b/include/QtPositioning/qgeoshape.h new file mode 100644 index 0000000..6e7d4db --- /dev/null +++ b/include/QtPositioning/qgeoshape.h @@ -0,0 +1 @@ +#include "../../src/positioning/qgeoshape.h" diff --git a/include/QtPositioning/qnmeapositioninfosource.h b/include/QtPositioning/qnmeapositioninfosource.h new file mode 100644 index 0000000..6f5d63d --- /dev/null +++ b/include/QtPositioning/qnmeapositioninfosource.h @@ -0,0 +1 @@ +#include "../../src/positioning/qnmeapositioninfosource.h" diff --git a/include/QtPositioning/qpositioningglobal.h b/include/QtPositioning/qpositioningglobal.h new file mode 100644 index 0000000..a291214 --- /dev/null +++ b/include/QtPositioning/qpositioningglobal.h @@ -0,0 +1 @@ +#include "../../src/positioning/qpositioningglobal.h" diff --git a/include/QtPositioning/qtpositioningversion.h b/include/QtPositioning/qtpositioningversion.h new file mode 100644 index 0000000..4c5b2ae --- /dev/null +++ b/include/QtPositioning/qtpositioningversion.h @@ -0,0 +1,9 @@ +/* This file was generated by syncqt. */ +#ifndef QT_QTPOSITIONING_VERSION_H +#define QT_QTPOSITIONING_VERSION_H + +#define QTPOSITIONING_VERSION_STR "5.11.3" + +#define QTPOSITIONING_VERSION 0x050B03 + +#endif // QT_QTPOSITIONING_VERSION_H diff --git a/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativeposition_p.h b/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativeposition_p.h new file mode 100644 index 0000000..151ff26 --- /dev/null +++ b/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativeposition_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioningquick/qdeclarativeposition_p.h" diff --git a/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativepositionsource_p.h b/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativepositionsource_p.h new file mode 100644 index 0000000..870a4fa --- /dev/null +++ b/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qdeclarativepositionsource_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioningquick/qdeclarativepositionsource_p.h" diff --git a/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qpositioningquickglobal_p.h b/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qpositioningquickglobal_p.h new file mode 100644 index 0000000..532a737 --- /dev/null +++ b/include/QtPositioningQuick/5.11.3/QtPositioningQuick/private/qpositioningquickglobal_p.h @@ -0,0 +1 @@ +#include "../../../../../src/positioningquick/qpositioningquickglobal_p.h" diff --git a/include/QtPositioningQuick/QtPositioningQuick b/include/QtPositioningQuick/QtPositioningQuick new file mode 100644 index 0000000..6b1a2f8 --- /dev/null +++ b/include/QtPositioningQuick/QtPositioningQuick @@ -0,0 +1,6 @@ +#ifndef QT_QTPOSITIONINGQUICK_MODULE_H +#define QT_QTPOSITIONINGQUICK_MODULE_H +#include +#include "qpositioningquickglobal.h" +#include "qtpositioningquickversion.h" +#endif diff --git a/include/QtPositioningQuick/QtPositioningQuickVersion b/include/QtPositioningQuick/QtPositioningQuickVersion new file mode 100644 index 0000000..2c697a7 --- /dev/null +++ b/include/QtPositioningQuick/QtPositioningQuickVersion @@ -0,0 +1 @@ +#include "qtpositioningquickversion.h" diff --git a/include/QtPositioningQuick/headers.pri b/include/QtPositioningQuick/headers.pri new file mode 100644 index 0000000..2e22860 --- /dev/null +++ b/include/QtPositioningQuick/headers.pri @@ -0,0 +1,6 @@ +SYNCQT.HEADER_FILES = qpositioningquickglobal.h +SYNCQT.GENERATED_HEADER_FILES = qtpositioningquickversion.h QtPositioningQuickVersion QtPositioningQuick +SYNCQT.PRIVATE_HEADER_FILES = qdeclarativeposition_p.h qdeclarativepositionsource_p.h qpositioningquickglobal_p.h +SYNCQT.QPA_HEADER_FILES = +SYNCQT.CLEAN_HEADER_FILES = qpositioningquickglobal.h +SYNCQT.INJECTIONS = diff --git a/include/QtPositioningQuick/qpositioningquickglobal.h b/include/QtPositioningQuick/qpositioningquickglobal.h new file mode 100644 index 0000000..4c34141 --- /dev/null +++ b/include/QtPositioningQuick/qpositioningquickglobal.h @@ -0,0 +1 @@ +#include "../../src/positioningquick/qpositioningquickglobal.h" diff --git a/include/QtPositioningQuick/qtpositioningquickversion.h b/include/QtPositioningQuick/qtpositioningquickversion.h new file mode 100644 index 0000000..57b7053 --- /dev/null +++ b/include/QtPositioningQuick/qtpositioningquickversion.h @@ -0,0 +1,9 @@ +/* This file was generated by syncqt. */ +#ifndef QT_QTPOSITIONINGQUICK_VERSION_H +#define QT_QTPOSITIONINGQUICK_VERSION_H + +#define QTPOSITIONINGQUICK_VERSION_STR "5.11.3" + +#define QTPOSITIONINGQUICK_VERSION 0x050B03 + +#endif // QT_QTPOSITIONINGQUICK_VERSION_H diff --git a/qtlocation.pro b/qtlocation.pro new file mode 100644 index 0000000..3d942a6 --- /dev/null +++ b/qtlocation.pro @@ -0,0 +1,7 @@ +load(configure) +qtCompileTest(gypsy) +qtCompileTest(winrt) + +load(qt_parts) + +DISTFILES += sync.profile configure.json diff --git a/src/3rdparty/clip2tri/LICENSE b/src/3rdparty/clip2tri/LICENSE new file mode 100644 index 0000000..9d99b88 --- /dev/null +++ b/src/3rdparty/clip2tri/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Bitfighter developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/3rdparty/clip2tri/clip2tri.cpp b/src/3rdparty/clip2tri/clip2tri.cpp new file mode 100644 index 0000000..db4911c --- /dev/null +++ b/src/3rdparty/clip2tri/clip2tri.cpp @@ -0,0 +1,406 @@ +/* + * Authors: kaen, raptor, sam686, watusimoto + * + * Originally from the bitfighter source code + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Bitfighter developers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "clip2tri.h" +#include + +#include + +static const double clipperScaleFactor = 1073741822.0; +static const double clipperScaleFactorInv = 1.0 / 1073741822.0; + +using namespace p2t; + +namespace c2t +{ + + +static const F32 CLIPPER_SCALE_FACT = 1000.0f; +static const F32 CLIPPER_SCALE_FACT_INVERSE = 0.001f; + +///////////////////////////////// + +Point::Point() +{ + x = 0; + y = 0; +} + +Point::Point(const Point& pt) +{ + x = pt.x; + y = pt.y; +} + + +///////////////////////////////// + +clip2tri::clip2tri() : openSubject(false) +{ + // Do nothing! +} + +clip2tri::~clip2tri() +{ + // Do nothing! +} + + +void clip2tri::triangulate(const vector > &inputPolygons, vector &outputTriangles, + const vector &boundingPolygon) +{ + // Use clipper to clean. This upscales the floating point input + PolyTree solution; + mergePolysToPolyTree(inputPolygons, solution); + + Path bounds = upscaleClipperPoints(boundingPolygon); + + // This will downscale the Clipper output and use poly2tri to triangulate + triangulateComplex(outputTriangles, bounds, solution); +} + +void clip2tri::addClipPolygon(const Path &path) +{ + try // prevent any exception to spill into Qt + { + clipper.AddPath(path, ptClip, true); + } + catch(QtClipperLib::clipperException &e) + { + printf("addClipPolygon: %s\n", e.what()); + } +} + +void clip2tri::addSubjectPath(const Path &path, bool closed) +{ + try // prevent any exception to spill into Qt + { + clipper.AddPath(path, ptSubject, closed); + } + catch(QtClipperLib::clipperException &e) + { + printf("addSubjectPath: %s\n", e.what()); + return; + } + if (!closed) + openSubject = true; +} + +void clip2tri::clearClipper() +{ + // clear doesn't throw + clipper.Clear(); + openSubject = false; +} + +static QtClipperLib::ClipType operation(const clip2tri::Operation &op) +{ + switch (op) { + case clip2tri::Intersection: + return QtClipperLib::ctIntersection; + case clip2tri::Union: + return QtClipperLib::ctUnion; + case clip2tri::Difference: + return QtClipperLib::ctDifference; + case clip2tri::Xor: + return QtClipperLib::ctXor; + } + return ctIntersection; +} + +static std::string operationName(const clip2tri::Operation &op) +{ + switch (op) { + case clip2tri::Intersection: + return std::string("Intersection"); + case clip2tri::Union: + return std::string("Union"); + case clip2tri::Difference: + return std::string("Difference"); + case clip2tri::Xor: + return std::string("Xor"); + } + return std::string("Intersection"); +} + +Paths clip2tri::execute(const clip2tri::Operation op, const PolyFillType subjFillType, const PolyFillType clipFillType) +{ + Paths solution; + try // prevent any exception from spilling into Qt + { + if (!openSubject) { + clipper.Execute(operation(op), solution, subjFillType, clipFillType); + } else { + PolyTree res; + clipper.Execute(operation(op), res, subjFillType, clipFillType); + PolyNode *n = res.GetFirst(); + if (n) { + solution.push_back(n->Contour); + while ((n = n->GetNext())) + solution.push_back(n->Contour); + } + } + } + catch(QtClipperLib::clipperException &e) + { + printf("executing %s: %s\n", operationName(op).c_str(), e.what()); + } + return solution; +} + +int clip2tri::pointInPolygon(const IntPoint &pt, const Path &path) +{ + return PointInPolygon(pt, path); +} + +Path clip2tri::upscaleClipperPoints(const vector &inputPolygon) +{ + Path outputPolygon; + outputPolygon.resize(inputPolygon.size()); + + for(S32 i = 0; i < inputPolygon.size(); i++) + outputPolygon[i] = IntPoint(S64(inputPolygon[i].x * CLIPPER_SCALE_FACT), S64(inputPolygon[i].y * CLIPPER_SCALE_FACT)); + + return outputPolygon; +} + + +Paths clip2tri::upscaleClipperPoints(const vector > &inputPolygons) +{ + Paths outputPolygons; + + outputPolygons.resize(inputPolygons.size()); + + for(S32 i = 0; i < inputPolygons.size(); i++) + { + outputPolygons[i].resize(inputPolygons[i].size()); + + for(S32 j = 0; j < inputPolygons[i].size(); j++) + outputPolygons[i][j] = IntPoint(S64(inputPolygons[i][j].x * CLIPPER_SCALE_FACT), S64(inputPolygons[i][j].y * CLIPPER_SCALE_FACT)); + } + + return outputPolygons; +} + + +vector > clip2tri::downscaleClipperPoints(const Paths &inputPolygons) +{ + vector > outputPolygons; + + outputPolygons.resize(inputPolygons.size()); + + for(U32 i = 0; i < inputPolygons.size(); i++) + { + outputPolygons[i].resize(inputPolygons[i].size()); + + for(U32 j = 0; j < inputPolygons[i].size(); j++) + outputPolygons[i][j] = Point(F32(inputPolygons[i][j].X) * CLIPPER_SCALE_FACT_INVERSE, F32(inputPolygons[i][j].Y) * CLIPPER_SCALE_FACT_INVERSE); + } + + return outputPolygons; +} + + +// Use Clipper to merge inputPolygons, placing the result in a Polytree +// NOTE: this does NOT downscale the Clipper points. You must do this afterwards +// +// Here you add all your non-navigatable objects (e.g. walls, barriers, etc.) +bool clip2tri::mergePolysToPolyTree(const vector > &inputPolygons, PolyTree &solution) +{ + Paths input = upscaleClipperPoints(inputPolygons); + + // Fire up clipper and union! + Clipper clipper; + clipper.StrictlySimple(true); + + try // there is a "throw" in AddPolygon + { + clipper.AddPaths(input, ptSubject, true); + } + catch(QtClipperLib::clipperException &e) + { + printf("mergePolysToPolyTree: %s\n", e.what()); + } + + return clipper.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} + + +// Delete all poly2tri points from a vector and clear the vector +static void deleteAndClear(vector &vec) +{ + for(U32 i = 0; i < vec.size(); i++) + delete vec[i]; + + vec.clear(); +} + + +// Shrink large polygons by reducing each coordinate by 1 in the +// general direction of the last point as we wind around +// +// This normally wouldn't work in every case, but our upscaled-by-1000 polygons +// have little chance to create new duplicate points with this method. +// +// For information on why this was needed, see: +// +// https://code.google.com/p/poly2tri/issues/detail?id=90 +// +static void edgeShrink(Path &path) +{ + U32 prev = path.size() - 1; + for(U32 i = 0; i < path.size(); i++) + { + // Adjust coordinate by 1 depending on the direction + path[i].X - path[prev].X > 0 ? path[i].X-- : path[i].X++; + path[i].Y - path[prev].Y > 0 ? path[i].Y-- : path[i].Y++; + + prev = i; + } +} + + +// This uses poly2tri to triangulate. poly2tri isn't very robust so clipper needs to do +// the cleaning of points before getting here. +// +// A tree structure of polygons is required for doing complex polygons-within-polygons. +// For reference discussion on how this started to be developed, see here: +// +// https://code.google.com/p/poly2tri/issues/detail?id=74 +// +// For assistance with a special case crash, see this utility: +// http://javascript.poly2tri.googlecode.com/hg/index.html +// +// FIXME: what is ignoreFills and ignoreHoles for? kaen? +bool clip2tri::triangulateComplex(vector &outputTriangles, const Path &outline, + const PolyTree &polyTree, bool ignoreFills, bool ignoreHoles) +{ + // Keep track of memory for all the poly2tri objects we create + vector cdtRegistry; + vector > holesRegistry; + vector > polylinesRegistry; + + + // Let's be tricky and add our outline to the root node (it should have none), it'll be + // our first Clipper hole + PolyNode *rootNode = NULL; + + PolyNode tempNode; + if(polyTree.Total() == 0) // Polytree is empty with no root node, e.g. on an empty level + rootNode = &tempNode; + else + rootNode = polyTree.GetFirst()->Parent; + + rootNode->Contour = outline; + + // Now traverse our polyline nodes and triangulate them with only their children holes + PolyNode *currentNode = rootNode; + while(currentNode != NULL) + { + // A Clipper hole is actually what we want to build zones for; they become our bounding + // polylines. poly2tri holes are therefore the inverse + if((!ignoreHoles && currentNode->IsHole()) || + (!ignoreFills && !currentNode->IsHole())) + { + // Build up this polyline in poly2tri's format (downscale Clipper points) + vector polyline; + for(U32 j = 0; j < currentNode->Contour.size(); j++) + polyline.push_back(new p2t::Point(F64(currentNode->Contour[j].X), F64(currentNode->Contour[j].Y))); + + polylinesRegistry.push_back(polyline); // Memory + + // Set our polyline in poly2tri + p2t::CDT* cdt = new p2t::CDT(polyline); + cdtRegistry.push_back(cdt); + + for(U32 j = 0; j < currentNode->Childs.size(); j++) + { + PolyNode *childNode = currentNode->Childs[j]; + + // Slightly modify the polygon to guarantee no duplicate points + edgeShrink(childNode->Contour); + + vector hole; + for(U32 k = 0; k < childNode->Contour.size(); k++) + hole.push_back(new p2t::Point(F64(childNode->Contour[k].X), F64(childNode->Contour[k].Y))); + + holesRegistry.push_back(hole); // Memory + + // Add the holes for this polyline + cdt->AddHole(hole); + } + + cdt->Triangulate(); + + // Add current output triangles to our total + vector currentOutput = cdt->GetTriangles(); + + // Copy our data to TNL::Point and to our output Vector + p2t::Triangle *currentTriangle; + for(U32 j = 0; j < currentOutput.size(); j++) + { + currentTriangle = currentOutput[j]; + outputTriangles.push_back(Point(currentTriangle->GetPoint(0)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(0)->y * CLIPPER_SCALE_FACT_INVERSE)); + outputTriangles.push_back(Point(currentTriangle->GetPoint(1)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(1)->y * CLIPPER_SCALE_FACT_INVERSE)); + outputTriangles.push_back(Point(currentTriangle->GetPoint(2)->x * CLIPPER_SCALE_FACT_INVERSE, currentTriangle->GetPoint(2)->y * CLIPPER_SCALE_FACT_INVERSE)); + } + } + + currentNode = currentNode->GetNext(); + } + + + // Clean up memory used with poly2tri + // + // Clean-up workers + for(S32 i = 0; i < cdtRegistry.size(); i++) + delete cdtRegistry[i]; + + // Free the polylines + for(S32 i = 0; i < polylinesRegistry.size(); i++) + { + vector polyline = polylinesRegistry[i]; + deleteAndClear(polyline); + } + + // Free the holes + for(S32 i = 0; i < holesRegistry.size(); i++) + { + vector hole = holesRegistry[i]; + deleteAndClear(hole); + } + + // Make sure we have output data + if(outputTriangles.size() == 0) + return false; + + return true; +} + + +} /* namespace c2t */ diff --git a/src/3rdparty/clip2tri/clip2tri.h b/src/3rdparty/clip2tri/clip2tri.h new file mode 100644 index 0000000..3848d00 --- /dev/null +++ b/src/3rdparty/clip2tri/clip2tri.h @@ -0,0 +1,102 @@ +/* + * Authors: kaen, raptor, sam686, watusimoto + * + * Originally from the bitfighter source code + * + * The MIT License (MIT) + * + * Copyright (c) 2014 Bitfighter developers + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef CLIP2TRI_H_ +#define CLIP2TRI_H_ + +#include +#include + +using namespace std; +using namespace QtClipperLib; + +namespace c2t +{ + +typedef signed int S32; +typedef signed long long S64; +typedef unsigned int U32; +typedef float F32; +typedef double F64; + + +struct Point +{ + F32 x; + F32 y; + + Point(); + Point(const Point &pt); + + template + Point(T in_x, U in_y) { x = static_cast(in_x); y = static_cast(in_y); } +}; + +class clip2tri +{ +private: + // + Path upscaleClipperPoints(const vector &inputPolygon); + + // These operate on a vector of polygons + Paths upscaleClipperPoints(const vector > &inputPolygons); + vector > downscaleClipperPoints(const Paths &inputPolygons); + + bool mergePolysToPolyTree(const vector > &inputPolygons, PolyTree &solution); + + bool triangulateComplex(vector &outputTriangles, const Path &outline, + const PolyTree &polyTree, bool ignoreFills = true, bool ignoreHoles = false); + +public: + enum Operation { Union, Intersection, Difference, Xor }; + clip2tri(); + virtual ~clip2tri(); + + void triangulate(const vector > &inputPolygons, vector &outputTriangles, + const vector &boundingPolygon); + + // Clip polygons are intended as closed, even if the first and last vertex aren't the same. + void addClipPolygon(const Path &path); + // Closed means the path has to be effectively closed. Meaning path[0] == path[path.size()-1] + void addSubjectPath(const Path &path, bool closed); + + void clearClipper(); + + Paths execute(const Operation op, + const PolyFillType subjFillType = pftNonZero, + const PolyFillType clipFillType = pftNonZero); + + static int pointInPolygon(const IntPoint &pt, const Path &path); + + Clipper clipper; + bool openSubject; +}; + +} /* namespace c2t */ + +#endif /* CLIP2TRI_H_ */ diff --git a/src/3rdparty/clip2tri/clip2tri.pro b/src/3rdparty/clip2tri/clip2tri.pro new file mode 100644 index 0000000..802c040 --- /dev/null +++ b/src/3rdparty/clip2tri/clip2tri.pro @@ -0,0 +1,22 @@ +TARGET = clip2tri + +CONFIG += staticlib exceptions warn_off optimize_full + +INCLUDEPATH += ../poly2tri +INCLUDEPATH += ../clipper + +load(qt_helper_lib) + +# workaround for QTBUG-31586 +contains(QT_CONFIG, c++11): CONFIG += c++11 + +gcc { + QMAKE_CFLAGS_OPTIMIZE_FULL += -ffast-math + !clang:!intel_icc:!rim_qcc: QMAKE_CXXFLAGS_WARN_ON += -Wno-error=return-type +} + +HEADERS += clip2tri.h +SOURCES += clip2tri.cpp + +LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lpoly2tri$$qtPlatformTargetSuffix() -lclipper$$qtPlatformTargetSuffix() + diff --git a/src/3rdparty/clip2tri/qt_attribution.json b/src/3rdparty/clip2tri/qt_attribution.json new file mode 100644 index 0000000..1316f63 --- /dev/null +++ b/src/3rdparty/clip2tri/qt_attribution.json @@ -0,0 +1,13 @@ +{ + "Id": "clip2tri", + "Name": "Clip2Tri Polygon Triangulation Library", + "QDocModule": "qtlocation", + "QtUsage": "Used in the QML plugin of Qt Location.", + + "Description": "Clip2Tri can be used together with Clipper for robust triangulation.", + "Homepage": "https://github.com/raptor/clip2tri", + "LicenseId": "MIT", + "License": "MIT License", + "LicenseFile": "LICENSE", + "Copyright": "Copyright (c) 2014 Bitfighter developers" +} diff --git a/src/3rdparty/clipper/LICENSE b/src/3rdparty/clipper/LICENSE new file mode 100644 index 0000000..bc0d6b0 --- /dev/null +++ b/src/3rdparty/clipper/LICENSE @@ -0,0 +1,21 @@ +Use, modification & distribution is subject to Boost Software License Ver 1. +http://www.boost.org/LICENSE_1_0.txt + +Attributions: +The code in this library is an extension of Bala Vatti's clipping algorithm: +"A generic solution to polygon clipping" +Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. +http://portal.acm.org/citation.cfm?id=129906 + +Computer graphics and geometric modeling: implementation and algorithms +By Max K. Agoston +Springer; 1 edition (January 4, 2005) +http://books.google.com/books?q=vatti+clipping+agoston + +See also: +"Polygon Offsetting by Computing Winding Numbers" +Paper no. DETC2005-85513 pp. 565-575 +ASME 2005 International Design Engineering Technical Conferences +and Computers and Information in Engineering Conference (IDETC/CIE2005) +September 24-28, 2005 , Long Beach, California, USA +http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf diff --git a/src/3rdparty/clipper/clipper.cpp b/src/3rdparty/clipper/clipper.cpp new file mode 100644 index 0000000..53ac4d8 --- /dev/null +++ b/src/3rdparty/clipper/clipper.cpp @@ -0,0 +1,4622 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +/******************************************************************************* +* * +* This is a translation of the Delphi Clipper library and the naming style * +* used has retained a Delphi flavour. * +* * +*******************************************************************************/ + +#include "clipper.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QtClipperLib { + +static double const pi = 3.141592653589793238; +static double const two_pi = pi *2; +static double const def_arc_tolerance = 0.25; + +enum Direction { dRightToLeft, dLeftToRight }; + +static int const Unassigned = -1; //edge not currently 'owning' a solution +static int const Skip = -2; //edge that would otherwise close a path + +#define HORIZONTAL (-1.0E+40) +#define TOLERANCE (1.0e-20) +#define NEAR_ZERO(val) (((val) > -TOLERANCE) && ((val) < TOLERANCE)) + +struct TEdge { + IntPoint Bot; + IntPoint Curr; //current (updated for every new scanbeam) + IntPoint Top; + double Dx; + PolyType PolyTyp; + EdgeSide Side; //side only refers to current side of solution poly + int WindDelta; //1 or -1 depending on winding direction + int WindCnt; + int WindCnt2; //winding count of the opposite polytype + int OutIdx; + TEdge *Next; + TEdge *Prev; + TEdge *NextInLML; + TEdge *NextInAEL; + TEdge *PrevInAEL; + TEdge *NextInSEL; + TEdge *PrevInSEL; +}; + +struct IntersectNode { + TEdge *Edge1; + TEdge *Edge2; + IntPoint Pt; +}; + +struct LocalMinimum { + cInt Y; + TEdge *LeftBound; + TEdge *RightBound; +}; + +struct OutPt; + +//OutRec: contains a path in the clipping solution. Edges in the AEL will +//carry a pointer to an OutRec when they are part of the clipping solution. +struct OutRec { + int Idx; + bool IsHole; + bool IsOpen; + OutRec *FirstLeft; //see comments in clipper.pas + PolyNode *PolyNd; + OutPt *Pts; + OutPt *BottomPt; +}; + +struct OutPt { + int Idx; + IntPoint Pt; + OutPt *Next; + OutPt *Prev; +}; + +struct Join { + OutPt *OutPt1; + OutPt *OutPt2; + IntPoint OffPt; +}; + +struct LocMinSorter +{ + inline bool operator()(const LocalMinimum& locMin1, const LocalMinimum& locMin2) + { + return locMin2.Y < locMin1.Y; + } +}; + +//------------------------------------------------------------------------------ +//------------------------------------------------------------------------------ + +inline cInt Round(double val) +{ + if ((val < 0)) return static_cast(val - 0.5); + else return static_cast(val + 0.5); +} +//------------------------------------------------------------------------------ + +inline cInt Abs(cInt val) +{ + return val < 0 ? -val : val; +} + +//------------------------------------------------------------------------------ +// PolyTree methods ... +//------------------------------------------------------------------------------ + +void PolyTree::Clear() +{ + for (PolyNodes::size_type i = 0; i < AllNodes.size(); ++i) + delete AllNodes[i]; + AllNodes.resize(0); + Childs.resize(0); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyTree::GetFirst() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return 0; +} +//------------------------------------------------------------------------------ + +int PolyTree::Total() const +{ + int result = (int)AllNodes.size(); + //with negative offsets, ignore the hidden outer polygon ... + if (result > 0 && Childs[0] != AllNodes[0]) result--; + return result; +} + +//------------------------------------------------------------------------------ +// PolyNode methods ... +//------------------------------------------------------------------------------ + +PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) +{ +} +//------------------------------------------------------------------------------ + +int PolyNode::ChildCount() const +{ + return (int)Childs.size(); +} +//------------------------------------------------------------------------------ + +void PolyNode::AddChild(PolyNode& child) +{ + unsigned cnt = (unsigned)Childs.size(); + Childs.push_back(&child); + child.Parent = this; + child.Index = cnt; +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNext() const +{ + if (!Childs.empty()) + return Childs[0]; + else + return GetNextSiblingUp(); +} +//------------------------------------------------------------------------------ + +PolyNode* PolyNode::GetNextSiblingUp() const +{ + if (!Parent) //protects against PolyTree.GetNextSiblingUp() + return 0; + else if (Index == Parent->Childs.size() - 1) + return Parent->GetNextSiblingUp(); + else + return Parent->Childs[Index + 1]; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsHole() const +{ + bool result = true; + PolyNode* node = Parent; + while (node) + { + result = !result; + node = node->Parent; + } + return result; +} +//------------------------------------------------------------------------------ + +bool PolyNode::IsOpen() const +{ + return m_IsOpen; +} +//------------------------------------------------------------------------------ + +#ifndef use_int32 + +//------------------------------------------------------------------------------ +// Int128 class (enables safe math on signed 64bit integers) +// eg Int128 val1((long64)9223372036854775807); //ie 2^63 -1 +// Int128 val2((long64)9223372036854775807); +// Int128 val3 = val1 * val2; +// val3.AsString => "85070591730234615847396907784232501249" (8.5e+37) +//------------------------------------------------------------------------------ + +class Int128 +{ + public: + ulong64 lo; + long64 hi; + + Int128(long64 _lo = 0) + { + lo = (ulong64)_lo; + if (_lo < 0) hi = -1; else hi = 0; + } + + + Int128(const Int128 &val): lo(val.lo), hi(val.hi){} + + Int128(const long64& _hi, const ulong64& _lo): lo(_lo), hi(_hi){} + + Int128& operator = (const long64 &val) + { + lo = (ulong64)val; + if (val < 0) hi = -1; else hi = 0; + return *this; + } + + bool operator == (const Int128 &val) const + {return (hi == val.hi && lo == val.lo);} + + bool operator != (const Int128 &val) const + { return !(*this == val);} + + bool operator > (const Int128 &val) const + { + if (hi != val.hi) + return hi > val.hi; + else + return lo > val.lo; + } + + bool operator < (const Int128 &val) const + { + if (hi != val.hi) + return hi < val.hi; + else + return lo < val.lo; + } + + bool operator >= (const Int128 &val) const + { return !(*this < val);} + + bool operator <= (const Int128 &val) const + { return !(*this > val);} + + Int128& operator += (const Int128 &rhs) + { + hi += rhs.hi; + lo += rhs.lo; + if (lo < rhs.lo) hi++; + return *this; + } + + Int128 operator + (const Int128 &rhs) const + { + Int128 result(*this); + result+= rhs; + return result; + } + + Int128& operator -= (const Int128 &rhs) + { + *this += -rhs; + return *this; + } + + Int128 operator - (const Int128 &rhs) const + { + Int128 result(*this); + result -= rhs; + return result; + } + + Int128 operator-() const //unary negation + { + if (lo == 0) + return Int128(-hi, 0); + else + return Int128(~hi, ~lo + 1); + } + + operator double() const + { + const double shift64 = 18446744073709551616.0; //2^64 + if (hi < 0) + { + if (lo == 0) return (double)hi * shift64; + else return -(double)(~lo + ~hi * shift64); + } + else + return (double)(lo + hi * shift64); + } + +}; +//------------------------------------------------------------------------------ + +Int128 Int128Mul (long64 lhs, long64 rhs) +{ + bool negate = (lhs < 0) != (rhs < 0); + + if (lhs < 0) lhs = -lhs; + ulong64 int1Hi = ulong64(lhs) >> 32; + ulong64 int1Lo = ulong64(lhs & 0xFFFFFFFF); + + if (rhs < 0) rhs = -rhs; + ulong64 int2Hi = ulong64(rhs) >> 32; + ulong64 int2Lo = ulong64(rhs & 0xFFFFFFFF); + + //nb: see comments in clipper.pas + ulong64 a = int1Hi * int2Hi; + ulong64 b = int1Lo * int2Lo; + ulong64 c = int1Hi * int2Lo + int1Lo * int2Hi; + + Int128 tmp; + tmp.hi = long64(a + (c >> 32)); + tmp.lo = long64(c << 32); + tmp.lo += long64(b); + if (tmp.lo < b) tmp.hi++; + if (negate) tmp = -tmp; + return tmp; +}; +#endif + +//------------------------------------------------------------------------------ +// Miscellaneous global functions +//------------------------------------------------------------------------------ + +bool Orientation(const Path &poly) +{ + return Area(poly) >= 0; +} +//------------------------------------------------------------------------------ + +double Area(const Path &poly) +{ + int size = (int)poly.size(); + if (size < 3) return 0; + + double a = 0; + for (int i = 0, j = size -1; i < size; ++i) + { + a += ((double)poly[j].X + poly[i].X) * ((double)poly[j].Y - poly[i].Y); + j = i; + } + return -a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutPt *op) +{ + const OutPt *startOp = op; + if (!op) return 0; + double a = 0; + do { + a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); + op = op->Next; + } while (op != startOp); + return a * 0.5; +} +//------------------------------------------------------------------------------ + +double Area(const OutRec &outRec) +{ + return Area(outRec.Pts); +} +//------------------------------------------------------------------------------ + +bool PointIsVertex(const IntPoint &Pt, OutPt *pp) +{ + OutPt *pp2 = pp; + do + { + if (pp2->Pt == Pt) return true; + pp2 = pp2->Next; + } + while (pp2 != pp); + return false; +} +//------------------------------------------------------------------------------ + +//See "The Point in Polygon Problem for Arbitrary Polygons" by Hormann & Agathos +//http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.88.5498&rep=rep1&type=pdf +int PointInPolygon(const IntPoint &pt, const Path &path) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + size_t cnt = path.size(); + if (cnt < 3) return 0; + IntPoint ip = path[0]; + for(size_t i = 1; i <= cnt; ++i) + { + IntPoint ipNext = (i == cnt ? path[0] : path[i]); + if (ipNext.Y == pt.Y) + { + if ((ipNext.X == pt.X) || (ip.Y == pt.Y && + ((ipNext.X > pt.X) == (ip.X < pt.X)))) return -1; + } + if ((ip.Y < pt.Y) != (ipNext.Y < pt.Y)) + { + if (ip.X >= pt.X) + { + if (ipNext.X > pt.X) result = 1 - result; + else + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } else + { + if (ipNext.X > pt.X) + { + double d = (double)(ip.X - pt.X) * (ipNext.Y - pt.Y) - + (double)(ipNext.X - pt.X) * (ip.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (ipNext.Y > ip.Y)) result = 1 - result; + } + } + } + ip = ipNext; + } + return result; +} +//------------------------------------------------------------------------------ + +int PointInPolygon (const IntPoint &pt, OutPt *op) +{ + //returns 0 if false, +1 if true, -1 if pt ON polygon boundary + int result = 0; + OutPt* startOp = op; + for(;;) + { + if (op->Next->Pt.Y == pt.Y) + { + if ((op->Next->Pt.X == pt.X) || (op->Pt.Y == pt.Y && + ((op->Next->Pt.X > pt.X) == (op->Pt.X < pt.X)))) return -1; + } + if ((op->Pt.Y < pt.Y) != (op->Next->Pt.Y < pt.Y)) + { + if (op->Pt.X >= pt.X) + { + if (op->Next->Pt.X > pt.X) result = 1 - result; + else + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } else + { + if (op->Next->Pt.X > pt.X) + { + double d = (double)(op->Pt.X - pt.X) * (op->Next->Pt.Y - pt.Y) - + (double)(op->Next->Pt.X - pt.X) * (op->Pt.Y - pt.Y); + if (!d) return -1; + if ((d > 0) == (op->Next->Pt.Y > op->Pt.Y)) result = 1 - result; + } + } + } + op = op->Next; + if (startOp == op) break; + } + return result; +} +//------------------------------------------------------------------------------ + +bool Poly2ContainsPoly1(OutPt *OutPt1, OutPt *OutPt2) +{ + OutPt* op = OutPt1; + do + { + //nb: PointInPolygon returns 0 if false, +1 if true, -1 if pt on polygon + int res = PointInPolygon(op->Pt, OutPt2); + if (res >= 0) return res > 0; + op = op->Next; + } + while (op != OutPt1); + return true; +} +//---------------------------------------------------------------------- + +bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == + Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); + else +#endif + return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == + (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt2.X-pt3.X) == Int128Mul(pt1.X-pt2.X, pt2.Y-pt3.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt2.X-pt3.X) == (pt1.X-pt2.X)*(pt2.Y-pt3.Y); +} +//------------------------------------------------------------------------------ + +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) +{ +#ifndef use_int32 + if (UseFullInt64Range) + return Int128Mul(pt1.Y-pt2.Y, pt3.X-pt4.X) == Int128Mul(pt1.X-pt2.X, pt3.Y-pt4.Y); + else +#endif + return (pt1.Y-pt2.Y)*(pt3.X-pt4.X) == (pt1.X-pt2.X)*(pt3.Y-pt4.Y); +} +//------------------------------------------------------------------------------ + +inline bool IsHorizontal(TEdge &e) +{ + return e.Dx == HORIZONTAL; +} +//------------------------------------------------------------------------------ + +inline double GetDx(const IntPoint pt1, const IntPoint pt2) +{ + return (pt1.Y == pt2.Y) ? + HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); +} +//--------------------------------------------------------------------------- + +inline void SetDx(TEdge &e) +{ + cInt dy = (e.Top.Y - e.Bot.Y); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; +} +//--------------------------------------------------------------------------- + +inline void SwapSides(TEdge &Edge1, TEdge &Edge2) +{ + EdgeSide Side = Edge1.Side; + Edge1.Side = Edge2.Side; + Edge2.Side = Side; +} +//------------------------------------------------------------------------------ + +inline void SwapPolyIndexes(TEdge &Edge1, TEdge &Edge2) +{ + int OutIdx = Edge1.OutIdx; + Edge1.OutIdx = Edge2.OutIdx; + Edge2.OutIdx = OutIdx; +} +//------------------------------------------------------------------------------ + +inline cInt TopX(TEdge &edge, const cInt currentY) +{ + return ( currentY == edge.Top.Y ) ? + edge.Top.X : edge.Bot.X + Round(edge.Dx *(currentY - edge.Bot.Y)); +} +//------------------------------------------------------------------------------ + +void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) +{ +#ifdef use_xyz + ip.Z = 0; +#endif + + double b1, b2; + if (Edge1.Dx == Edge2.Dx) + { + ip.Y = Edge1.Curr.Y; + ip.X = TopX(Edge1, ip.Y); + return; + } + else if (Edge1.Dx == 0) + { + ip.X = Edge1.Bot.X; + if (IsHorizontal(Edge2)) + ip.Y = Edge2.Bot.Y; + else + { + b2 = Edge2.Bot.Y - (Edge2.Bot.X / Edge2.Dx); + ip.Y = Round(ip.X / Edge2.Dx + b2); + } + } + else if (Edge2.Dx == 0) + { + ip.X = Edge2.Bot.X; + if (IsHorizontal(Edge1)) + ip.Y = Edge1.Bot.Y; + else + { + b1 = Edge1.Bot.Y - (Edge1.Bot.X / Edge1.Dx); + ip.Y = Round(ip.X / Edge1.Dx + b1); + } + } + else + { + b1 = Edge1.Bot.X - Edge1.Bot.Y * Edge1.Dx; + b2 = Edge2.Bot.X - Edge2.Bot.Y * Edge2.Dx; + double q = (b2-b1) / (Edge1.Dx - Edge2.Dx); + ip.Y = Round(q); + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = Round(Edge1.Dx * q + b1); + else + ip.X = Round(Edge2.Dx * q + b2); + } + + if (ip.Y < Edge1.Top.Y || ip.Y < Edge2.Top.Y) + { + if (Edge1.Top.Y > Edge2.Top.Y) + ip.Y = Edge1.Top.Y; + else + ip.Y = Edge2.Top.Y; + if (std::fabs(Edge1.Dx) < std::fabs(Edge2.Dx)) + ip.X = TopX(Edge1, ip.Y); + else + ip.X = TopX(Edge2, ip.Y); + } + //finally, don't allow 'ip' to be BELOW curr.Y (ie bottom of scanbeam) ... + if (ip.Y > Edge1.Curr.Y) + { + ip.Y = Edge1.Curr.Y; + //use the more vertical edge to derive X ... + if (std::fabs(Edge1.Dx) > std::fabs(Edge2.Dx)) + ip.X = TopX(Edge2, ip.Y); else + ip.X = TopX(Edge1, ip.Y); + } +} +//------------------------------------------------------------------------------ + +void ReversePolyPtLinks(OutPt *pp) +{ + if (!pp) return; + OutPt *pp1, *pp2; + pp1 = pp; + do { + pp2 = pp1->Next; + pp1->Next = pp1->Prev; + pp1->Prev = pp2; + pp1 = pp2; + } while( pp1 != pp ); +} +//------------------------------------------------------------------------------ + +void DisposeOutPts(OutPt*& pp) +{ + if (pp == 0) return; + pp->Prev->Next = 0; + while( pp ) + { + OutPt *tmpPp = pp; + pp = pp->Next; + delete tmpPp; + } +} +//------------------------------------------------------------------------------ + +inline void InitEdge(TEdge* e, TEdge* eNext, TEdge* ePrev, const IntPoint& Pt) +{ + std::memset(e, 0, sizeof(TEdge)); + e->Next = eNext; + e->Prev = ePrev; + e->Curr = Pt; + e->OutIdx = Unassigned; +} +//------------------------------------------------------------------------------ + +void InitEdge2(TEdge& e, PolyType Pt) +{ + if (e.Curr.Y >= e.Next->Curr.Y) + { + e.Bot = e.Curr; + e.Top = e.Next->Curr; + } else + { + e.Top = e.Curr; + e.Bot = e.Next->Curr; + } + SetDx(e); + e.PolyTyp = Pt; +} +//------------------------------------------------------------------------------ + +TEdge* RemoveEdge(TEdge* e) +{ + //removes e from double_linked_list (but without removing from memory) + e->Prev->Next = e->Next; + e->Next->Prev = e->Prev; + TEdge* result = e->Next; + e->Prev = 0; //flag as removed (see ClipperBase.Clear) + return result; +} +//------------------------------------------------------------------------------ + +inline void ReverseHorizontal(TEdge &e) +{ + //swap horizontal edges' Top and Bottom x's so they follow the natural + //progression of the bounds - ie so their xbots will align with the + //adjoining lower edge. [Helpful in the ProcessHorizontal() method.] + std::swap(e.Top.X, e.Bot.X); +#ifdef use_xyz + std::swap(e.Top.Z, e.Bot.Z); +#endif +} +//------------------------------------------------------------------------------ + +void SwapPoints(IntPoint &pt1, IntPoint &pt2) +{ + IntPoint tmp = pt1; + pt1 = pt2; + pt2 = tmp; +} +//------------------------------------------------------------------------------ + +bool GetOverlapSegment(IntPoint pt1a, IntPoint pt1b, IntPoint pt2a, + IntPoint pt2b, IntPoint &pt1, IntPoint &pt2) +{ + //precondition: segments are Collinear. + if (Abs(pt1a.X - pt1b.X) > Abs(pt1a.Y - pt1b.Y)) + { + if (pt1a.X > pt1b.X) SwapPoints(pt1a, pt1b); + if (pt2a.X > pt2b.X) SwapPoints(pt2a, pt2b); + if (pt1a.X > pt2a.X) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.X < pt2b.X) pt2 = pt1b; else pt2 = pt2b; + return pt1.X < pt2.X; + } else + { + if (pt1a.Y < pt1b.Y) SwapPoints(pt1a, pt1b); + if (pt2a.Y < pt2b.Y) SwapPoints(pt2a, pt2b); + if (pt1a.Y < pt2a.Y) pt1 = pt1a; else pt1 = pt2a; + if (pt1b.Y > pt2b.Y) pt2 = pt1b; else pt2 = pt2b; + return pt1.Y > pt2.Y; + } +} +//------------------------------------------------------------------------------ + +bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) +{ + OutPt *p = btmPt1->Prev; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Prev; + double dx1p = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + p = btmPt1->Next; + while ((p->Pt == btmPt1->Pt) && (p != btmPt1)) p = p->Next; + double dx1n = std::fabs(GetDx(btmPt1->Pt, p->Pt)); + + p = btmPt2->Prev; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Prev; + double dx2p = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + p = btmPt2->Next; + while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; + double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); + + if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && + std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); +} +//------------------------------------------------------------------------------ + +OutPt* GetBottomPt(OutPt *pp) +{ + OutPt* dups = 0; + OutPt* p = pp->Next; + while (p != pp) + { + if (p->Pt.Y > pp->Pt.Y) + { + pp = p; + dups = 0; + } + else if (p->Pt.Y == pp->Pt.Y && p->Pt.X <= pp->Pt.X) + { + if (p->Pt.X < pp->Pt.X) + { + dups = 0; + pp = p; + } else + { + if (p->Next != pp && p->Prev != pp) dups = p; + } + } + p = p->Next; + } + if (dups) + { + //there appears to be at least 2 vertices at BottomPt so ... + while (dups != p) + { + if (!FirstIsBottomPt(p, dups)) pp = dups; + dups = dups->Next; + while (dups->Pt != pp->Pt) dups = dups->Next; + } + } + return pp; +} +//------------------------------------------------------------------------------ + +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) +{ + if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) + return false; + else if (pt1.X != pt3.X) + return (pt2.X > pt1.X) == (pt2.X < pt3.X); + else + return (pt2.Y > pt1.Y) == (pt2.Y < pt3.Y); +} +//------------------------------------------------------------------------------ + +bool HorzSegmentsOverlap(cInt seg1a, cInt seg1b, cInt seg2a, cInt seg2b) +{ + if (seg1a > seg1b) std::swap(seg1a, seg1b); + if (seg2a > seg2b) std::swap(seg2a, seg2b); + return (seg1a < seg2b) && (seg2a < seg1b); +} + +//------------------------------------------------------------------------------ +// ClipperBase class methods ... +//------------------------------------------------------------------------------ + +ClipperBase::ClipperBase() //constructor +{ + m_CurrentLM = m_MinimaList.begin(); //begin() == end() here + m_UseFullRange = false; +} +//------------------------------------------------------------------------------ + +ClipperBase::~ClipperBase() //destructor +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void RangeTest(const IntPoint& Pt, bool& useFullRange) +{ + if (useFullRange) + { + if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) + throw clipperException("Coordinate outside allowed range"); + } + else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) + { + useFullRange = true; + RangeTest(Pt, useFullRange); + } +} +//------------------------------------------------------------------------------ + +TEdge* FindNextLocMin(TEdge* E) +{ + for (;;) + { + while (E->Bot != E->Prev->Bot || E->Curr == E->Top) E = E->Next; + if (!IsHorizontal(*E) && !IsHorizontal(*E->Prev)) break; + while (IsHorizontal(*E->Prev)) E = E->Prev; + TEdge* E2 = E; + while (IsHorizontal(*E)) E = E->Next; + if (E->Top.Y == E->Prev->Bot.Y) continue; //ie just an intermediate horz. + if (E2->Prev->Bot.X < E->Bot.X) E = E2; + break; + } + return E; +} +//------------------------------------------------------------------------------ + +TEdge* ClipperBase::ProcessBound(TEdge* E, bool NextIsForward) +{ + TEdge *Result = E; + TEdge *Horz = 0; + + if (E->OutIdx == Skip) + { + //if edges still remain in the current bound beyond the skip edge then + //create another LocMin and call ProcessBound once more + if (NextIsForward) + { + while (E->Top.Y == E->Next->Bot.Y) E = E->Next; + //don't include top horizontals when parsing a bound a second time, + //they will be contained in the opposite bound ... + while (E != Result && IsHorizontal(*E)) E = E->Prev; + } + else + { + while (E->Top.Y == E->Prev->Bot.Y) E = E->Prev; + while (E != Result && IsHorizontal(*E)) E = E->Next; + } + + if (E == Result) + { + if (NextIsForward) Result = E->Next; + else Result = E->Prev; + } + else + { + //there are more edges in the bound beyond result starting with E + if (NextIsForward) + E = Result->Next; + else + E = Result->Prev; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + E->WindDelta = 0; + Result = ProcessBound(E, NextIsForward); + m_MinimaList.push_back(locMin); + } + return Result; + } + + TEdge *EStart; + + if (IsHorizontal(*E)) + { + //We need to be careful with open paths because this may not be a + //true local minima (ie E may be following a skip edge). + //Also, consecutive horz. edges may start heading left before going right. + if (NextIsForward) + EStart = E->Prev; + else + EStart = E->Next; + if (IsHorizontal(*EStart)) //ie an adjoining horizontal skip edge + { + if (EStart->Bot.X != E->Bot.X && EStart->Top.X != E->Bot.X) + ReverseHorizontal(*E); + } + else if (EStart->Bot.X != E->Bot.X) + ReverseHorizontal(*E); + } + + EStart = E; + if (NextIsForward) + { + while (Result->Top.Y == Result->Next->Bot.Y && Result->Next->OutIdx != Skip) + Result = Result->Next; + if (IsHorizontal(*Result) && Result->Next->OutIdx != Skip) + { + //nb: at the top of a bound, horizontals are added to the bound + //only when the preceding edge attaches to the horizontal's left vertex + //unless a Skip edge is encountered when that becomes the top divide + Horz = Result; + while (IsHorizontal(*Horz->Prev)) Horz = Horz->Prev; + if (Horz->Prev->Top.X > Result->Next->Top.X) Result = Horz->Prev; + } + while (E != Result) + { + E->NextInLML = E->Next; + if (IsHorizontal(*E) && E != EStart && + E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + E = E->Next; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Prev->Top.X) + ReverseHorizontal(*E); + Result = Result->Next; //move to the edge just beyond current bound + } else + { + while (Result->Top.Y == Result->Prev->Bot.Y && Result->Prev->OutIdx != Skip) + Result = Result->Prev; + if (IsHorizontal(*Result) && Result->Prev->OutIdx != Skip) + { + Horz = Result; + while (IsHorizontal(*Horz->Next)) Horz = Horz->Next; + if (Horz->Next->Top.X == Result->Prev->Top.X || + Horz->Next->Top.X > Result->Prev->Top.X) Result = Horz->Next; + } + + while (E != Result) + { + E->NextInLML = E->Prev; + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + E = E->Prev; + } + if (IsHorizontal(*E) && E != EStart && E->Bot.X != E->Next->Top.X) + ReverseHorizontal(*E); + Result = Result->Prev; //move to the edge just beyond current bound + } + + return Result; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) +{ +#ifdef use_lines + if (!Closed && PolyTyp == ptClip) + throw clipperException("AddPath: Open paths must be subject."); +#else + if (!Closed) + throw clipperException("AddPath: Open paths have been disabled."); +#endif + + int highI = (int)pg.size() -1; + if (Closed) while (highI > 0 && (pg[highI] == pg[0])) --highI; + while (highI > 0 && (pg[highI] == pg[highI -1])) --highI; + if ((Closed && highI < 2) || (!Closed && highI < 1)) return false; + + //create a new edge array ... + TEdge *edges = new TEdge [highI +1]; + + bool IsFlat = true; + //1. Basic (first) edge initialization ... + try + { + edges[1].Curr = pg[1]; + RangeTest(pg[0], m_UseFullRange); + RangeTest(pg[highI], m_UseFullRange); + InitEdge(&edges[0], &edges[1], &edges[highI], pg[0]); + InitEdge(&edges[highI], &edges[0], &edges[highI-1], pg[highI]); + for (int i = highI - 1; i >= 1; --i) + { + RangeTest(pg[i], m_UseFullRange); + InitEdge(&edges[i], &edges[i+1], &edges[i-1], pg[i]); + } + } + catch(...) + { + delete [] edges; + throw; //range test fails + } + TEdge *eStart = &edges[0]; + + //2. Remove duplicate vertices, and (when closed) collinear edges ... + TEdge *E = eStart, *eLoopStop = eStart; + for (;;) + { + //nb: allows matching start and end points when not Closed ... + if (E->Curr == E->Next->Curr && (Closed || E->Next != eStart)) + { + if (E == E->Next) break; + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + eLoopStop = E; + continue; + } + if (E->Prev == E->Next) + break; //only two vertices + else if (Closed && + SlopesEqual(E->Prev->Curr, E->Curr, E->Next->Curr, m_UseFullRange) && + (!m_PreserveCollinear || + !Pt2IsBetweenPt1AndPt3(E->Prev->Curr, E->Curr, E->Next->Curr))) + { + //Collinear edges are allowed for open paths but in closed paths + //the default is to merge adjacent collinear edges into a single edge. + //However, if the PreserveCollinear property is enabled, only overlapping + //collinear edges (ie spikes) will be removed from closed paths. + if (E == eStart) eStart = E->Next; + E = RemoveEdge(E); + E = E->Prev; + eLoopStop = E; + continue; + } + E = E->Next; + if ((E == eLoopStop) || (!Closed && E->Next == eStart)) break; + } + + if ((!Closed && (E == E->Next)) || (Closed && (E->Prev == E->Next))) + { + delete [] edges; + return false; + } + + if (!Closed) + { + m_HasOpenPaths = true; + eStart->Prev->OutIdx = Skip; + } + + //3. Do second stage of edge initialization ... + E = eStart; + do + { + InitEdge2(*E, PolyTyp); + E = E->Next; + if (IsFlat && E->Curr.Y != eStart->Curr.Y) IsFlat = false; + } + while (E != eStart); + + //4. Finally, add edge bounds to LocalMinima list ... + + //Totally flat paths must be handled differently when adding them + //to LocalMinima list to avoid endless loops etc ... + if (IsFlat) + { + if (Closed) + { + delete [] edges; + return false; + } + E->Prev->OutIdx = Skip; + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + locMin.LeftBound = 0; + locMin.RightBound = E; + locMin.RightBound->Side = esRight; + locMin.RightBound->WindDelta = 0; + for (;;) + { + if (E->Bot.X != E->Prev->Top.X) ReverseHorizontal(*E); + if (E->Next->OutIdx == Skip) break; + E->NextInLML = E->Next; + E = E->Next; + } + m_MinimaList.push_back(locMin); + m_edges.push_back(edges); + return true; + } + + m_edges.push_back(edges); + bool leftBoundIsForward; + TEdge* EMin = 0; + + //workaround to avoid an endless loop in the while loop below when + //open paths have matching start and end points ... + if (E->Prev->Bot == E->Prev->Top) E = E->Next; + + for (;;) + { + E = FindNextLocMin(E); + if (E == EMin) break; + else if (!EMin) EMin = E; + + //E and E.Prev now share a local minima (left aligned if horizontal). + //Compare their slopes to find which starts which bound ... + MinimaList::value_type locMin; + locMin.Y = E->Bot.Y; + if (E->Dx < E->Prev->Dx) + { + locMin.LeftBound = E->Prev; + locMin.RightBound = E; + leftBoundIsForward = false; //Q.nextInLML = Q.prev + } else + { + locMin.LeftBound = E; + locMin.RightBound = E->Prev; + leftBoundIsForward = true; //Q.nextInLML = Q.next + } + + if (!Closed) locMin.LeftBound->WindDelta = 0; + else if (locMin.LeftBound->Next == locMin.RightBound) + locMin.LeftBound->WindDelta = -1; + else locMin.LeftBound->WindDelta = 1; + locMin.RightBound->WindDelta = -locMin.LeftBound->WindDelta; + + E = ProcessBound(locMin.LeftBound, leftBoundIsForward); + if (E->OutIdx == Skip) E = ProcessBound(E, leftBoundIsForward); + + TEdge* E2 = ProcessBound(locMin.RightBound, !leftBoundIsForward); + if (E2->OutIdx == Skip) E2 = ProcessBound(E2, !leftBoundIsForward); + + if (locMin.LeftBound->OutIdx == Skip) + locMin.LeftBound = 0; + else if (locMin.RightBound->OutIdx == Skip) + locMin.RightBound = 0; + m_MinimaList.push_back(locMin); + if (!leftBoundIsForward) E = E2; + } + return true; +} +//------------------------------------------------------------------------------ + +bool ClipperBase::AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed) +{ + bool result = false; + for (Paths::size_type i = 0; i < ppg.size(); ++i) + if (AddPath(ppg[i], PolyTyp, Closed)) result = true; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Clear() +{ + DisposeLocalMinimaList(); + for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) + { + TEdge* edges = m_edges[i]; + delete [] edges; + } + m_edges.clear(); + m_UseFullRange = false; + m_HasOpenPaths = false; +} +//------------------------------------------------------------------------------ + +void ClipperBase::Reset() +{ + m_CurrentLM = m_MinimaList.begin(); + if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process + std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue + //reset all edges ... + for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) + { + InsertScanbeam(lm->Y); + TEdge* e = lm->LeftBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esLeft; + e->OutIdx = Unassigned; + } + + e = lm->RightBound; + if (e) + { + e->Curr = e->Bot; + e->Side = esRight; + e->OutIdx = Unassigned; + } + } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeLocalMinimaList() +{ + m_MinimaList.clear(); + m_CurrentLM = m_MinimaList.begin(); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) +{ + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); + ++m_CurrentLM; + return true; +} +//------------------------------------------------------------------------------ + +IntRect ClipperBase::GetBounds() +{ + IntRect result; + MinimaList::iterator lm = m_MinimaList.begin(); + if (lm == m_MinimaList.end()) + { + result.left = result.top = result.right = result.bottom = 0; + return result; + } + result.left = lm->LeftBound->Bot.X; + result.top = lm->LeftBound->Bot.Y; + result.right = lm->LeftBound->Bot.X; + result.bottom = lm->LeftBound->Bot.Y; + while (lm != m_MinimaList.end()) + { + //todo - needs fixing for open paths + result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); + TEdge* e = lm->LeftBound; + for (;;) { + TEdge* bottomE = e; + while (e->NextInLML) + { + if (e->Bot.X < result.left) result.left = e->Bot.X; + if (e->Bot.X > result.right) result.right = e->Bot.X; + e = e->NextInLML; + } + result.left = std::min(result.left, e->Bot.X); + result.right = std::max(result.right, e->Bot.X); + result.left = std::min(result.left, e->Top.X); + result.right = std::max(result.right, e->Top.X); + result.top = std::min(result.top, e->Top.Y); + if (bottomE == lm->LeftBound) e = lm->RightBound; + else break; + } + ++lm; + } + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopScanbeam(cInt &Y) +{ + if (m_Scanbeam.empty()) return false; + Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted + if (AelPrev) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +OutRec* ClipperBase::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size() - 1; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if (Edge1->NextInAEL == Edge2) + { + TEdge* Next = Edge2->NextInAEL; + if (Next) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if (Edge2->NextInAEL == Edge1) + { + TEdge* Next = Edge1->NextInAEL; + if (Next) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; + else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) +{ + if (!e->NextInLML) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::LocalMinimaPending() +{ + return (m_CurrentLM != m_MinimaList.end()); +} + +//------------------------------------------------------------------------------ +// TClipper methods ... +//------------------------------------------------------------------------------ + +Clipper::Clipper(int initOptions) : ClipperBase() //constructor +{ + m_ExecuteLocked = false; + m_UseFullRange = false; + m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); + m_StrictSimple = ((initOptions & ioStrictlySimple) != 0); + m_PreserveCollinear = ((initOptions & ioPreserveCollinear) != 0); + m_HasOpenPaths = false; +#ifdef use_xyz + m_ZFill = 0; +#endif +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::ZFillFunction(ZFillCallback zFillFunc) +{ + m_ZFill = zFillFunc; +} +//------------------------------------------------------------------------------ +#endif + +bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) +{ + return Execute(clipType, solution, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree &polytree, PolyFillType fillType) +{ + return Execute(clipType, polytree, fillType, fillType); +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, Paths &solution, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + if (m_HasOpenPaths) + throw clipperException("Error: PolyTree struct is needed for open path clipping."); + m_ExecuteLocked = true; + solution.resize(0); + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = false; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult(solution); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +bool Clipper::Execute(ClipType clipType, PolyTree& polytree, + PolyFillType subjFillType, PolyFillType clipFillType) +{ + if( m_ExecuteLocked ) return false; + m_ExecuteLocked = true; + m_SubjFillType = subjFillType; + m_ClipFillType = clipFillType; + m_ClipType = clipType; + m_UsingPolyTree = true; + bool succeeded = ExecuteInternal(); + if (succeeded) BuildResult2(polytree); + DisposeAllOutRecs(); + m_ExecuteLocked = false; + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::FixHoleLinkage(OutRec &outrec) +{ + //skip OutRecs that (a) contain outermost polygons or + //(b) already have the correct owner/child linkage ... + if (!outrec.FirstLeft || + (outrec.IsHole != outrec.FirstLeft->IsHole && + outrec.FirstLeft->Pts)) return; + + OutRec* orfl = outrec.FirstLeft; + while (orfl && ((orfl->IsHole == outrec.IsHole) || !orfl->Pts)) + orfl = orfl->FirstLeft; + outrec.FirstLeft = orfl; +} +//------------------------------------------------------------------------------ + +bool Clipper::ExecuteInternal() +{ + bool succeeded = true; + try { + Reset(); + m_Maxima = MaximaList(); + m_SortedEdges = 0; + + succeeded = true; + cInt botY, topY; + if (!PopScanbeam(botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(topY) || LocalMinimaPending()) + { + ProcessHorizontals(); + ClearGhostJoins(); + if (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } + ProcessEdgesAtTopOfScanbeam(topY); + botY = topY; + InsertLocalMinimaIntoAEL(botY); + } + } + catch(...) + { + succeeded = false; + } + + if (succeeded) + { + //fix orientations ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts || outRec->IsOpen) continue; + if ((outRec->IsHole ^ m_ReverseOutput) == (Area(*outRec) > 0)) + ReversePolyPtLinks(outRec->Pts); + } + + if (!m_Joins.empty()) JoinCommonEdges(); + + //unfortunately FixupOutPolygon() must be done after JoinCommonEdges() + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec *outRec = m_PolyOuts[i]; + if (!outRec->Pts) continue; + if (outRec->IsOpen) + FixupOutPolyline(*outRec); + else + FixupOutPolygon(*outRec); + } + + if (m_StrictSimple) DoSimplePolygons(); + } + + ClearJoins(); + ClearGhostJoins(); + return succeeded; +} +//------------------------------------------------------------------------------ + +void Clipper::SetWindingCount(TEdge &edge) +{ + TEdge *e = edge.PrevInAEL; + //find the edge of the same polytype that immediately preceeds 'edge' in AEL + while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; + if (!e) + { + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; + edge.WindCnt2 = 0; + e = m_ActiveEdges; //ie get ready to calc WindCnt2 + } + else if (edge.WindDelta == 0 && m_ClipType != ctUnion) + { + edge.WindCnt = 1; + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else if (IsEvenOddFillType(edge)) + { + //EvenOdd filling ... + if (edge.WindDelta == 0) + { + //are we inside a subj polygon ... + bool Inside = true; + TEdge *e2 = e->PrevInAEL; + while (e2) + { + if (e2->PolyTyp == e->PolyTyp && e2->WindDelta != 0) + Inside = !Inside; + e2 = e2->PrevInAEL; + } + edge.WindCnt = (Inside ? 0 : 1); + } + else + { + edge.WindCnt = edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + else + { + //nonZero, Positive or Negative filling ... + if (e->WindCnt * e->WindDelta < 0) + { + //prev edge is 'decreasing' WindCount (WC) toward zero + //so we're outside the previous polygon ... + if (Abs(e->WindCnt) > 1) + { + //outside prev poly but still inside another. + //when reversing direction of prev poly use the same WC + if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise continue to 'decrease' WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + else + //now outside all polys of same polytype so set own WC ... + edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + } else + { + //prev edge is 'increasing' WindCount (WC) away from zero + //so we're inside the previous polygon ... + if (edge.WindDelta == 0) + edge.WindCnt = (e->WindCnt < 0 ? e->WindCnt - 1 : e->WindCnt + 1); + //if wind direction is reversing prev then use same WC + else if (e->WindDelta * edge.WindDelta < 0) edge.WindCnt = e->WindCnt; + //otherwise add to WC ... + else edge.WindCnt = e->WindCnt + edge.WindDelta; + } + edge.WindCnt2 = e->WindCnt2; + e = e->NextInAEL; //ie get ready to calc WindCnt2 + } + + //update WindCnt2 ... + if (IsEvenOddAltFillType(edge)) + { + //EvenOdd filling ... + while (e != &edge) + { + if (e->WindDelta != 0) + edge.WindCnt2 = (edge.WindCnt2 == 0 ? 1 : 0); + e = e->NextInAEL; + } + } else + { + //nonZero, Positive or Negative filling ... + while ( e != &edge ) + { + edge.WindCnt2 += e->WindDelta; + e = e->NextInAEL; + } + } +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_SubjFillType == pftEvenOdd; else + return m_ClipFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsEvenOddAltFillType(const TEdge& edge) const +{ + if (edge.PolyTyp == ptSubject) + return m_ClipFillType == pftEvenOdd; else + return m_SubjFillType == pftEvenOdd; +} +//------------------------------------------------------------------------------ + +bool Clipper::IsContributing(const TEdge& edge) const +{ + PolyFillType pft, pft2; + if (edge.PolyTyp == ptSubject) + { + pft = m_SubjFillType; + pft2 = m_ClipFillType; + } else + { + pft = m_ClipFillType; + pft2 = m_SubjFillType; + } + + switch(pft) + { + case pftEvenOdd: + //return false if a subj line has been flagged as inside a subj polygon + if (edge.WindDelta == 0 && edge.WindCnt != 1) return false; + break; + case pftNonZero: + if (Abs(edge.WindCnt) != 1) return false; + break; + case pftPositive: + if (edge.WindCnt != 1) return false; + break; + default: //pftNegative + if (edge.WindCnt != -1) return false; + } + + switch(m_ClipType) + { + case ctIntersection: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctUnion: + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + break; + case ctDifference: + if (edge.PolyTyp == ptSubject) + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 != 0); + case pftPositive: + return (edge.WindCnt2 > 0); + default: + return (edge.WindCnt2 < 0); + } + break; + case ctXor: + if (edge.WindDelta == 0) //XOr always contributing unless open + switch(pft2) + { + case pftEvenOdd: + case pftNonZero: + return (edge.WindCnt2 == 0); + case pftPositive: + return (edge.WindCnt2 <= 0); + default: + return (edge.WindCnt2 >= 0); + } + else + return true; + break; + default: + return true; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + OutPt* result; + TEdge *e, *prevE; + if (IsHorizontal(*e2) || ( e1->Dx > e2->Dx )) + { + result = AddOutPt(e1, Pt); + e2->OutIdx = e1->OutIdx; + e1->Side = esLeft; + e2->Side = esRight; + e = e1; + if (e->PrevInAEL == e2) + prevE = e2->PrevInAEL; + else + prevE = e->PrevInAEL; + } else + { + result = AddOutPt(e2, Pt); + e1->OutIdx = e2->OutIdx; + e1->Side = esRight; + e2->Side = esLeft; + e = e2; + if (e->PrevInAEL == e1) + prevE = e1->PrevInAEL; + else + prevE = e->PrevInAEL; + } + + if (prevE && prevE->OutIdx >= 0) + { + cInt xPrev = TopX(*prevE, Pt.Y); + cInt xE = TopX(*e, Pt.Y); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } + } + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) +{ + AddOutPt( e1, Pt ); + if (e2->WindDelta == 0) AddOutPt(e2, Pt); + if( e1->OutIdx == e2->OutIdx ) + { + e1->OutIdx = Unassigned; + e2->OutIdx = Unassigned; + } + else if (e1->OutIdx < e2->OutIdx) + AppendPolygon(e1, e2); + else + AppendPolygon(e2, e1); +} +//------------------------------------------------------------------------------ + +void Clipper::AddEdgeToSEL(TEdge *edge) +{ + //SEL pointers in PEdge are reused to build a list of horizontal edges. + //However, we don't need to worry about order with horizontal edge processing. + if( !m_SortedEdges ) + { + m_SortedEdges = edge; + edge->PrevInSEL = 0; + edge->NextInSEL = 0; + } + else + { + edge->NextInSEL = m_SortedEdges; + edge->PrevInSEL = 0; + m_SortedEdges->PrevInSEL = edge; + m_SortedEdges = edge; + } +} +//------------------------------------------------------------------------------ + +bool Clipper::PopEdgeFromSEL(TEdge *&edge) +{ + if (!m_SortedEdges) return false; + edge = m_SortedEdges; + DeleteFromSEL(m_SortedEdges); + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::CopyAELToSEL() +{ + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while ( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op1; + j->OutPt2 = op2; + j->OffPt = OffPt; + m_Joins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearJoins() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + delete m_Joins[i]; + m_Joins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::ClearGhostJoins() +{ + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); i++) + delete m_GhostJoins[i]; + m_GhostJoins.resize(0); +} +//------------------------------------------------------------------------------ + +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) +{ + Join* j = new Join; + j->OutPt1 = op; + j->OutPt2 = 0; + j->OffPt = OffPt; + m_GhostJoins.push_back(j); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) +{ + const LocalMinimum *lm; + while (PopLocalMinima(botY, lm)) + { + TEdge* lb = lm->LeftBound; + TEdge* rb = lm->RightBound; + + OutPt *Op1 = 0; + if (!lb) + { + //nb: don't insert LB into either AEL or SEL + InsertEdgeIntoAEL(rb, 0); + SetWindingCount(*rb); + if (IsContributing(*rb)) + Op1 = AddOutPt(rb, rb->Bot); + } + else if (!rb) + { + InsertEdgeIntoAEL(lb, 0); + SetWindingCount(*lb); + if (IsContributing(*lb)) + Op1 = AddOutPt(lb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + else + { + InsertEdgeIntoAEL(lb, 0); + InsertEdgeIntoAEL(rb, lb); + SetWindingCount( *lb ); + rb->WindCnt = lb->WindCnt; + rb->WindCnt2 = lb->WindCnt2; + if (IsContributing(*lb)) + Op1 = AddLocalMinPoly(lb, rb, lb->Bot); + InsertScanbeam(lb->Top.Y); + } + + if (rb) + { + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + InsertScanbeam(rb->NextInLML->Top.Y); + } + else InsertScanbeam( rb->Top.Y ); + } + + if (!lb || !rb) continue; + + //if any output polygons share an edge, they'll need joining later ... + if (Op1 && IsHorizontal(*rb) && + m_GhostJoins.size() > 0 && (rb->WindDelta != 0)) + { + for (JoinList::size_type i = 0; i < m_GhostJoins.size(); ++i) + { + Join* jr = m_GhostJoins[i]; + //if the horizontal Rb and a 'ghost' horizontal overlap, then convert + //the 'ghost' join to a real join ready for later ... + if (HorzSegmentsOverlap(jr->OutPt1->Pt.X, jr->OffPt.X, rb->Bot.X, rb->Top.X)) + AddJoin(jr->OutPt1, Op1, jr->OffPt); + } + } + + if (lb->OutIdx >= 0 && lb->PrevInAEL && + lb->PrevInAEL->Curr.X == lb->Bot.X && + lb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && + (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); + AddJoin(Op1, Op2, lb->Top); + } + + if(lb->NextInAEL != rb) + { + + if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && + (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) + { + OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); + AddJoin(Op1, Op2, rb->Top); + } + + TEdge* e = lb->NextInAEL; + if (e) + { + while( e != rb ) + { + //nb: For calculating winding counts etc, IntersectEdges() assumes + //that param1 will be to the Right of param2 ABOVE the intersection ... + IntersectEdges(rb , e , lb->Curr); //order important here + e = e->NextInAEL; + } + } + } + + } +} +//------------------------------------------------------------------------------ + +void Clipper::DeleteFromSEL(TEdge *e) +{ + TEdge* SelPrev = e->PrevInSEL; + TEdge* SelNext = e->NextInSEL; + if( !SelPrev && !SelNext && (e != m_SortedEdges) ) return; //already deleted + if( SelPrev ) SelPrev->NextInSEL = SelNext; + else m_SortedEdges = SelNext; + if( SelNext ) SelNext->PrevInSEL = SelPrev; + e->NextInSEL = 0; + e->PrevInSEL = 0; +} +//------------------------------------------------------------------------------ + +#ifdef use_xyz +void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2) +{ + if (pt.Z != 0 || !m_ZFill) return; + else if (pt == e1.Bot) pt.Z = e1.Bot.Z; + else if (pt == e1.Top) pt.Z = e1.Top.Z; + else if (pt == e2.Bot) pt.Z = e2.Bot.Z; + else if (pt == e2.Top) pt.Z = e2.Top.Z; + else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); +} +//------------------------------------------------------------------------------ +#endif + +void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) +{ + bool e1Contributing = ( e1->OutIdx >= 0 ); + bool e2Contributing = ( e2->OutIdx >= 0 ); + +#ifdef use_xyz + SetZ(Pt, *e1, *e2); +#endif + +#ifdef use_lines + //if either edge is on an OPEN path ... + if (e1->WindDelta == 0 || e2->WindDelta == 0) + { + //ignore subject-subject open path intersections UNLESS they + //are both open paths, AND they are both 'contributing maximas' ... + if (e1->WindDelta == 0 && e2->WindDelta == 0) return; + + //if intersecting a subj line with a subj poly ... + else if (e1->PolyTyp == e2->PolyTyp && + e1->WindDelta != e2->WindDelta && m_ClipType == ctUnion) + { + if (e1->WindDelta == 0) + { + if (e2Contributing) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + } + else + { + if (e1Contributing) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + } + else if (e1->PolyTyp != e2->PolyTyp) + { + //toggle subj open path OutIdx on/off when Abs(clip.WndCnt) == 1 ... + if ((e1->WindDelta == 0) && abs(e2->WindCnt) == 1 && + (m_ClipType != ctUnion || e2->WindCnt2 == 0)) + { + AddOutPt(e1, Pt); + if (e1Contributing) e1->OutIdx = Unassigned; + } + else if ((e2->WindDelta == 0) && (abs(e1->WindCnt) == 1) && + (m_ClipType != ctUnion || e1->WindCnt2 == 0)) + { + AddOutPt(e2, Pt); + if (e2Contributing) e2->OutIdx = Unassigned; + } + } + return; + } +#endif + + //update winding counts... + //assumes that e1 will be to the Right of e2 ABOVE the intersection + if ( e1->PolyTyp == e2->PolyTyp ) + { + if ( IsEvenOddFillType( *e1) ) + { + int oldE1WindCnt = e1->WindCnt; + e1->WindCnt = e2->WindCnt; + e2->WindCnt = oldE1WindCnt; + } else + { + if (e1->WindCnt + e2->WindDelta == 0 ) e1->WindCnt = -e1->WindCnt; + else e1->WindCnt += e2->WindDelta; + if ( e2->WindCnt - e1->WindDelta == 0 ) e2->WindCnt = -e2->WindCnt; + else e2->WindCnt -= e1->WindDelta; + } + } else + { + if (!IsEvenOddFillType(*e2)) e1->WindCnt2 += e2->WindDelta; + else e1->WindCnt2 = ( e1->WindCnt2 == 0 ) ? 1 : 0; + if (!IsEvenOddFillType(*e1)) e2->WindCnt2 -= e1->WindDelta; + else e2->WindCnt2 = ( e2->WindCnt2 == 0 ) ? 1 : 0; + } + + PolyFillType e1FillType, e2FillType, e1FillType2, e2FillType2; + if (e1->PolyTyp == ptSubject) + { + e1FillType = m_SubjFillType; + e1FillType2 = m_ClipFillType; + } else + { + e1FillType = m_ClipFillType; + e1FillType2 = m_SubjFillType; + } + if (e2->PolyTyp == ptSubject) + { + e2FillType = m_SubjFillType; + e2FillType2 = m_ClipFillType; + } else + { + e2FillType = m_ClipFillType; + e2FillType2 = m_SubjFillType; + } + + cInt e1Wc, e2Wc; + switch (e1FillType) + { + case pftPositive: e1Wc = e1->WindCnt; break; + case pftNegative: e1Wc = -e1->WindCnt; break; + default: e1Wc = Abs(e1->WindCnt); + } + switch(e2FillType) + { + case pftPositive: e2Wc = e2->WindCnt; break; + case pftNegative: e2Wc = -e2->WindCnt; break; + default: e2Wc = Abs(e2->WindCnt); + } + + if ( e1Contributing && e2Contributing ) + { + if ((e1Wc != 0 && e1Wc != 1) || (e2Wc != 0 && e2Wc != 1) || + (e1->PolyTyp != e2->PolyTyp && m_ClipType != ctXor) ) + { + AddLocalMaxPoly(e1, e2, Pt); + } + else + { + AddOutPt(e1, Pt); + AddOutPt(e2, Pt); + SwapSides( *e1 , *e2 ); + SwapPolyIndexes( *e1 , *e2 ); + } + } + else if ( e1Contributing ) + { + if (e2Wc == 0 || e2Wc == 1) + { + AddOutPt(e1, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( e2Contributing ) + { + if (e1Wc == 0 || e1Wc == 1) + { + AddOutPt(e2, Pt); + SwapSides(*e1, *e2); + SwapPolyIndexes(*e1, *e2); + } + } + else if ( (e1Wc == 0 || e1Wc == 1) && (e2Wc == 0 || e2Wc == 1)) + { + //neither edge is currently contributing ... + + cInt e1Wc2, e2Wc2; + switch (e1FillType2) + { + case pftPositive: e1Wc2 = e1->WindCnt2; break; + case pftNegative : e1Wc2 = -e1->WindCnt2; break; + default: e1Wc2 = Abs(e1->WindCnt2); + } + switch (e2FillType2) + { + case pftPositive: e2Wc2 = e2->WindCnt2; break; + case pftNegative: e2Wc2 = -e2->WindCnt2; break; + default: e2Wc2 = Abs(e2->WindCnt2); + } + + if (e1->PolyTyp != e2->PolyTyp) + { + AddLocalMinPoly(e1, e2, Pt); + } + else if (e1Wc == 1 && e2Wc == 1) + switch( m_ClipType ) { + case ctIntersection: + if (e1Wc2 > 0 && e2Wc2 > 0) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctUnion: + if ( e1Wc2 <= 0 && e2Wc2 <= 0 ) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctDifference: + if (((e1->PolyTyp == ptClip) && (e1Wc2 > 0) && (e2Wc2 > 0)) || + ((e1->PolyTyp == ptSubject) && (e1Wc2 <= 0) && (e2Wc2 <= 0))) + AddLocalMinPoly(e1, e2, Pt); + break; + case ctXor: + AddLocalMinPoly(e1, e2, Pt); + } + else + SwapSides( *e1, *e2 ); + } +} +//------------------------------------------------------------------------------ + +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) +{ + TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; + while (e2) + { + if (e2->OutIdx >= 0 && e2->WindDelta != 0) + { + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; + } + e2 = e2->PrevInAEL; + } + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } +} +//------------------------------------------------------------------------------ + +OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) +{ + //work out which polygon fragment has the correct hole state ... + if (!outRec1->BottomPt) + outRec1->BottomPt = GetBottomPt(outRec1->Pts); + if (!outRec2->BottomPt) + outRec2->BottomPt = GetBottomPt(outRec2->Pts); + OutPt *OutPt1 = outRec1->BottomPt; + OutPt *OutPt2 = outRec2->BottomPt; + if (OutPt1->Pt.Y > OutPt2->Pt.Y) return outRec1; + else if (OutPt1->Pt.Y < OutPt2->Pt.Y) return outRec2; + else if (OutPt1->Pt.X < OutPt2->Pt.X) return outRec1; + else if (OutPt1->Pt.X > OutPt2->Pt.X) return outRec2; + else if (OutPt1->Next == OutPt1) return outRec2; + else if (OutPt2->Next == OutPt2) return outRec1; + else if (FirstIsBottomPt(OutPt1, OutPt2)) return outRec1; + else return outRec2; +} +//------------------------------------------------------------------------------ + +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) +{ + do + { + outRec1 = outRec1->FirstLeft; + if (outRec1 == outRec2) return true; + } while (outRec1); + return false; +} +//------------------------------------------------------------------------------ + +OutRec* Clipper::GetOutRec(int Idx) +{ + OutRec* outrec = m_PolyOuts[Idx]; + while (outrec != m_PolyOuts[outrec->Idx]) + outrec = m_PolyOuts[outrec->Idx]; + return outrec; +} +//------------------------------------------------------------------------------ + +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) +{ + //get the start and ends of both output polygons ... + OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; + OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; + + OutRec *holeStateRec; + if (OutRec1RightOfOutRec2(outRec1, outRec2)) + holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) + holeStateRec = outRec1; + else + holeStateRec = GetLowermostRec(outRec1, outRec2); + + //get the start and ends of both output polygons and + //join e2 poly onto e1 poly and delete pointers to e2 ... + + OutPt* p1_lft = outRec1->Pts; + OutPt* p1_rt = p1_lft->Prev; + OutPt* p2_lft = outRec2->Pts; + OutPt* p2_rt = p2_lft->Prev; + + //join e2 poly onto e1 poly and delete pointers to e2 ... + if( e1->Side == esLeft ) + { + if( e2->Side == esLeft ) + { + //z y x a b c + ReversePolyPtLinks(p2_lft); + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + outRec1->Pts = p2_rt; + } else + { + //x y z a b c + p2_rt->Next = p1_lft; + p1_lft->Prev = p2_rt; + p2_lft->Prev = p1_rt; + p1_rt->Next = p2_lft; + outRec1->Pts = p2_lft; + } + } else + { + if( e2->Side == esRight ) + { + //a b c z y x + ReversePolyPtLinks(p2_lft); + p1_rt->Next = p2_rt; + p2_rt->Prev = p1_rt; + p2_lft->Next = p1_lft; + p1_lft->Prev = p2_lft; + } else + { + //a b c x y z + p1_rt->Next = p2_lft; + p2_lft->Prev = p1_rt; + p1_lft->Prev = p2_rt; + p2_rt->Next = p1_lft; + } + } + + outRec1->BottomPt = 0; + if (holeStateRec == outRec2) + { + if (outRec2->FirstLeft != outRec1) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec1->IsHole = outRec2->IsHole; + } + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->FirstLeft = outRec1; + + int OKIdx = e1->OutIdx; + int ObsoleteIdx = e2->OutIdx; + + e1->OutIdx = Unassigned; //nb: safe because we only get here via AddLocalMaxPoly + e2->OutIdx = Unassigned; + + TEdge* e = m_ActiveEdges; + while( e ) + { + if( e->OutIdx == ObsoleteIdx ) + { + e->OutIdx = OKIdx; + e->Side = e1->Side; + break; + } + e = e->NextInAEL; + } + + outRec2->Idx = outRec1->Idx; +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) +{ + if( e->OutIdx < 0 ) + { + OutRec *outRec = CreateOutRec(); + outRec->IsOpen = (e->WindDelta == 0); + OutPt* newOp = new OutPt; + outRec->Pts = newOp; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = newOp; + newOp->Prev = newOp; + if (!outRec->IsOpen) + SetHoleState(e, outRec); + e->OutIdx = outRec->Idx; + return newOp; + } else + { + OutRec *outRec = m_PolyOuts[e->OutIdx]; + //OutRec.Pts is the 'Left-most' point & OutRec.Pts.Prev is the 'Right-most' + OutPt* op = outRec->Pts; + + bool ToFront = (e->Side == esLeft); + if (ToFront && (pt == op->Pt)) return op; + else if (!ToFront && (pt == op->Prev->Pt)) return op->Prev; + + OutPt* newOp = new OutPt; + newOp->Idx = outRec->Idx; + newOp->Pt = pt; + newOp->Next = op; + newOp->Prev = op->Prev; + newOp->Prev->Next = newOp; + op->Prev = newOp; + if (ToFront) outRec->Pts = newOp; + return newOp; + } +} +//------------------------------------------------------------------------------ + +OutPt* Clipper::GetLastOutPt(TEdge *e) +{ + OutRec *outRec = m_PolyOuts[e->OutIdx]; + if (e->Side == esLeft) + return outRec->Pts; + else + return outRec->Pts->Prev; +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessHorizontals() +{ + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) + ProcessHorizontal(horzEdge); +} +//------------------------------------------------------------------------------ + +inline bool IsMinima(TEdge *e) +{ + return e && (e->Prev->NextInLML != e) && (e->Next->NextInLML != e); +} +//------------------------------------------------------------------------------ + +inline bool IsMaxima(TEdge *e, const cInt Y) +{ + return e && e->Top.Y == Y && !e->NextInLML; +} +//------------------------------------------------------------------------------ + +inline bool IsIntermediate(TEdge *e, const cInt Y) +{ + return e->Top.Y == Y && e->NextInLML; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPair(TEdge *e) +{ + if ((e->Next->Top == e->Top) && !e->Next->NextInLML) + return e->Next; + else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) + return e->Prev; + else return 0; +} +//------------------------------------------------------------------------------ + +TEdge *GetMaximaPairEx(TEdge *e) +{ + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::SwapPositionsInSEL(TEdge *Edge1, TEdge *Edge2) +{ + if( !( Edge1->NextInSEL ) && !( Edge1->PrevInSEL ) ) return; + if( !( Edge2->NextInSEL ) && !( Edge2->PrevInSEL ) ) return; + + if( Edge1->NextInSEL == Edge2 ) + { + TEdge* Next = Edge2->NextInSEL; + if( Next ) Next->PrevInSEL = Edge1; + TEdge* Prev = Edge1->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge2; + Edge2->PrevInSEL = Prev; + Edge2->NextInSEL = Edge1; + Edge1->PrevInSEL = Edge2; + Edge1->NextInSEL = Next; + } + else if( Edge2->NextInSEL == Edge1 ) + { + TEdge* Next = Edge1->NextInSEL; + if( Next ) Next->PrevInSEL = Edge2; + TEdge* Prev = Edge2->PrevInSEL; + if( Prev ) Prev->NextInSEL = Edge1; + Edge1->PrevInSEL = Prev; + Edge1->NextInSEL = Edge2; + Edge2->PrevInSEL = Edge1; + Edge2->NextInSEL = Next; + } + else + { + TEdge* Next = Edge1->NextInSEL; + TEdge* Prev = Edge1->PrevInSEL; + Edge1->NextInSEL = Edge2->NextInSEL; + if( Edge1->NextInSEL ) Edge1->NextInSEL->PrevInSEL = Edge1; + Edge1->PrevInSEL = Edge2->PrevInSEL; + if( Edge1->PrevInSEL ) Edge1->PrevInSEL->NextInSEL = Edge1; + Edge2->NextInSEL = Next; + if( Edge2->NextInSEL ) Edge2->NextInSEL->PrevInSEL = Edge2; + Edge2->PrevInSEL = Prev; + if( Edge2->PrevInSEL ) Edge2->PrevInSEL->NextInSEL = Edge2; + } + + if( !Edge1->PrevInSEL ) m_SortedEdges = Edge1; + else if( !Edge2->PrevInSEL ) m_SortedEdges = Edge2; +} +//------------------------------------------------------------------------------ + +TEdge* GetNextInAEL(TEdge *e, Direction dir) +{ + return dir == dLeftToRight ? e->NextInAEL : e->PrevInAEL; +} +//------------------------------------------------------------------------------ + +void GetHorzDirection(TEdge& HorzEdge, Direction& Dir, cInt& Left, cInt& Right) +{ + if (HorzEdge.Bot.X < HorzEdge.Top.X) + { + Left = HorzEdge.Bot.X; + Right = HorzEdge.Top.X; + Dir = dLeftToRight; + } else + { + Left = HorzEdge.Top.X; + Right = HorzEdge.Bot.X; + Dir = dRightToLeft; + } +} +//------------------------------------------------------------------------ + +/******************************************************************************* +* Notes: Horizontal edges (HEs) at scanline intersections (ie at the Top or * +* Bottom of a scanbeam) are processed as if layered. The order in which HEs * +* are processed doesn't matter. HEs intersect with other HE Bot.Xs only [#] * +* (or they could intersect with Top.Xs only, ie EITHER Bot.Xs OR Top.Xs), * +* and with other non-horizontal edges [*]. Once these intersections are * +* processed, intermediate HEs then 'promote' the Edge above (NextInLML) into * +* the AEL. These 'promoted' edges may in turn intersect [%] with other HEs. * +*******************************************************************************/ + +void Clipper::ProcessHorizontal(TEdge *horzEdge) +{ + Direction dir; + cInt horzLeft, horzRight; + bool IsOpen = (horzEdge->WindDelta == 0); + + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + TEdge* eLastHorz = horzEdge, *eMaxPair = 0; + while (eLastHorz->NextInLML && IsHorizontal(*eLastHorz->NextInLML)) + eLastHorz = eLastHorz->NextInLML; + if (!eLastHorz->NextInLML) + eMaxPair = GetMaximaPair(eLastHorz); + + MaximaList::const_iterator maxIt; + MaximaList::const_reverse_iterator maxRit; + if (m_Maxima.size() > 0) + { + //get the first maxima in range (X) ... + if (dir == dLeftToRight) + { + maxIt = m_Maxima.begin(); + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; + if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) + maxIt = m_Maxima.end(); + } + else + { + maxRit = m_Maxima.rbegin(); + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; + if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) + maxRit = m_Maxima.rend(); + } + } + + OutPt* op1 = 0; + + for (;;) //loop through consec. horizontal edges + { + + bool IsLastHorz = (horzEdge == eLastHorz); + TEdge* e = GetNextInAEL(horzEdge, dir); + while(e) + { + + //this code block inserts extra coords into horizontal edges (in output + //polygons) whereever maxima touch these horizontal edges. This helps + //'simplifying' polygons (ie if the Simplify property is set). + if (m_Maxima.size() > 0) + { + if (dir == dLeftToRight) + { + while (maxIt != m_Maxima.end() && *maxIt < e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); + maxIt++; + } + } + else + { + while (maxRit != m_Maxima.rend() && *maxRit > e->Curr.X) + { + if (horzEdge->OutIdx >= 0 && !IsOpen) + AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); + maxRit++; + } + } + }; + + if ((dir == dLeftToRight && e->Curr.X > horzRight) || + (dir == dRightToLeft && e->Curr.X < horzLeft)) break; + + //Also break if we've got to the end of an intermediate horizontal edge ... + //nb: Smaller Dx's are to the right of larger Dx's ABOVE the horizontal. + if (e->Curr.X == horzEdge->Top.X && horzEdge->NextInLML && + e->Dx < horzEdge->NextInLML->Dx) break; + + if (horzEdge->OutIdx >= 0 && !IsOpen) //note: may be done multiple times + { + op1 = AddOutPt(horzEdge, e->Curr); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Bot); + } + + //OK, so far we're still in range of the horizontal Edge but make sure + //we're at the last of consec. horizontals when matching with eMaxPair + if(e == eMaxPair && IsLastHorz) + { + if (horzEdge->OutIdx >= 0) + AddLocalMaxPoly(horzEdge, eMaxPair, horzEdge->Top); + DeleteFromAEL(horzEdge); + DeleteFromAEL(eMaxPair); + return; + } + + if(dir == dLeftToRight) + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges(horzEdge, e, Pt); + } + else + { + IntPoint Pt = IntPoint(e->Curr.X, horzEdge->Curr.Y); + IntersectEdges( e, horzEdge, Pt); + } + TEdge* eNext = GetNextInAEL(e, dir); + SwapPositionsInAEL( horzEdge, e ); + e = eNext; + } //end while(e) + + //Break out of loop if HorzEdge.NextInLML is not also horizontal ... + if (!horzEdge->NextInLML || !IsHorizontal(*horzEdge->NextInLML)) break; + + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Bot); + GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); + + } //end for (;;) + + if (horzEdge->OutIdx >= 0 && !op1) + { + op1 = GetLastOutPt(horzEdge); + TEdge* eNextHorz = m_SortedEdges; + while (eNextHorz) + { + if (eNextHorz->OutIdx >= 0 && + HorzSegmentsOverlap(horzEdge->Bot.X, + horzEdge->Top.X, eNextHorz->Bot.X, eNextHorz->Top.X)) + { + OutPt* op2 = GetLastOutPt(eNextHorz); + AddJoin(op2, op1, eNextHorz->Top); + } + eNextHorz = eNextHorz->NextInSEL; + } + AddGhostJoin(op1, horzEdge->Top); + } + + if (horzEdge->NextInLML) + { + if(horzEdge->OutIdx >= 0) + { + op1 = AddOutPt( horzEdge, horzEdge->Top); + UpdateEdgeIntoAEL(horzEdge); + if (horzEdge->WindDelta == 0) return; + //nb: HorzEdge is no longer horizontal here + TEdge* ePrev = horzEdge->PrevInAEL; + TEdge* eNext = horzEdge->NextInAEL; + if (ePrev && ePrev->Curr.X == horzEdge->Bot.X && + ePrev->Curr.Y == horzEdge->Bot.Y && ePrev->WindDelta != 0 && + (ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(*horzEdge, *ePrev, m_UseFullRange))) + { + OutPt* op2 = AddOutPt(ePrev, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + else if (eNext && eNext->Curr.X == horzEdge->Bot.X && + eNext->Curr.Y == horzEdge->Bot.Y && eNext->WindDelta != 0 && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(*horzEdge, *eNext, m_UseFullRange)) + { + OutPt* op2 = AddOutPt(eNext, horzEdge->Bot); + AddJoin(op1, op2, horzEdge->Top); + } + } + else + UpdateEdgeIntoAEL(horzEdge); + } + else + { + if (horzEdge->OutIdx >= 0) AddOutPt(horzEdge, horzEdge->Top); + DeleteFromAEL(horzEdge); + } +} +//------------------------------------------------------------------------------ + +bool Clipper::ProcessIntersections(const cInt topY) +{ + if( !m_ActiveEdges ) return true; + try { + BuildIntersectList(topY); + size_t IlSize = m_IntersectList.size(); + if (IlSize == 0) return true; + if (IlSize == 1 || FixupIntersectionOrder()) ProcessIntersectList(); + else return false; + } + catch(...) + { + m_SortedEdges = 0; + DisposeIntersectNodes(); + throw clipperException("ProcessIntersections error"); + } + m_SortedEdges = 0; + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DisposeIntersectNodes() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i ) + delete m_IntersectList[i]; + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +void Clipper::BuildIntersectList(const cInt topY) +{ + if ( !m_ActiveEdges ) return; + + //prepare for sorting ... + TEdge* e = m_ActiveEdges; + m_SortedEdges = e; + while( e ) + { + e->PrevInSEL = e->PrevInAEL; + e->NextInSEL = e->NextInAEL; + e->Curr.X = TopX( *e, topY ); + e = e->NextInAEL; + } + + //bubblesort ... + bool isModified; + do + { + isModified = false; + e = m_SortedEdges; + while( e->NextInSEL ) + { + TEdge *eNext = e->NextInSEL; + IntPoint Pt; + if(e->Curr.X > eNext->Curr.X) + { + IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); + IntersectNode * newNode = new IntersectNode; + newNode->Edge1 = e; + newNode->Edge2 = eNext; + newNode->Pt = Pt; + m_IntersectList.push_back(newNode); + + SwapPositionsInSEL(e, eNext); + isModified = true; + } + else + e = eNext; + } + if( e->PrevInSEL ) e->PrevInSEL->NextInSEL = 0; + else break; + } + while ( isModified ); + m_SortedEdges = 0; //important +} +//------------------------------------------------------------------------------ + + +void Clipper::ProcessIntersectList() +{ + for (size_t i = 0; i < m_IntersectList.size(); ++i) + { + IntersectNode* iNode = m_IntersectList[i]; + { + IntersectEdges( iNode->Edge1, iNode->Edge2, iNode->Pt); + SwapPositionsInAEL( iNode->Edge1 , iNode->Edge2 ); + } + delete iNode; + } + m_IntersectList.clear(); +} +//------------------------------------------------------------------------------ + +bool IntersectListSort(IntersectNode* node1, IntersectNode* node2) +{ + return node2->Pt.Y < node1->Pt.Y; +} +//------------------------------------------------------------------------------ + +inline bool EdgesAdjacent(const IntersectNode &inode) +{ + return (inode.Edge1->NextInSEL == inode.Edge2) || + (inode.Edge1->PrevInSEL == inode.Edge2); +} +//------------------------------------------------------------------------------ + +bool Clipper::FixupIntersectionOrder() +{ + //pre-condition: intersections are sorted Bottom-most first. + //Now it's crucial that intersections are made only between adjacent edges, + //so to ensure this the order of intersections may need adjusting ... + CopyAELToSEL(); + std::sort(m_IntersectList.begin(), m_IntersectList.end(), IntersectListSort); + size_t cnt = m_IntersectList.size(); + for (size_t i = 0; i < cnt; ++i) + { + if (!EdgesAdjacent(*m_IntersectList[i])) + { + size_t j = i + 1; + while (j < cnt && !EdgesAdjacent(*m_IntersectList[j])) j++; + if (j == cnt) return false; + std::swap(m_IntersectList[i], m_IntersectList[j]); + } + SwapPositionsInSEL(m_IntersectList[i]->Edge1, m_IntersectList[i]->Edge2); + } + return true; +} +//------------------------------------------------------------------------------ + +void Clipper::DoMaxima(TEdge *e) +{ + TEdge* eMaxPair = GetMaximaPairEx(e); + if (!eMaxPair) + { + if (e->OutIdx >= 0) + AddOutPt(e, e->Top); + DeleteFromAEL(e); + return; + } + + TEdge* eNext = e->NextInAEL; + while(eNext && eNext != eMaxPair) + { + IntersectEdges(e, eNext, e->Top); + SwapPositionsInAEL(e, eNext); + eNext = e->NextInAEL; + } + + if(e->OutIdx == Unassigned && eMaxPair->OutIdx == Unassigned) + { + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } + else if( e->OutIdx >= 0 && eMaxPair->OutIdx >= 0 ) + { + if (e->OutIdx >= 0) AddLocalMaxPoly(e, eMaxPair, e->Top); + DeleteFromAEL(e); + DeleteFromAEL(eMaxPair); + } +#ifdef use_lines + else if (e->WindDelta == 0) + { + if (e->OutIdx >= 0) + { + AddOutPt(e, e->Top); + e->OutIdx = Unassigned; + } + DeleteFromAEL(e); + + if (eMaxPair->OutIdx >= 0) + { + AddOutPt(eMaxPair, e->Top); + eMaxPair->OutIdx = Unassigned; + } + DeleteFromAEL(eMaxPair); + } +#endif + else throw clipperException("DoMaxima error"); +} +//------------------------------------------------------------------------------ + +void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) +{ + TEdge* e = m_ActiveEdges; + while( e ) + { + //1. process maxima, treating them as if they're 'bent' horizontal edges, + // but exclude maxima with horizontal edges. nb: e can't be a horizontal. + bool IsMaximaEdge = IsMaxima(e, topY); + + if(IsMaximaEdge) + { + TEdge* eMaxPair = GetMaximaPairEx(e); + IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); + } + + if(IsMaximaEdge) + { + if (m_StrictSimple) m_Maxima.push_back(e->Top.X); + TEdge* ePrev = e->PrevInAEL; + DoMaxima(e); + if( !ePrev ) e = m_ActiveEdges; + else e = ePrev->NextInAEL; + } + else + { + //2. promote horizontal edges, otherwise update Curr.X and Curr.Y ... + if (IsIntermediate(e, topY) && IsHorizontal(*e->NextInLML)) + { + UpdateEdgeIntoAEL(e); + if (e->OutIdx >= 0) + AddOutPt(e, e->Bot); + AddEdgeToSEL(e); + } + else + { + e->Curr.X = TopX( *e, topY ); + e->Curr.Y = topY; + } + + //When StrictlySimple and 'e' is being touched by another edge, then + //make sure both edges have a vertex here ... + if (m_StrictSimple) + { + TEdge* ePrev = e->PrevInAEL; + if ((e->OutIdx >= 0) && (e->WindDelta != 0) && ePrev && (ePrev->OutIdx >= 0) && + (ePrev->Curr.X == e->Curr.X) && (ePrev->WindDelta != 0)) + { + IntPoint pt = e->Curr; +#ifdef use_xyz + SetZ(pt, *ePrev, *e); +#endif + OutPt* op = AddOutPt(ePrev, pt); + OutPt* op2 = AddOutPt(e, pt); + AddJoin(op, op2, pt); //StrictlySimple (type-3) join + } + } + + e = e->NextInAEL; + } + } + + //3. Process horizontals at the Top of the scanbeam ... + m_Maxima.sort(); + ProcessHorizontals(); + m_Maxima.clear(); + + //4. Promote intermediate vertices ... + e = m_ActiveEdges; + while(e) + { + if(IsIntermediate(e, topY)) + { + OutPt* op = 0; + if( e->OutIdx >= 0 ) + op = AddOutPt(e, e->Top); + UpdateEdgeIntoAEL(e); + + //if output polygons share an edge, they'll need joining later ... + TEdge* ePrev = e->PrevInAEL; + TEdge* eNext = e->NextInAEL; + if (ePrev && ePrev->Curr.X == e->Bot.X && + ePrev->Curr.Y == e->Bot.Y && op && + ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && + (e->WindDelta != 0) && (ePrev->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(ePrev, e->Bot); + AddJoin(op, op2, e->Top); + } + else if (eNext && eNext->Curr.X == e->Bot.X && + eNext->Curr.Y == e->Bot.Y && op && + eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && + (e->WindDelta != 0) && (eNext->WindDelta != 0)) + { + OutPt* op2 = AddOutPt(eNext, e->Bot); + AddJoin(op, op2, e->Top); + } + } + e = e->NextInAEL; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolyline(OutRec &outrec) +{ + OutPt *pp = outrec.Pts; + OutPt *lastPP = pp->Prev; + while (pp != lastPP) + { + pp = pp->Next; + if (pp->Pt == pp->Prev->Pt) + { + if (pp == lastPP) lastPP = pp->Prev; + OutPt *tmpPP = pp->Prev; + tmpPP->Next = pp->Next; + pp->Next->Prev = tmpPP; + delete pp; + pp = tmpPP; + } + } + + if (pp == pp->Prev) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } +} +//------------------------------------------------------------------------------ + +void Clipper::FixupOutPolygon(OutRec &outrec) +{ + //FixupOutPolygon() - removes duplicate points and simplifies consecutive + //parallel edges by removing the middle vertex. + OutPt *lastOK = 0; + outrec.BottomPt = 0; + OutPt *pp = outrec.Pts; + bool preserveCol = m_PreserveCollinear || m_StrictSimple; + + for (;;) + { + if (pp->Prev == pp || pp->Prev == pp->Next) + { + DisposeOutPts(pp); + outrec.Pts = 0; + return; + } + + //test for duplicate points and collinear edges ... + if ((pp->Pt == pp->Next->Pt) || (pp->Pt == pp->Prev->Pt) || + (SlopesEqual(pp->Prev->Pt, pp->Pt, pp->Next->Pt, m_UseFullRange) && + (!preserveCol || !Pt2IsBetweenPt1AndPt3(pp->Prev->Pt, pp->Pt, pp->Next->Pt)))) + { + lastOK = 0; + OutPt *tmp = pp; + pp->Prev->Next = pp->Next; + pp->Next->Prev = pp->Prev; + pp = pp->Prev; + delete tmp; + } + else if (pp == lastOK) break; + else + { + if (!lastOK) lastOK = pp; + pp = pp->Next; + } + } + outrec.Pts = pp; +} +//------------------------------------------------------------------------------ + +int PointCount(OutPt *Pts) +{ + if (!Pts) return 0; + int result = 0; + OutPt* p = Pts; + do + { + result++; + p = p->Next; + } + while (p != Pts); + return result; +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult(Paths &polys) +{ + polys.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + if (!m_PolyOuts[i]->Pts) continue; + Path pg; + OutPt* p = m_PolyOuts[i]->Pts->Prev; + int cnt = PointCount(p); + if (cnt < 2) continue; + pg.reserve(cnt); + for (int i = 0; i < cnt; ++i) + { + pg.push_back(p->Pt); + p = p->Prev; + } + polys.push_back(pg); + } +} +//------------------------------------------------------------------------------ + +void Clipper::BuildResult2(PolyTree& polytree) +{ + polytree.Clear(); + polytree.AllNodes.reserve(m_PolyOuts.size()); + //add each output polygon/contour to polytree ... + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + int cnt = PointCount(outRec->Pts); + if ((outRec->IsOpen && cnt < 2) || (!outRec->IsOpen && cnt < 3)) continue; + FixHoleLinkage(*outRec); + PolyNode* pn = new PolyNode(); + //nb: polytree takes ownership of all the PolyNodes + polytree.AllNodes.push_back(pn); + outRec->PolyNd = pn; + pn->Parent = 0; + pn->Index = 0; + pn->Contour.reserve(cnt); + OutPt *op = outRec->Pts->Prev; + for (int j = 0; j < cnt; j++) + { + pn->Contour.push_back(op->Pt); + op = op->Prev; + } + } + + //fixup PolyNode links etc ... + polytree.Childs.reserve(m_PolyOuts.size()); + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); i++) + { + OutRec* outRec = m_PolyOuts[i]; + if (!outRec->PolyNd) continue; + if (outRec->IsOpen) + { + outRec->PolyNd->m_IsOpen = true; + polytree.AddChild(*outRec->PolyNd); + } + else if (outRec->FirstLeft && outRec->FirstLeft->PolyNd) + outRec->FirstLeft->PolyNd->AddChild(*outRec->PolyNd); + else + polytree.AddChild(*outRec->PolyNd); + } +} +//------------------------------------------------------------------------------ + +void SwapIntersectNodes(IntersectNode &int1, IntersectNode &int2) +{ + //just swap the contents (because fIntersectNodes is a single-linked-list) + IntersectNode inode = int1; //gets a copy of Int1 + int1.Edge1 = int2.Edge1; + int1.Edge2 = int2.Edge2; + int1.Pt = int2.Pt; + int2.Edge1 = inode.Edge1; + int2.Edge2 = inode.Edge2; + int2.Pt = inode.Pt; +} +//------------------------------------------------------------------------------ + +inline bool E2InsertsBeforeE1(TEdge &e1, TEdge &e2) +{ + if (e2.Curr.X == e1.Curr.X) + { + if (e2.Top.Y > e1.Top.Y) + return e2.Top.X < TopX(e1, e2.Top.Y); + else return e1.Top.X > TopX(e2, e1.Top.Y); + } + else return e2.Curr.X < e1.Curr.X; +} +//------------------------------------------------------------------------------ + +bool GetOverlap(const cInt a1, const cInt a2, const cInt b1, const cInt b2, + cInt& Left, cInt& Right) +{ + if (a1 < a2) + { + if (b1 < b2) {Left = std::max(a1,b1); Right = std::min(a2,b2);} + else {Left = std::max(a1,b2); Right = std::min(a2,b1);} + } + else + { + if (b1 < b2) {Left = std::max(a2,b1); Right = std::min(a1,b2);} + else {Left = std::max(a2,b2); Right = std::min(a1,b1);} + } + return Left < Right; +} +//------------------------------------------------------------------------------ + +inline void UpdateOutPtIdxs(OutRec& outrec) +{ + OutPt* op = outrec.Pts; + do + { + op->Idx = outrec.Idx; + op = op->Prev; + } + while(op != outrec.Pts); +} +//------------------------------------------------------------------------------ + +void Clipper::InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge) +{ + if(!m_ActiveEdges) + { + edge->PrevInAEL = 0; + edge->NextInAEL = 0; + m_ActiveEdges = edge; + } + else if(!startEdge && E2InsertsBeforeE1(*m_ActiveEdges, *edge)) + { + edge->PrevInAEL = 0; + edge->NextInAEL = m_ActiveEdges; + m_ActiveEdges->PrevInAEL = edge; + m_ActiveEdges = edge; + } + else + { + if(!startEdge) startEdge = m_ActiveEdges; + while(startEdge->NextInAEL && + !E2InsertsBeforeE1(*startEdge->NextInAEL , *edge)) + startEdge = startEdge->NextInAEL; + edge->NextInAEL = startEdge->NextInAEL; + if(startEdge->NextInAEL) startEdge->NextInAEL->PrevInAEL = edge; + edge->PrevInAEL = startEdge; + startEdge->NextInAEL = edge; + } +} +//---------------------------------------------------------------------- + +OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) +{ + OutPt* result = new OutPt; + result->Pt = outPt->Pt; + result->Idx = outPt->Idx; + if (InsertAfter) + { + result->Next = outPt->Next; + result->Prev = outPt; + outPt->Next->Prev = result; + outPt->Next = result; + } + else + { + result->Prev = outPt->Prev; + result->Next = outPt; + outPt->Prev->Next = result; + outPt->Prev = result; + } + return result; +} +//------------------------------------------------------------------------------ + +bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, + const IntPoint Pt, bool DiscardLeft) +{ + Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); + Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); + if (Dir1 == Dir2) return false; + + //When DiscardLeft, we want Op1b to be on the Left of Op1, otherwise we + //want Op1b to be on the Right. (And likewise with Op2 and Op2b.) + //So, to facilitate this while inserting Op1b and Op2b ... + //when DiscardLeft, make sure we're AT or RIGHT of Pt before adding Op1b, + //otherwise make sure we're AT or LEFT of Pt. (Likewise with Op2b.) + if (Dir1 == dLeftToRight) + { + while (op1->Next->Pt.X <= Pt.X && + op1->Next->Pt.X >= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, !DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, !DiscardLeft); + } + } + else + { + while (op1->Next->Pt.X >= Pt.X && + op1->Next->Pt.X <= op1->Pt.X && op1->Next->Pt.Y == Pt.Y) + op1 = op1->Next; + if (!DiscardLeft && (op1->Pt.X != Pt.X)) op1 = op1->Next; + op1b = DupOutPt(op1, DiscardLeft); + if (op1b->Pt != Pt) + { + op1 = op1b; + op1->Pt = Pt; + op1b = DupOutPt(op1, DiscardLeft); + } + } + + if (Dir2 == dLeftToRight) + { + while (op2->Next->Pt.X <= Pt.X && + op2->Next->Pt.X >= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, !DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, !DiscardLeft); + }; + } else + { + while (op2->Next->Pt.X >= Pt.X && + op2->Next->Pt.X <= op2->Pt.X && op2->Next->Pt.Y == Pt.Y) + op2 = op2->Next; + if (!DiscardLeft && (op2->Pt.X != Pt.X)) op2 = op2->Next; + op2b = DupOutPt(op2, DiscardLeft); + if (op2b->Pt != Pt) + { + op2 = op2b; + op2->Pt = Pt; + op2b = DupOutPt(op2, DiscardLeft); + }; + }; + + if ((Dir1 == dLeftToRight) == DiscardLeft) + { + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + } + else + { + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + } + return true; +} +//------------------------------------------------------------------------------ + +bool Clipper::JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2) +{ + OutPt *op1 = j->OutPt1, *op1b; + OutPt *op2 = j->OutPt2, *op2b; + + //There are 3 kinds of joins for output polygons ... + //1. Horizontal joins where Join.OutPt1 & Join.OutPt2 are vertices anywhere + //along (horizontal) collinear edges (& Join.OffPt is on the same horizontal). + //2. Non-horizontal joins where Join.OutPt1 & Join.OutPt2 are at the same + //location at the Bottom of the overlapping segment (& Join.OffPt is above). + //3. StrictSimple joins where edges touch but are not collinear and where + //Join.OutPt1, Join.OutPt2 & Join.OffPt all share the same point. + bool isHorizontal = (j->OutPt1->Pt.Y == j->OffPt.Y); + + if (isHorizontal && (j->OffPt == j->OutPt1->Pt) && + (j->OffPt == j->OutPt2->Pt)) + { + //Strictly Simple join ... + if (outRec1 != outRec2) return false; + op1b = j->OutPt1->Next; + while (op1b != op1 && (op1b->Pt == j->OffPt)) + op1b = op1b->Next; + bool reverse1 = (op1b->Pt.Y > j->OffPt.Y); + op2b = j->OutPt2->Next; + while (op2b != op2 && (op2b->Pt == j->OffPt)) + op2b = op2b->Next; + bool reverse2 = (op2b->Pt.Y > j->OffPt.Y); + if (reverse1 == reverse2) return false; + if (reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } + else if (isHorizontal) + { + //treat horizontal joins differently to non-horizontal joins since with + //them we're not yet sure where the overlapping is. OutPt1.Pt & OutPt2.Pt + //may be anywhere along the horizontal edge. + op1b = op1; + while (op1->Prev->Pt.Y == op1->Pt.Y && op1->Prev != op1b && op1->Prev != op2) + op1 = op1->Prev; + while (op1b->Next->Pt.Y == op1b->Pt.Y && op1b->Next != op1 && op1b->Next != op2) + op1b = op1b->Next; + if (op1b->Next == op1 || op1b->Next == op2) return false; //a flat 'polygon' + + op2b = op2; + while (op2->Prev->Pt.Y == op2->Pt.Y && op2->Prev != op2b && op2->Prev != op1b) + op2 = op2->Prev; + while (op2b->Next->Pt.Y == op2b->Pt.Y && op2b->Next != op2 && op2b->Next != op1) + op2b = op2b->Next; + if (op2b->Next == op2 || op2b->Next == op1) return false; //a flat 'polygon' + + cInt Left, Right; + //Op1 --> Op1b & Op2 --> Op2b are the extremites of the horizontal edges + if (!GetOverlap(op1->Pt.X, op1b->Pt.X, op2->Pt.X, op2b->Pt.X, Left, Right)) + return false; + + //DiscardLeftSide: when overlapping edges are joined, a spike will created + //which needs to be cleaned up. However, we don't want Op1 or Op2 caught up + //on the discard Side as either may still be needed for other joins ... + IntPoint Pt; + bool DiscardLeftSide; + if (op1->Pt.X >= Left && op1->Pt.X <= Right) + { + Pt = op1->Pt; DiscardLeftSide = (op1->Pt.X > op1b->Pt.X); + } + else if (op2->Pt.X >= Left&& op2->Pt.X <= Right) + { + Pt = op2->Pt; DiscardLeftSide = (op2->Pt.X > op2b->Pt.X); + } + else if (op1b->Pt.X >= Left && op1b->Pt.X <= Right) + { + Pt = op1b->Pt; DiscardLeftSide = op1b->Pt.X > op1->Pt.X; + } + else + { + Pt = op2b->Pt; DiscardLeftSide = (op2b->Pt.X > op2->Pt.X); + } + j->OutPt1 = op1; j->OutPt2 = op2; + return JoinHorz(op1, op1b, op2, op2b, Pt, DiscardLeftSide); + } else + { + //nb: For non-horizontal joins ... + // 1. Jr.OutPt1.Pt.Y == Jr.OutPt2.Pt.Y + // 2. Jr.OutPt1.Pt > Jr.OffPt.Y + + //make sure the polygons are correctly oriented ... + op1b = op1->Next; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Next; + bool Reverse1 = ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse1) + { + op1b = op1->Prev; + while ((op1b->Pt == op1->Pt) && (op1b != op1)) op1b = op1b->Prev; + if ((op1b->Pt.Y > op1->Pt.Y) || + !SlopesEqual(op1->Pt, op1b->Pt, j->OffPt, m_UseFullRange)) return false; + }; + op2b = op2->Next; + while ((op2b->Pt == op2->Pt) && (op2b != op2))op2b = op2b->Next; + bool Reverse2 = ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)); + if (Reverse2) + { + op2b = op2->Prev; + while ((op2b->Pt == op2->Pt) && (op2b != op2)) op2b = op2b->Prev; + if ((op2b->Pt.Y > op2->Pt.Y) || + !SlopesEqual(op2->Pt, op2b->Pt, j->OffPt, m_UseFullRange)) return false; + } + + if ((op1b == op1) || (op2b == op2) || (op1b == op2b) || + ((outRec1 == outRec2) && (Reverse1 == Reverse2))) return false; + + if (Reverse1) + { + op1b = DupOutPt(op1, false); + op2b = DupOutPt(op2, true); + op1->Prev = op2; + op2->Next = op1; + op1b->Next = op2b; + op2b->Prev = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } else + { + op1b = DupOutPt(op1, true); + op2b = DupOutPt(op2, false); + op1->Next = op2; + op2->Prev = op1; + op1b->Prev = op2b; + op2b->Next = op1b; + j->OutPt1 = op1; + j->OutPt2 = op1b; + return true; + } + } +} +//---------------------------------------------------------------------- + +static OutRec* ParseFirstLeft(OutRec* FirstLeft) +{ + while (FirstLeft && !FirstLeft->Pts) + FirstLeft = FirstLeft->FirstLeft; + return FirstLeft; +} +//------------------------------------------------------------------------------ + +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //tests if NewOutRec contains the polygon before reassigning FirstLeft + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && firstLeft == OldOutRec) + { + if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) + outRec->FirstLeft = NewOutRec; + } + } +} +//---------------------------------------------------------------------- + +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + + if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) + outRec->FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) + outRec->FirstLeft = OuterOutRec; + else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) + outRec->FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ + //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; + } +} +//---------------------------------------------------------------------- + +void Clipper::JoinCommonEdges() +{ + for (JoinList::size_type i = 0; i < m_Joins.size(); i++) + { + Join* join = m_Joins[i]; + + OutRec *outRec1 = GetOutRec(join->OutPt1->Idx); + OutRec *outRec2 = GetOutRec(join->OutPt2->Idx); + + if (!outRec1->Pts || !outRec2->Pts) continue; + if (outRec1->IsOpen || outRec2->IsOpen) continue; + + //get the polygon fragment with the correct hole state (FirstLeft) + //before calling JoinPoints() ... + OutRec *holeStateRec; + if (outRec1 == outRec2) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; + else holeStateRec = GetLowermostRec(outRec1, outRec2); + + if (!JoinPoints(join, outRec1, outRec2)) continue; + + if (outRec1 == outRec2) + { + //instead of joining two polygons, we've just created a new one by + //splitting one polygon into two. + outRec1->Pts = join->OutPt1; + outRec1->BottomPt = 0; + outRec2 = CreateOutRec(); + outRec2->Pts = join->OutPt2; + + //update all OutRec2.Pts Idx's ... + UpdateOutPtIdxs(*outRec2); + + if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) + { + //outRec1 contains outRec2 ... + outRec2->IsHole = !outRec1->IsHole; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + + if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) + ReversePolyPtLinks(outRec2->Pts); + + } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) + { + //outRec2 contains outRec1 ... + outRec2->IsHole = outRec1->IsHole; + outRec1->IsHole = !outRec2->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + outRec1->FirstLeft = outRec2; + + if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); + + if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) + ReversePolyPtLinks(outRec1->Pts); + } + else + { + //the 2 polygons are completely separate ... + outRec2->IsHole = outRec1->IsHole; + outRec2->FirstLeft = outRec1->FirstLeft; + + //fixup FirstLeft pointers that may need reassigning to OutRec2 + if (m_UsingPolyTree) FixupFirstLefts1(outRec1, outRec2); + } + + } else + { + //joined 2 polygons together ... + + outRec2->Pts = 0; + outRec2->BottomPt = 0; + outRec2->Idx = outRec1->Idx; + + outRec1->IsHole = holeStateRec->IsHole; + if (holeStateRec == outRec2) + outRec1->FirstLeft = outRec2->FirstLeft; + outRec2->FirstLeft = outRec1; + + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); + } + } +} + +//------------------------------------------------------------------------------ +// ClipperOffset support functions ... +//------------------------------------------------------------------------------ + +DoublePoint GetUnitNormal(const IntPoint &pt1, const IntPoint &pt2) +{ + if(pt2.X == pt1.X && pt2.Y == pt1.Y) + return DoublePoint(0, 0); + + double Dx = (double)(pt2.X - pt1.X); + double dy = (double)(pt2.Y - pt1.Y); + double f = 1 *1.0/ std::sqrt( Dx*Dx + dy*dy ); + Dx *= f; + dy *= f; + return DoublePoint(dy, -Dx); +} + +//------------------------------------------------------------------------------ +// ClipperOffset class +//------------------------------------------------------------------------------ + +ClipperOffset::ClipperOffset(double miterLimit, double arcTolerance) +{ + this->MiterLimit = miterLimit; + this->ArcTolerance = arcTolerance; + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +ClipperOffset::~ClipperOffset() +{ + Clear(); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Clear() +{ + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + delete m_polyNodes.Childs[i]; + m_polyNodes.Childs.clear(); + m_lowest.X = -1; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPath(const Path& path, JoinType joinType, EndType endType) +{ + int highI = (int)path.size() - 1; + if (highI < 0) return; + PolyNode* newNode = new PolyNode(); + newNode->m_jointype = joinType; + newNode->m_endtype = endType; + + //strip duplicate points from path and also get index to the lowest point ... + if (endType == etClosedLine || endType == etClosedPolygon) + while (highI > 0 && path[0] == path[highI]) highI--; + newNode->Contour.reserve(highI + 1); + newNode->Contour.push_back(path[0]); + int j = 0, k = 0; + for (int i = 1; i <= highI; i++) + if (newNode->Contour[j] != path[i]) + { + j++; + newNode->Contour.push_back(path[i]); + if (path[i].Y > newNode->Contour[k].Y || + (path[i].Y == newNode->Contour[k].Y && + path[i].X < newNode->Contour[k].X)) k = j; + } + if (endType == etClosedPolygon && j < 2) + { + delete newNode; + return; + } + m_polyNodes.AddChild(*newNode); + + //if this path's lowest pt is lower than all the others then update m_lowest + if (endType != etClosedPolygon) return; + if (m_lowest.X < 0) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + else + { + IntPoint ip = m_polyNodes.Childs[(int)m_lowest.X]->Contour[(int)m_lowest.Y]; + if (newNode->Contour[k].Y > ip.Y || + (newNode->Contour[k].Y == ip.Y && + newNode->Contour[k].X < ip.X)) + m_lowest = IntPoint(m_polyNodes.ChildCount() - 1, k); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::AddPaths(const Paths& paths, JoinType joinType, EndType endType) +{ + for (Paths::size_type i = 0; i < paths.size(); ++i) + AddPath(paths[i], joinType, endType); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::FixOrientations() +{ + //fixup orientations of all closed paths if the orientation of the + //closed path with the lowermost vertex is wrong ... + if (m_lowest.X >= 0 && + !Orientation(m_polyNodes.Childs[(int)m_lowest.X]->Contour)) + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon || + (node.m_endtype == etClosedLine && Orientation(node.Contour))) + ReversePath(node.Contour); + } + } else + { + for (int i = 0; i < m_polyNodes.ChildCount(); ++i) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedLine && !Orientation(node.Contour)) + ReversePath(node.Contour); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(Paths& solution, double delta) +{ + solution.clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + if (solution.size() > 0) solution.erase(solution.begin()); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::Execute(PolyTree& solution, double delta) +{ + solution.Clear(); + FixOrientations(); + DoOffset(delta); + + //now clean up 'corners' ... + Clipper clpr; + clpr.AddPaths(m_destPolys, ptSubject, true); + if (delta > 0) + { + clpr.Execute(ctUnion, solution, pftPositive, pftPositive); + } + else + { + IntRect r = clpr.GetBounds(); + Path outer(4); + outer[0] = IntPoint(r.left - 10, r.bottom + 10); + outer[1] = IntPoint(r.right + 10, r.bottom + 10); + outer[2] = IntPoint(r.right + 10, r.top - 10); + outer[3] = IntPoint(r.left - 10, r.top - 10); + + clpr.AddPath(outer, ptSubject, true); + clpr.ReverseSolution(true); + clpr.Execute(ctUnion, solution, pftNegative, pftNegative); + //remove the outer PolyNode rectangle ... + if (solution.ChildCount() == 1 && solution.Childs[0]->ChildCount() > 0) + { + PolyNode* outerNode = solution.Childs[0]; + solution.Childs.reserve(outerNode->ChildCount()); + solution.Childs[0] = outerNode->Childs[0]; + solution.Childs[0]->Parent = outerNode->Parent; + for (int i = 1; i < outerNode->ChildCount(); ++i) + solution.AddChild(*outerNode->Childs[i]); + } + else + solution.Clear(); + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoOffset(double delta) +{ + m_destPolys.clear(); + m_delta = delta; + + //if Zero offset, just copy any CLOSED polygons to m_p and return ... + if (NEAR_ZERO(delta)) + { + m_destPolys.reserve(m_polyNodes.ChildCount()); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + if (node.m_endtype == etClosedPolygon) + m_destPolys.push_back(node.Contour); + } + return; + } + + //see offset_triginometry3.svg in the documentation folder ... + if (MiterLimit > 2) m_miterLim = 2/(MiterLimit * MiterLimit); + else m_miterLim = 0.5; + + double y; + if (ArcTolerance <= 0.0) y = def_arc_tolerance; + else if (ArcTolerance > std::fabs(delta) * def_arc_tolerance) + y = std::fabs(delta) * def_arc_tolerance; + else y = ArcTolerance; + //see offset_triginometry2.svg in the documentation folder ... + double steps = pi / std::acos(1 - y / std::fabs(delta)); + if (steps > std::fabs(delta) * pi) + steps = std::fabs(delta) * pi; //ie excessive precision check + m_sin = std::sin(two_pi / steps); + m_cos = std::cos(two_pi / steps); + m_StepsPerRad = steps / two_pi; + if (delta < 0.0) m_sin = -m_sin; + + m_destPolys.reserve(m_polyNodes.ChildCount() * 2); + for (int i = 0; i < m_polyNodes.ChildCount(); i++) + { + PolyNode& node = *m_polyNodes.Childs[i]; + m_srcPoly = node.Contour; + + int len = (int)m_srcPoly.size(); + if (len == 0 || (delta <= 0 && (len < 3 || node.m_endtype != etClosedPolygon))) + continue; + + m_destPoly.clear(); + if (len == 1) + { + if (node.m_jointype == jtRound) + { + double X = 1.0, Y = 0.0; + for (cInt j = 1; j <= steps; j++) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + double X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + } + else + { + double X = -1.0, Y = -1.0; + for (int j = 0; j < 4; ++j) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[0].X + X * delta), + Round(m_srcPoly[0].Y + Y * delta))); + if (X < 0) X = 1; + else if (Y < 0) Y = 1; + else X = -1; + } + } + m_destPolys.push_back(m_destPoly); + continue; + } + //build m_normals ... + m_normals.clear(); + m_normals.reserve(len); + for (int j = 0; j < len - 1; ++j) + m_normals.push_back(GetUnitNormal(m_srcPoly[j], m_srcPoly[j + 1])); + if (node.m_endtype == etClosedLine || node.m_endtype == etClosedPolygon) + m_normals.push_back(GetUnitNormal(m_srcPoly[len - 1], m_srcPoly[0])); + else + m_normals.push_back(DoublePoint(m_normals[len - 2])); + + if (node.m_endtype == etClosedPolygon) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else if (node.m_endtype == etClosedLine) + { + int k = len - 1; + for (int j = 0; j < len; ++j) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + m_destPoly.clear(); + //re-build m_normals ... + DoublePoint n = m_normals[len -1]; + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-n.X, -n.Y); + k = 0; + for (int j = len - 1; j >= 0; j--) + OffsetPoint(j, k, node.m_jointype); + m_destPolys.push_back(m_destPoly); + } + else + { + int k = 0; + for (int j = 1; j < len - 1; ++j) + OffsetPoint(j, k, node.m_jointype); + + IntPoint pt1; + if (node.m_endtype == etOpenButt) + { + int j = len - 1; + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X + m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y + m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[j].X - m_normals[j].X * + delta), (cInt)Round(m_srcPoly[j].Y - m_normals[j].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + int j = len - 1; + k = len - 2; + m_sinA = 0; + m_normals[j] = DoublePoint(-m_normals[j].X, -m_normals[j].Y); + if (node.m_endtype == etOpenSquare) + DoSquare(j, k); + else + DoRound(j, k); + } + + //re-build m_normals ... + for (int j = len - 1; j > 0; j--) + m_normals[j] = DoublePoint(-m_normals[j - 1].X, -m_normals[j - 1].Y); + m_normals[0] = DoublePoint(-m_normals[1].X, -m_normals[1].Y); + + k = len - 1; + for (int j = k - 1; j > 0; --j) OffsetPoint(j, k, node.m_jointype); + + if (node.m_endtype == etOpenButt) + { + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X - m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y - m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + pt1 = IntPoint((cInt)Round(m_srcPoly[0].X + m_normals[0].X * delta), + (cInt)Round(m_srcPoly[0].Y + m_normals[0].Y * delta)); + m_destPoly.push_back(pt1); + } + else + { + k = 1; + m_sinA = 0; + if (node.m_endtype == etOpenSquare) + DoSquare(0, 1); + else + DoRound(0, 1); + } + m_destPolys.push_back(m_destPoly); + } + } +} +//------------------------------------------------------------------------------ + +void ClipperOffset::OffsetPoint(int j, int& k, JoinType jointype) +{ + //cross product ... + m_sinA = (m_normals[k].X * m_normals[j].Y - m_normals[j].X * m_normals[k].Y); + if (std::fabs(m_sinA * m_delta) < 1.0) + { + //dot product ... + double cosA = (m_normals[k].X * m_normals[j].X + m_normals[j].Y * m_normals[k].Y ); + if (cosA > 0) // angle => 0 degrees + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + return; + } + //else angle => 180 degrees + } + else if (m_sinA > 1.0) m_sinA = 1.0; + else if (m_sinA < -1.0) m_sinA = -1.0; + + if (m_sinA * m_delta < 0) + { + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[k].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[k].Y * m_delta))); + m_destPoly.push_back(m_srcPoly[j]); + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); + } + else + switch (jointype) + { + case jtMiter: + { + double r = 1 + (m_normals[j].X * m_normals[k].X + + m_normals[j].Y * m_normals[k].Y); + if (r >= m_miterLim) DoMiter(j, k, r); else DoSquare(j, k); + break; + } + case jtSquare: DoSquare(j, k); break; + case jtRound: DoRound(j, k); break; + } + k = j; +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoSquare(int j, int k) +{ + double dx = std::tan(std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y) / 4); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[k].X - m_normals[k].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[k].Y + m_normals[k].X * dx)))); + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_delta * (m_normals[j].X + m_normals[j].Y * dx)), + Round(m_srcPoly[j].Y + m_delta * (m_normals[j].Y - m_normals[j].X * dx)))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoMiter(int j, int k, double r) +{ + double q = m_delta / r; + m_destPoly.push_back(IntPoint(Round(m_srcPoly[j].X + (m_normals[k].X + m_normals[j].X) * q), + Round(m_srcPoly[j].Y + (m_normals[k].Y + m_normals[j].Y) * q))); +} +//------------------------------------------------------------------------------ + +void ClipperOffset::DoRound(int j, int k) +{ + double a = std::atan2(m_sinA, + m_normals[k].X * m_normals[j].X + m_normals[k].Y * m_normals[j].Y); + int steps = std::max((int)Round(m_StepsPerRad * std::fabs(a)), 1); + + double X = m_normals[k].X, Y = m_normals[k].Y, X2; + for (int i = 0; i < steps; ++i) + { + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + X * m_delta), + Round(m_srcPoly[j].Y + Y * m_delta))); + X2 = X; + X = X * m_cos - m_sin * Y; + Y = X2 * m_sin + Y * m_cos; + } + m_destPoly.push_back(IntPoint( + Round(m_srcPoly[j].X + m_normals[j].X * m_delta), + Round(m_srcPoly[j].Y + m_normals[j].Y * m_delta))); +} + +//------------------------------------------------------------------------------ +// Miscellaneous public functions +//------------------------------------------------------------------------------ + +void Clipper::DoSimplePolygons() +{ + PolyOutList::size_type i = 0; + while (i < m_PolyOuts.size()) + { + OutRec* outrec = m_PolyOuts[i++]; + OutPt* op = outrec->Pts; + if (!op || outrec->IsOpen) continue; + do //for each Pt in Polygon until duplicate found do ... + { + OutPt* op2 = op->Next; + while (op2 != outrec->Pts) + { + if ((op->Pt == op2->Pt) && op2->Next != op && op2->Prev != op) + { + //split the polygon into two ... + OutPt* op3 = op->Prev; + OutPt* op4 = op2->Prev; + op->Prev = op4; + op4->Next = op; + op2->Prev = op3; + op3->Next = op2; + + outrec->Pts = op; + OutRec* outrec2 = CreateOutRec(); + outrec2->Pts = op2; + UpdateOutPtIdxs(*outrec2); + if (Poly2ContainsPoly1(outrec2->Pts, outrec->Pts)) + { + //OutRec2 is contained by OutRec1 ... + outrec2->IsHole = !outrec->IsHole; + outrec2->FirstLeft = outrec; + if (m_UsingPolyTree) FixupFirstLefts2(outrec2, outrec); + } + else + if (Poly2ContainsPoly1(outrec->Pts, outrec2->Pts)) + { + //OutRec1 is contained by OutRec2 ... + outrec2->IsHole = outrec->IsHole; + outrec->IsHole = !outrec2->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + outrec->FirstLeft = outrec2; + if (m_UsingPolyTree) FixupFirstLefts2(outrec, outrec2); + } + else + { + //the 2 polygons are separate ... + outrec2->IsHole = outrec->IsHole; + outrec2->FirstLeft = outrec->FirstLeft; + if (m_UsingPolyTree) FixupFirstLefts1(outrec, outrec2); + } + op2 = op; //ie get ready for the Next iteration + } + op2 = op2->Next; + } + op = op->Next; + } + while (op != outrec->Pts); + } +} +//------------------------------------------------------------------------------ + +void ReversePath(Path& p) +{ + std::reverse(p.begin(), p.end()); +} +//------------------------------------------------------------------------------ + +void ReversePaths(Paths& p) +{ + for (Paths::size_type i = 0; i < p.size(); ++i) + ReversePath(p[i]); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPath(in_poly, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType) +{ + Clipper c; + c.StrictlySimple(true); + c.AddPaths(in_polys, ptSubject, true); + c.Execute(ctUnion, out_polys, fillType, fillType); +} +//------------------------------------------------------------------------------ + +void SimplifyPolygons(Paths &polys, PolyFillType fillType) +{ + SimplifyPolygons(polys, polys, fillType); +} +//------------------------------------------------------------------------------ + +inline double DistanceSqrd(const IntPoint& pt1, const IntPoint& pt2) +{ + double Dx = ((double)pt1.X - pt2.X); + double dy = ((double)pt1.Y - pt2.Y); + return (Dx*Dx + dy*dy); +} +//------------------------------------------------------------------------------ + +double DistanceFromLineSqrd( + const IntPoint& pt, const IntPoint& ln1, const IntPoint& ln2) +{ + //The equation of a line in general form (Ax + By + C = 0) + //given 2 points (x¹,y¹) & (x²,y²) is ... + //(y¹ - y²)x + (x² - x¹)y + (y² - y¹)x¹ - (x² - x¹)y¹ = 0 + //A = (y¹ - y²); B = (x² - x¹); C = (y² - y¹)x¹ - (x² - x¹)y¹ + //perpendicular distance of point (x³,y³) = (Ax³ + By³ + C)/Sqrt(A² + B²) + //see http://en.wikipedia.org/wiki/Perpendicular_distance + double A = double(ln1.Y - ln2.Y); + double B = double(ln2.X - ln1.X); + double C = A * ln1.X + B * ln1.Y; + C = A * pt.X + B * pt.Y - C; + return (C * C) / (A * A + B * B); +} +//--------------------------------------------------------------------------- + +bool SlopesNearCollinear(const IntPoint& pt1, + const IntPoint& pt2, const IntPoint& pt3, double distSqrd) +{ + //this function is more accurate when the point that's geometrically + //between the other 2 points is the one that's tested for distance. + //ie makes it more likely to pick up 'spikes' ... + if (Abs(pt1.X - pt2.X) > Abs(pt1.Y - pt2.Y)) + { + if ((pt1.X > pt2.X) == (pt1.X < pt3.X)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.X > pt1.X) == (pt2.X < pt3.X)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } + else + { + if ((pt1.Y > pt2.Y) == (pt1.Y < pt3.Y)) + return DistanceFromLineSqrd(pt1, pt2, pt3) < distSqrd; + else if ((pt2.Y > pt1.Y) == (pt2.Y < pt3.Y)) + return DistanceFromLineSqrd(pt2, pt1, pt3) < distSqrd; + else + return DistanceFromLineSqrd(pt3, pt1, pt2) < distSqrd; + } +} +//------------------------------------------------------------------------------ + +bool PointsAreClose(IntPoint pt1, IntPoint pt2, double distSqrd) +{ + double Dx = (double)pt1.X - pt2.X; + double dy = (double)pt1.Y - pt2.Y; + return ((Dx * Dx) + (dy * dy) <= distSqrd); +} +//------------------------------------------------------------------------------ + +OutPt* ExcludeOp(OutPt* op) +{ + OutPt* result = op->Prev; + result->Next = op->Next; + op->Next->Prev = result; + result->Idx = 0; + return result; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance) +{ + //distance = proximity in units/pixels below which vertices + //will be stripped. Default ~= sqrt(2). + + size_t size = in_poly.size(); + + if (size == 0) + { + out_poly.clear(); + return; + } + + OutPt* outPts = new OutPt[size]; + for (size_t i = 0; i < size; ++i) + { + outPts[i].Pt = in_poly[i]; + outPts[i].Next = &outPts[(i + 1) % size]; + outPts[i].Next->Prev = &outPts[i]; + outPts[i].Idx = 0; + } + + double distSqrd = distance * distance; + OutPt* op = &outPts[0]; + while (op->Idx == 0 && op->Next != op->Prev) + { + if (PointsAreClose(op->Pt, op->Prev->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else if (PointsAreClose(op->Prev->Pt, op->Next->Pt, distSqrd)) + { + ExcludeOp(op->Next); + op = ExcludeOp(op); + size -= 2; + } + else if (SlopesNearCollinear(op->Prev->Pt, op->Pt, op->Next->Pt, distSqrd)) + { + op = ExcludeOp(op); + size--; + } + else + { + op->Idx = 1; + op = op->Next; + } + } + + if (size < 3) size = 0; + out_poly.resize(size); + for (size_t i = 0; i < size; ++i) + { + out_poly[i] = op->Pt; + op = op->Next; + } + delete [] outPts; +} +//------------------------------------------------------------------------------ + +void CleanPolygon(Path& poly, double distance) +{ + CleanPolygon(poly, poly, distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) +{ + out_polys.resize(in_polys.size()); + for (Paths::size_type i = 0; i < in_polys.size(); ++i) + CleanPolygon(in_polys[i], out_polys[i], distance); +} +//------------------------------------------------------------------------------ + +void CleanPolygons(Paths& polys, double distance) +{ + CleanPolygons(polys, polys, distance); +} +//------------------------------------------------------------------------------ + +void Minkowski(const Path& poly, const Path& path, + Paths& solution, bool isSum, bool isClosed) +{ + int delta = (isClosed ? 1 : 0); + size_t polyCnt = poly.size(); + size_t pathCnt = path.size(); + Paths pp; + pp.reserve(pathCnt); + if (isSum) + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X + poly[j].X, path[i].Y + poly[j].Y)); + pp.push_back(p); + } + else + for (size_t i = 0; i < pathCnt; ++i) + { + Path p; + p.reserve(polyCnt); + for (size_t j = 0; j < poly.size(); ++j) + p.push_back(IntPoint(path[i].X - poly[j].X, path[i].Y - poly[j].Y)); + pp.push_back(p); + } + + solution.clear(); + solution.reserve((pathCnt + delta) * (polyCnt + 1)); + for (size_t i = 0; i < pathCnt - 1 + delta; ++i) + for (size_t j = 0; j < polyCnt; ++j) + { + Path quad; + quad.reserve(4); + quad.push_back(pp[i % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][j % polyCnt]); + quad.push_back(pp[(i + 1) % pathCnt][(j + 1) % polyCnt]); + quad.push_back(pp[i % pathCnt][(j + 1) % polyCnt]); + if (!Orientation(quad)) ReversePath(quad); + solution.push_back(quad); + } +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed) +{ + Minkowski(pattern, path, solution, true, pathIsClosed); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void TranslatePath(const Path& input, Path& output, const IntPoint delta) +{ + //precondition: input != output + output.resize(input.size()); + for (size_t i = 0; i < input.size(); ++i) + output[i] = IntPoint(input[i].X + delta.X, input[i].Y + delta.Y); +} +//------------------------------------------------------------------------------ + +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed) +{ + Clipper c; + for (size_t i = 0; i < paths.size(); ++i) + { + Paths tmp; + Minkowski(pattern, paths[i], tmp, true, pathIsClosed); + c.AddPaths(tmp, ptSubject, true); + if (pathIsClosed) + { + Path tmp2; + TranslatePath(paths[i], tmp2, pattern[0]); + c.AddPath(tmp2, ptClip, true); + } + } + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution) +{ + Minkowski(poly1, poly2, solution, false, true); + Clipper c; + c.AddPaths(solution, ptSubject, true); + c.Execute(ctUnion, solution, pftNonZero, pftNonZero); +} +//------------------------------------------------------------------------------ + +enum NodeType {ntAny, ntOpen, ntClosed}; + +void AddPolyNodeToPaths(const PolyNode& polynode, NodeType nodetype, Paths& paths) +{ + bool match = true; + if (nodetype == ntClosed) match = !polynode.IsOpen(); + else if (nodetype == ntOpen) return; + + if (!polynode.Contour.empty() && match) + paths.push_back(polynode.Contour); + for (int i = 0; i < polynode.ChildCount(); ++i) + AddPolyNodeToPaths(*polynode.Childs[i], nodetype, paths); +} +//------------------------------------------------------------------------------ + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntAny, paths); +} +//------------------------------------------------------------------------------ + +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + AddPolyNodeToPaths(polytree, ntClosed, paths); +} +//------------------------------------------------------------------------------ + +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths) +{ + paths.resize(0); + paths.reserve(polytree.Total()); + //Open paths are top level only, so ... + for (int i = 0; i < polytree.ChildCount(); ++i) + if (polytree.Childs[i]->IsOpen()) + paths.push_back(polytree.Childs[i]->Contour); +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const IntPoint &p) +{ + s << "(" << p.X << "," << p.Y << ")"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Path &p) +{ + if (p.empty()) return s; + Path::size_type last = p.size() -1; + for (Path::size_type i = 0; i < last; i++) + s << "(" << p[i].X << "," << p[i].Y << "), "; + s << "(" << p[last].X << "," << p[last].Y << ")\n"; + return s; +} +//------------------------------------------------------------------------------ + +std::ostream& operator <<(std::ostream &s, const Paths &p) +{ + for (Paths::size_type i = 0; i < p.size(); i++) + s << p[i]; + s << "\n"; + return s; +} +//------------------------------------------------------------------------------ + +} //QtClipperLib namespace diff --git a/src/3rdparty/clipper/clipper.h b/src/3rdparty/clipper/clipper.h new file mode 100644 index 0000000..78013af --- /dev/null +++ b/src/3rdparty/clipper/clipper.h @@ -0,0 +1,404 @@ +/******************************************************************************* +* * +* Author : Angus Johnson * +* Version : 6.4.0 * +* Date : 2 July 2015 * +* Website : http://www.angusj.com * +* Copyright : Angus Johnson 2010-2015 * +* * +* License: * +* Use, modification & distribution is subject to Boost Software License Ver 1. * +* http://www.boost.org/LICENSE_1_0.txt * +* * +* Attributions: * +* The code in this library is an extension of Bala Vatti's clipping algorithm: * +* "A generic solution to polygon clipping" * +* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * +* http://portal.acm.org/citation.cfm?id=129906 * +* * +* Computer graphics and geometric modeling: implementation and algorithms * +* By Max K. Agoston * +* Springer; 1 edition (January 4, 2005) * +* http://books.google.com/books?q=vatti+clipping+agoston * +* * +* See also: * +* "Polygon Offsetting by Computing Winding Numbers" * +* Paper no. DETC2005-85513 pp. 565-575 * +* ASME 2005 International Design Engineering Technical Conferences * +* and Computers and Information in Engineering Conference (IDETC/CIE2005) * +* September 24-28, 2005 , Long Beach, California, USA * +* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * +* * +*******************************************************************************/ + +#ifndef clipper_hpp +#define clipper_hpp + +#define CLIPPER_VERSION "6.2.6" + +//use_int32: When enabled 32bit ints are used instead of 64bit ints. This +//improve performance but coordinate values are limited to the range +/- 46340 +//#define use_int32 + +//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. +//#define use_xyz + +//use_lines: Enables line clipping. Adds a very minor cost to performance. +#define use_lines + +//use_deprecated: Enables temporary support for the obsolete functions +//#define use_deprecated + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace QtClipperLib { + +enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; +enum PolyType { ptSubject, ptClip }; +//By far the most widely used winding rules for polygon filling are +//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) +//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) +//see http://glprogramming.com/red/chapter11.html +enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; + +#ifdef use_int32 + typedef int cInt; + static cInt const loRange = 0x7FFF; + static cInt const hiRange = 0x7FFF; +#else + typedef signed long long cInt; + static cInt const loRange = 0x3FFFFFFF; + static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; + typedef signed long long long64; //used by Int128 class + typedef unsigned long long ulong64; + +#endif + +struct IntPoint { + cInt X; + cInt Y; +#ifdef use_xyz + cInt Z; + IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; +#else + IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; +#endif + + friend inline bool operator== (const IntPoint& a, const IntPoint& b) + { + return a.X == b.X && a.Y == b.Y; + } + friend inline bool operator!= (const IntPoint& a, const IntPoint& b) + { + return a.X != b.X || a.Y != b.Y; + } +}; +//------------------------------------------------------------------------------ + +typedef std::vector< IntPoint > Path; +typedef std::vector< Path > Paths; + +inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} +inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} + +std::ostream& operator <<(std::ostream &s, const IntPoint &p); +std::ostream& operator <<(std::ostream &s, const Path &p); +std::ostream& operator <<(std::ostream &s, const Paths &p); + +struct DoublePoint +{ + double X; + double Y; + DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} + DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} +}; +//------------------------------------------------------------------------------ + +#ifdef use_xyz +typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); +#endif + +enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; +enum JoinType {jtSquare, jtRound, jtMiter}; +enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound}; + +class PolyNode; +typedef std::vector< PolyNode* > PolyNodes; + +class PolyNode +{ +public: + PolyNode(); + virtual ~PolyNode(){}; + Path Contour; + PolyNodes Childs; + PolyNode* Parent; + PolyNode* GetNext() const; + bool IsHole() const; + bool IsOpen() const; + int ChildCount() const; +private: + unsigned Index; //node index in Parent.Childs + bool m_IsOpen; + JoinType m_jointype; + EndType m_endtype; + PolyNode* GetNextSiblingUp() const; + void AddChild(PolyNode& child); + friend class Clipper; //to access Index + friend class ClipperOffset; +}; + +class PolyTree: public PolyNode +{ +public: + ~PolyTree(){Clear();}; + PolyNode* GetFirst() const; + void Clear(); + int Total() const; +private: + PolyNodes AllNodes; + friend class Clipper; //to access AllNodes +}; + +bool Orientation(const Path &poly); +double Area(const Path &poly); +int PointInPolygon(const IntPoint &pt, const Path &path); + +void SimplifyPolygon(const Path &in_poly, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(const Paths &in_polys, Paths &out_polys, PolyFillType fillType = pftEvenOdd); +void SimplifyPolygons(Paths &polys, PolyFillType fillType = pftEvenOdd); + +void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); +void CleanPolygon(Path& poly, double distance = 1.415); +void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); +void CleanPolygons(Paths& polys, double distance = 1.415); + +void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed); +void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed); +void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution); + +void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); +void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); +void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); + +void ReversePath(Path& p); +void ReversePaths(Paths& p); + +struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; + +//enums that are used internally ... +enum EdgeSide { esLeft = 1, esRight = 2}; + +//forward declarations (for stuff used internally) ... +struct TEdge; +struct IntersectNode; +struct LocalMinimum; +struct OutPt; +struct OutRec; +struct Join; + +typedef std::vector < OutRec* > PolyOutList; +typedef std::vector < TEdge* > EdgeList; +typedef std::vector < Join* > JoinList; +typedef std::vector < IntersectNode* > IntersectList; + +//------------------------------------------------------------------------------ + +//ClipperBase is the ancestor to the Clipper class. It should not be +//instantiated directly. This class simply abstracts the conversion of sets of +//polygon coordinates into edge objects that are stored in a LocalMinima list. +class ClipperBase +{ +public: + ClipperBase(); + virtual ~ClipperBase(); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); + virtual void Clear(); + IntRect GetBounds(); + bool PreserveCollinear() {return m_PreserveCollinear;}; + void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; +protected: + void DisposeLocalMinimaList(); + TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); + virtual void Reset(); + TEdge* ProcessBound(TEdge* E, bool IsClockwise); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec* CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); + + typedef std::vector MinimaList; + MinimaList::iterator m_CurrentLM; + MinimaList m_MinimaList; + + bool m_UseFullRange; + EdgeList m_edges; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; +}; +//------------------------------------------------------------------------------ + +class Clipper : public virtual ClipperBase +{ +public: + Clipper(int initOptions = 0); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + Paths &solution, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType fillType = pftEvenOdd); + bool Execute(ClipType clipType, + PolyTree &polytree, + PolyFillType subjFillType, + PolyFillType clipFillType); + bool ReverseSolution() { return m_ReverseOutput; }; + void ReverseSolution(bool value) {m_ReverseOutput = value;}; + bool StrictlySimple() {return m_StrictSimple;}; + void StrictlySimple(bool value) {m_StrictSimple = value;}; + //set the callback function for z value filling on intersections (otherwise Z is 0) +#ifdef use_xyz + void ZFillFunction(ZFillCallback zFillFunc); +#endif +protected: + virtual bool ExecuteInternal(); +private: + JoinList m_Joins; + JoinList m_GhostJoins; + IntersectList m_IntersectList; + ClipType m_ClipType; + typedef std::list MaximaList; + MaximaList m_Maxima; + TEdge *m_SortedEdges; + bool m_ExecuteLocked; + PolyFillType m_ClipFillType; + PolyFillType m_SubjFillType; + bool m_ReverseOutput; + bool m_UsingPolyTree; + bool m_StrictSimple; +#ifdef use_xyz + ZFillCallback m_ZFill; //custom callback +#endif + void SetWindingCount(TEdge& edge); + bool IsEvenOddFillType(const TEdge& edge) const; + bool IsEvenOddAltFillType(const TEdge& edge) const; + void InsertLocalMinimaIntoAEL(const cInt botY); + void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); + void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); + void CopyAELToSEL(); + void DeleteFromSEL(TEdge *e); + void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); + bool IsContributing(const TEdge& edge) const; + bool IsTopHorz(const cInt XPos); + void DoMaxima(TEdge *e); + void ProcessHorizontals(); + void ProcessHorizontal(TEdge *horzEdge); + void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); + OutRec* GetOutRec(int idx); + void AppendPolygon(TEdge *e1, TEdge *e2); + void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); + OutPt* AddOutPt(TEdge *e, const IntPoint &pt); + OutPt* GetLastOutPt(TEdge *e); + bool ProcessIntersections(const cInt topY); + void BuildIntersectList(const cInt topY); + void ProcessIntersectList(); + void ProcessEdgesAtTopOfScanbeam(const cInt topY); + void BuildResult(Paths& polys); + void BuildResult2(PolyTree& polytree); + void SetHoleState(TEdge *e, OutRec *outrec); + void DisposeIntersectNodes(); + bool FixupIntersectionOrder(); + void FixupOutPolygon(OutRec &outrec); + void FixupOutPolyline(OutRec &outrec); + bool IsHole(TEdge *e); + bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); + void FixHoleLinkage(OutRec &outrec); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); + void ClearJoins(); + void ClearGhostJoins(); + void AddGhostJoin(OutPt *op, const IntPoint offPt); + bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); + void JoinCommonEdges(); + void DoSimplePolygons(); + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); +#ifdef use_xyz + void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); +#endif +}; +//------------------------------------------------------------------------------ + +class ClipperOffset +{ +public: + ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); + ~ClipperOffset(); + void AddPath(const Path& path, JoinType joinType, EndType endType); + void AddPaths(const Paths& paths, JoinType joinType, EndType endType); + void Execute(Paths& solution, double delta); + void Execute(PolyTree& solution, double delta); + void Clear(); + double MiterLimit; + double ArcTolerance; +private: + Paths m_destPolys; + Path m_srcPoly; + Path m_destPoly; + std::vector m_normals; + double m_delta, m_sinA, m_sin, m_cos; + double m_miterLim, m_StepsPerRad; + IntPoint m_lowest; + PolyNode m_polyNodes; + + void FixOrientations(); + void DoOffset(double delta); + void OffsetPoint(int j, int& k, JoinType jointype); + void DoSquare(int j, int k); + void DoMiter(int j, int k, double r); + void DoRound(int j, int k); +}; +//------------------------------------------------------------------------------ + +class clipperException : public std::exception +{ + public: + clipperException(const char* description): m_descr(description) {} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} + private: + std::string m_descr; +}; +//------------------------------------------------------------------------------ + +} //QtClipperLib namespace + +#endif //clipper_hpp + + diff --git a/src/3rdparty/clipper/clipper.pro b/src/3rdparty/clipper/clipper.pro new file mode 100644 index 0000000..874d55c --- /dev/null +++ b/src/3rdparty/clipper/clipper.pro @@ -0,0 +1,16 @@ +TARGET = clipper + +CONFIG += staticlib exceptions warn_off optimize_full + +load(qt_helper_lib) + +# workaround for QTBUG-31586 +contains(QT_CONFIG, c++11): CONFIG += c++11 + +gcc { + QMAKE_CFLAGS_OPTIMIZE_FULL += -ffast-math + !clang:!intel_icc:!rim_qcc: QMAKE_CXXFLAGS_WARN_ON += -Wno-error=return-type +} + +HEADERS += clipper.h +SOURCES += clipper.cpp diff --git a/src/3rdparty/clipper/qt_attribution.json b/src/3rdparty/clipper/qt_attribution.json new file mode 100644 index 0000000..4f6a232 --- /dev/null +++ b/src/3rdparty/clipper/qt_attribution.json @@ -0,0 +1,13 @@ +{ + "Id": "clipper", + "Name": "Clipper Polygon Clipping Library", + "QDocModule": "qtlocation", + "QtUsage": "Used in the QML plugin of Qt Location.", + "Description": "The Clipper library performs line & polygon clipping - intersection, union, difference & exclusive-or, and line & polygon offsetting.", + "Homepage": "http://www.angusj.com/delphi/clipper.php", + "Version": "6.4.0", + "LicenseId": "BSL-1.0", + "License": "Boost Software License 1.0", + "LicenseFile": "LICENSE", + "Copyright": "Copyright Angus Johnson 2010-2015" +} diff --git a/src/3rdparty/earcut/LICENSE b/src/3rdparty/earcut/LICENSE new file mode 100644 index 0000000..8bafb57 --- /dev/null +++ b/src/3rdparty/earcut/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/src/3rdparty/earcut/earcut.hpp b/src/3rdparty/earcut/earcut.hpp new file mode 100644 index 0000000..ba3fb17 --- /dev/null +++ b/src/3rdparty/earcut/earcut.hpp @@ -0,0 +1,779 @@ +#pragma once +#ifndef EARCUT_HPP +#define EARCUT_HPP + +#include +#include +#include +#include +#include +#include + +namespace qt_mapbox { + +namespace util { + +template struct nth { + + inline static typename std::tuple_element::type + get(const T& t) { return std::get(t); } +}; + + +} + +namespace detail { + +template +class Earcut { +public: + std::vector indices; + N vertices = 0; + + template + void operator()(const Polygon& points); + +private: + struct Node { + Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node(Node&&) = delete; + Node& operator=(Node&&) = delete; + + const N i; + const double x; + const double y; + + // previous and next vertice nodes in a polygon ring + Node* prev = nullptr; + Node* next = nullptr; + + // z-order curve value + int32_t z = 0; + + // previous and next nodes in z-order + Node* prevZ = nullptr; + Node* nextZ = nullptr; + + // indicates whether this is a steiner point + bool steiner = false; + }; + + template Node* linkedList(const Ring& points, const bool clockwise); + Node* filterPoints(Node* start, Node* end = nullptr); + void earcutLinked(Node* ear, int pass = 0); + bool isEar(Node* ear); + bool isEarHashed(Node* ear); + Node* cureLocalIntersections(Node* start); + void splitEarcut(Node* start); + template Node* eliminateHoles(const Polygon& points, Node* outerNode); + void eliminateHole(Node* hole, Node* outerNode); + Node* findHoleBridge(Node* hole, Node* outerNode); + void indexCurve(Node* start); + Node* sortLinked(Node* list); + int32_t zOrder(const double x_, const double y_); + Node* getLeftmost(Node* start); + bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; + bool isValidDiagonal(Node* a, Node* b); + double area(const Node* p, const Node* q, const Node* r) const; + bool equals(const Node* p1, const Node* p2); + bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); + bool intersectsPolygon(const Node* a, const Node* b); + bool locallyInside(const Node* a, const Node* b); + bool middleInside(const Node* a, const Node* b); + Node* splitPolygon(Node* a, Node* b); + template Node* insertNode(N i, const Point& p, Node* last); + void removeNode(Node* p); + + bool hashing; + double minX, maxX; + double minY, maxY; + double size; + + template > + class ObjectPool { + public: + ObjectPool() { } + ObjectPool(std::size_t blockSize_) { + reset(blockSize_); + } + ~ObjectPool() { + clear(); + } + template + T* construct(Args&&... args) { + if (currentIndex >= blockSize) { + currentBlock = alloc.allocate(blockSize); + allocations.emplace_back(currentBlock); + currentIndex = 0; + } + T* object = ¤tBlock[currentIndex++]; + alloc.construct(object, std::forward(args)...); + return object; + } + void reset(std::size_t newBlockSize) { + for (auto allocation : allocations) alloc.deallocate(allocation, blockSize); + allocations.clear(); + blockSize = std::max(1, newBlockSize); + currentBlock = nullptr; + currentIndex = blockSize; + } + void clear() { reset(blockSize); } + private: + T* currentBlock = nullptr; + std::size_t currentIndex = 1; + std::size_t blockSize = 1; + std::vector allocations; + Alloc alloc; + }; + ObjectPool nodes; +}; + +template template +void Earcut::operator()(const Polygon& points) { + // reset + indices.clear(); + vertices = 0; + + if (points.empty()) return; + + double x; + double y; + size = 0; + int threshold = 80; + std::size_t len = 0; + + for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { + threshold -= static_cast(points[i].size()); + len += points[i].size(); + } + + //estimate size of nodes and indices + nodes.reset(len * 3 / 2); + indices.reserve(len + points[0].size()); + + Node* outerNode = linkedList(points[0], true); + if (!outerNode) return; + + if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + hashing = threshold < 0; + if (hashing) { + Node* p = outerNode->next; + minX = maxX = p->x; + minY = maxY = p->y; + do { + x = p->x; + y = p->y; + minX = (std::min)(minX, x); + minY = (std::min)(minY, y); + maxX = (std::max)(maxX, x); + maxY = (std::max)(maxY, y); + p = p->next; + } while (p != outerNode); + + // minX, minY and size are later used to transform coords into integers for z-order calculation + size = (std::max)(maxX - minX, maxY - minY); + } + + earcutLinked(outerNode); + + nodes.clear(); +} + +// create a circular doubly linked list from polygon points in the specified winding order +template template +typename Earcut::Node* +Earcut::linkedList(const Ring& points, const bool clockwise) { + using Point = typename Ring::value_type; + double sum = 0; + const int len = static_cast(points.size()); + int i, j; + Point p1, p2; + Node* last = nullptr; + + // calculate original winding order of a polygon ring + for (i = 0, j = len - 1; i < len; j = i++) { + p1 = points[i]; + p2 = points[j]; + const double p20 = util::nth<0, Point>::get(p2); + const double p10 = util::nth<0, Point>::get(p1); + const double p11 = util::nth<1, Point>::get(p1); + const double p21 = util::nth<1, Point>::get(p2); + sum += (p20 - p10) * (p11 + p21); + } + + // link points into circular doubly-linked list in the specified winding order + if (clockwise == (sum > 0)) { + for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); + } else { + for (i = len - 1; i >= 0; i--) last = insertNode(vertices + i, points[i], last); + } + + if (last && equals(last, last->next)) { + removeNode(last); + last = last->next; + } + + vertices += len; + + return last; +} + +// eliminate colinear or duplicate points +template +typename Earcut::Node* +Earcut::filterPoints(Node* start, Node* end) { + if (!end) end = start; + + Node* p = start; + bool again; + do { + again = false; + + if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { + removeNode(p); + p = end = p->prev; + + if (p == p->next) return nullptr; + again = true; + + } else { + p = p->next; + } + } while (again || p != end); + + return end; +} + +// main ear slicing loop which triangulates a polygon (given as a linked list) +template +void Earcut::earcutLinked(Node* ear, int pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && hashing) indexCurve(ear); + + Node* stop = ear; + Node* prev; + Node* next; + + int iterations = 0; + + // iterate through ears, slicing them one by one + while (ear->prev != ear->next) { + iterations++; + prev = ear->prev; + next = ear->next; + + if (hashing ? isEarHashed(ear) : isEar(ear)) { + // cut off the triangle + indices.emplace_back(prev->i); + indices.emplace_back(ear->i); + indices.emplace_back(next->i); + + removeNode(ear); + + // skipping the next vertice leads to less sliver triangles + ear = next->next; + stop = next->next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear == stop) { + // try filtering points and slicing again + if (!pass) earcutLinked(filterPoints(ear), 1); + + // if this didn't work, try curing all small self-intersections locally + else if (pass == 1) { + ear = cureLocalIntersections(ear); + earcutLinked(ear, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass == 2) splitEarcut(ear); + + break; + } + } +} + +// check whether a polygon node forms a valid ear with adjacent nodes +template +bool Earcut::isEar(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + Node* p = ear->next->next; + + while (p != ear->prev) { + if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->next; + } + + return true; +} + +template +bool Earcut::isEarHashed(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // triangle bbox; min & max are calculated like this for speed + const double minTX = (std::min)(a->x, (std::min)(b->x, c->x)); + const double minTY = (std::min)(a->y, (std::min)(b->y, c->y)); + const double maxTX = (std::max)(a->x, (std::max)(b->x, c->x)); + const double maxTY = (std::max)(a->y, (std::max)(b->y, c->y)); + + // z-order range for the current triangle bbox; + const int32_t minZ = zOrder(minTX, minTY); + const int32_t maxZ = zOrder(maxTX, maxTY); + + // first look for points inside the triangle in increasing z-order + Node* p = ear->nextZ; + + while (p && p->z <= maxZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->nextZ; + } + + // then look for points in decreasing z-order + p = ear->prevZ; + + while (p && p->z >= minZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->prevZ; + } + + return true; +} + +// go through all polygon nodes and cure small local self-intersections +template +typename Earcut::Node* +Earcut::cureLocalIntersections(Node* start) { + Node* p = start; + do { + Node* a = p->prev; + Node* b = p->next->next; + + // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) + if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { + indices.emplace_back(a->i); + indices.emplace_back(p->i); + indices.emplace_back(b->i); + + // remove two nodes involved + removeNode(p); + removeNode(p->next); + + p = start = b; + } + p = p->next; + } while (p != start); + + return p; +} + +// try splitting polygon into two and triangulate them independently +template +void Earcut::splitEarcut(Node* start) { + // look for a valid diagonal that divides the polygon into two + Node* a = start; + do { + Node* b = a->next->next; + while (b != a->prev) { + if (a->i != b->i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + Node* c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a->next); + c = filterPoints(c, c->next); + + // run earcut on each half + earcutLinked(a); + earcutLinked(c); + return; + } + b = b->next; + } + a = a->next; + } while (a != start); +} + +// link every hole into the outer loop, producing a single-ring polygon without holes +template template +typename Earcut::Node* +Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { + const size_t len = points.size(); + + std::vector queue; + for (size_t i = 1; i < len; i++) { + Node* list = linkedList(points[i], false); + if (list) { + if (list == list->next) list->steiner = true; + queue.push_back(getLeftmost(list)); + } + } + std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { + return a->x < b->x; + }); + + // process holes from left to right + for (size_t i = 0; i < queue.size(); i++) { + eliminateHole(queue[i], outerNode); + outerNode = filterPoints(outerNode, outerNode->next); + } + + return outerNode; +} + +// find a bridge between vertices that connects hole with an outer ring and and link it +template +void Earcut::eliminateHole(Node* hole, Node* outerNode) { + outerNode = findHoleBridge(hole, outerNode); + if (outerNode) { + Node* b = splitPolygon(outerNode, hole); + filterPoints(b, b->next); + } +} + +// David Eberly's algorithm for finding a bridge between hole and outer polygon +template +typename Earcut::Node* +Earcut::findHoleBridge(Node* hole, Node* outerNode) { + Node* p = outerNode; + double hx = hole->x; + double hy = hole->y; + double qx = -std::numeric_limits::infinity(); + Node* m = nullptr; + + // find a segment intersected by a ray from the hole's leftmost Vertex to the left; + // segment's endpoint with lesser x will be potential connection Vertex + do { + if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { + double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); + if (x <= hx && x > qx) { + qx = x; + if (x == hx) { + if (hy == p->y) return p; + if (hy == p->next->y) return p->next; + } + m = p->x < p->next->x ? p : p->next; + } + } + p = p->next; + } while (p != outerNode); + + if (!m) return 0; + + if (hx == qx) return m->prev; + + // look for points inside the triangle of hole Vertex, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex + + const Node* stop = m; + double tanMin = std::numeric_limits::infinity(); + double tanCur = 0; + + p = m->next; + double mx = m->x; + double my = m->y; + + while (p != stop) { + if (hx >= p->x && p->x >= mx && hx != p->x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { + + tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential + + if ((tanCur < tanMin || (tanCur == tanMin && p->x > m->x)) && locallyInside(p, hole)) { + m = p; + tanMin = tanCur; + } + } + + p = p->next; + } + + return m; +} + +// interlink polygon nodes in z-order +template +void Earcut::indexCurve(Node* start) { + assert(start); + Node* p = start; + + do { + p->z = p->z ? p->z : zOrder(p->x, p->y); + p->prevZ = p->prev; + p->nextZ = p->next; + p = p->next; + } while (p != start); + + p->prevZ->nextZ = nullptr; + p->prevZ = nullptr; + + sortLinked(p); +} + +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +template +typename Earcut::Node* +Earcut::sortLinked(Node* list) { + assert(list); + Node* p; + Node* q; + Node* e; + Node* tail; + int i, numMerges, pSize, qSize; + int inSize = 1; + + while (true) { + p = list; + list = nullptr; + tail = nullptr; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q->nextZ; + if (!q) break; + } + + qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize == 0) { + e = q; + q = q->nextZ; + qSize--; + } else if (qSize == 0 || !q) { + e = p; + p = p->nextZ; + pSize--; + } else if (p->z <= q->z) { + e = p; + p = p->nextZ; + pSize--; + } else { + e = q; + q = q->nextZ; + qSize--; + } + + if (tail) tail->nextZ = e; + else list = e; + + e->prevZ = tail; + tail = e; + } + + p = q; + } + + tail->nextZ = nullptr; + + if (numMerges <= 1) return list; + + inSize *= 2; + } +} + +// z-order of a Vertex given coords and size of the data bounding box +template +int32_t Earcut::zOrder(const double x_, const double y_) { + // coords are transformed into non-negative 15-bit integer range + int32_t x = static_cast(32767.0 * (x_ - minX) / size); + int32_t y = static_cast(32767.0 * (y_ - minY) / size); + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); +} + +// find the leftmost node of a polygon ring +template +typename Earcut::Node* +Earcut::getLeftmost(Node* start) { + Node* p = start; + Node* leftmost = start; + do { + if (p->x < leftmost->x) leftmost = p; + p = p->next; + } while (p != start); + + return leftmost; +} + +// check if a point lies within a convex triangle +template +bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { + return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 && + (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 && + (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0; +} + +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +template +bool Earcut::isValidDiagonal(Node* a, Node* b) { + return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && + locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b); +} + +// signed area of a triangle +template +double Earcut::area(const Node* p, const Node* q, const Node* r) const { + return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); +} + +// check if two points are equal +template +bool Earcut::equals(const Node* p1, const Node* p2) { + return p1->x == p2->x && p1->y == p2->y; +} + +// check if two segments intersect +template +bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { + if ((equals(p1, q1) && equals(p2, q2)) || + (equals(p1, q2) && equals(p2, q1))) return true; + return (area(p1, q1, p2) > 0) != (area(p1, q1, q2) > 0) && + (area(p2, q2, p1) > 0) != (area(p2, q2, q1) > 0); +} + +// check if a polygon diagonal intersects any polygon segments +template +bool Earcut::intersectsPolygon(const Node* a, const Node* b) { + const Node* p = a; + do { + if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && + intersects(p, p->next, a, b)) return true; + p = p->next; + } while (p != a); + + return false; +} + +// check if a polygon diagonal is locally inside the polygon +template +bool Earcut::locallyInside(const Node* a, const Node* b) { + return area(a->prev, a, a->next) < 0 ? + area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : + area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; +} + +// check if the middle Vertex of a polygon diagonal is inside the polygon +template +bool Earcut::middleInside(const Node* a, const Node* b) { + const Node* p = a; + bool inside = false; + double px = (a->x + b->x) / 2; + double py = (a->y + b->y) / 2; + do { + if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && + (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) + inside = !inside; + p = p->next; + } while (p != a); + + return inside; +} + +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits +// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a +// single ring +template +typename Earcut::Node* +Earcut::splitPolygon(Node* a, Node* b) { + Node* a2 = nodes.construct(a->i, a->x, a->y); + Node* b2 = nodes.construct(b->i, b->x, b->y); + Node* an = a->next; + Node* bp = b->prev; + + a->next = b; + b->prev = a; + + a2->next = an; + an->prev = a2; + + b2->next = a2; + a2->prev = b2; + + bp->next = b2; + b2->prev = bp; + + return b2; +} + +// create a node and util::optionally link it with previous one (in a circular doubly linked list) +template template +typename Earcut::Node* +Earcut::insertNode(N i, const Point& pt, Node* last) { + Node* p = nodes.construct(i, util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); + + if (!last) { + p->prev = p; + p->next = p; + + } else { + assert(last); + p->next = last->next; + p->prev = last; + last->next->prev = p; + last->next = p; + } + return p; +} + +template +void Earcut::removeNode(Node* p) { + p->next->prev = p->prev; + p->prev->next = p->next; + + if (p->prevZ) p->prevZ->nextZ = p->nextZ; + if (p->nextZ) p->nextZ->prevZ = p->prevZ; +} +} + +template +std::vector earcut(const Polygon& poly) { + qt_mapbox::detail::Earcut earcut; + earcut(poly); + return earcut.indices; +} +} +#endif //EARCUT_HPP diff --git a/src/3rdparty/earcut/qt_attribution.json b/src/3rdparty/earcut/qt_attribution.json new file mode 100644 index 0000000..0e52989 --- /dev/null +++ b/src/3rdparty/earcut/qt_attribution.json @@ -0,0 +1,13 @@ +{ + "Id": "earcut", + "Name": "Earcut Polygon Triangulation Library", + "QDocModule": "qtlocation", + "QtUsage": "Used in the QML plugin of Qt Location.", + + "Description": "A C++ port of earcut.js, a fast, header-only polygon triangulation library.", + "Homepage": "https://github.com/mapbox/earcut.hpp", + "LicenseId": "ISC", + "License": "ISC License", + "LicenseFile": "LICENSE", + "Copyright": "Copyright (c) 2015 Mapbox" +} diff --git a/src/3rdparty/poly2tri/AUTHORS b/src/3rdparty/poly2tri/AUTHORS new file mode 100644 index 0000000..421601a --- /dev/null +++ b/src/3rdparty/poly2tri/AUTHORS @@ -0,0 +1,8 @@ +Primary Contributors: + + Mason Green (C++, Python) + Thomas Åhlén (Java) + +Other Contributors: + + diff --git a/src/3rdparty/poly2tri/LICENSE b/src/3rdparty/poly2tri/LICENSE new file mode 100644 index 0000000..9417c08 --- /dev/null +++ b/src/3rdparty/poly2tri/LICENSE @@ -0,0 +1,27 @@ +Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors +http://code.google.com/p/poly2tri/ + +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of Poly2Tri nor the names of its contributors may be + used to endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/3rdparty/poly2tri/common/shapes.cpp b/src/3rdparty/poly2tri/common/shapes.cpp new file mode 100644 index 0000000..2ac7e97 --- /dev/null +++ b/src/3rdparty/poly2tri/common/shapes.cpp @@ -0,0 +1,363 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "shapes.h" +#include + +namespace p2t { + +Triangle::Triangle(Point& a, Point& b, Point& c) +{ + points_[0] = &a; points_[1] = &b; points_[2] = &c; + neighbors_[0] = NULL; neighbors_[1] = NULL; neighbors_[2] = NULL; + constrained_edge[0] = constrained_edge[1] = constrained_edge[2] = false; + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; + interior_ = false; +} + +// Update neighbor pointers +void Triangle::MarkNeighbor(Point* p1, Point* p2, Triangle* t) +{ + if ((p1 == points_[2] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[2])) + neighbors_[0] = t; + else if ((p1 == points_[0] && p2 == points_[2]) || (p1 == points_[2] && p2 == points_[0])) + neighbors_[1] = t; + else if ((p1 == points_[0] && p2 == points_[1]) || (p1 == points_[1] && p2 == points_[0])) + neighbors_[2] = t; + else + assert(0); +} + +// Exhaustive search to update neighbor pointers +void Triangle::MarkNeighbor(Triangle& t) +{ + if (t.Contains(points_[1], points_[2])) { + neighbors_[0] = &t; + t.MarkNeighbor(points_[1], points_[2], this); + } else if (t.Contains(points_[0], points_[2])) { + neighbors_[1] = &t; + t.MarkNeighbor(points_[0], points_[2], this); + } else if (t.Contains(points_[0], points_[1])) { + neighbors_[2] = &t; + t.MarkNeighbor(points_[0], points_[1], this); + } +} + +/** + * Clears all references to all other triangles and points + */ +void Triangle::Clear() +{ + Triangle *t; + for (int i=0; i<3; i++) + { + t = neighbors_[i]; + if (t != NULL) + { + t->ClearNeighbor( this ); + } + } + ClearNeighbors(); + points_[0]=points_[1]=points_[2] = NULL; +} + +void Triangle::ClearNeighbor(Triangle *triangle ) +{ + if (neighbors_[0] == triangle) + { + neighbors_[0] = NULL; + } + else if (neighbors_[1] == triangle) + { + neighbors_[1] = NULL; + } + else + { + neighbors_[2] = NULL; + } +} + +void Triangle::ClearNeighbors() +{ + neighbors_[0] = NULL; + neighbors_[1] = NULL; + neighbors_[2] = NULL; +} + +void Triangle::ClearDelunayEdges() +{ + delaunay_edge[0] = delaunay_edge[1] = delaunay_edge[2] = false; +} + +Point* Triangle::OppositePoint(Triangle& t, Point& p) +{ + Point *cw = t.PointCW(p); + return PointCW(*cw); +} + +// Legalized triangle by rotating clockwise around point(0) +void Triangle::Legalize(Point& point) +{ + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &point; +} + +// Legalize triagnle by rotating clockwise around oPoint +void Triangle::Legalize(Point& opoint, Point& npoint) +{ + if (&opoint == points_[0]) { + points_[1] = points_[0]; + points_[0] = points_[2]; + points_[2] = &npoint; + } else if (&opoint == points_[1]) { + points_[2] = points_[1]; + points_[1] = points_[0]; + points_[0] = &npoint; + } else if (&opoint == points_[2]) { + points_[0] = points_[2]; + points_[2] = points_[1]; + points_[1] = &npoint; + } else { + assert(0); + } +} + +int Triangle::Index(const Point* p) +{ + if (p == points_[0]) { + return 0; + } else if (p == points_[1]) { + return 1; + } else if (p == points_[2]) { + return 2; + } + assert(0); +} + +int Triangle::EdgeIndex(const Point* p1, const Point* p2) +{ + if (points_[0] == p1) { + if (points_[1] == p2) { + return 2; + } else if (points_[2] == p2) { + return 1; + } + } else if (points_[1] == p1) { + if (points_[2] == p2) { + return 0; + } else if (points_[0] == p2) { + return 2; + } + } else if (points_[2] == p1) { + if (points_[0] == p2) { + return 1; + } else if (points_[1] == p2) { + return 0; + } + } + return -1; +} + +void Triangle::MarkConstrainedEdge(const int index) +{ + constrained_edge[index] = true; +} + +void Triangle::MarkConstrainedEdge(Edge& edge) +{ + MarkConstrainedEdge(edge.p, edge.q); +} + +// Mark edge as constrained +void Triangle::MarkConstrainedEdge(Point* p, Point* q) +{ + if ((q == points_[0] && p == points_[1]) || (q == points_[1] && p == points_[0])) { + constrained_edge[2] = true; + } else if ((q == points_[0] && p == points_[2]) || (q == points_[2] && p == points_[0])) { + constrained_edge[1] = true; + } else if ((q == points_[1] && p == points_[2]) || (q == points_[2] && p == points_[1])) { + constrained_edge[0] = true; + } +} + +// The point counter-clockwise to given point +Point* Triangle::PointCW(Point& point) +{ + if (&point == points_[0]) { + return points_[2]; + } else if (&point == points_[1]) { + return points_[0]; + } else if (&point == points_[2]) { + return points_[1]; + } + assert(0); +} + +// The point counter-clockwise to given point +Point* Triangle::PointCCW(Point& point) +{ + if (&point == points_[0]) { + return points_[1]; + } else if (&point == points_[1]) { + return points_[2]; + } else if (&point == points_[2]) { + return points_[0]; + } + assert(0); +} + +// The neighbor clockwise to given point +Triangle* Triangle::NeighborCW(Point& point) +{ + if (&point == points_[0]) { + return neighbors_[1]; + } else if (&point == points_[1]) { + return neighbors_[2]; + } + return neighbors_[0]; +} + +// The neighbor counter-clockwise to given point +Triangle* Triangle::NeighborCCW(Point& point) +{ + if (&point == points_[0]) { + return neighbors_[2]; + } else if (&point == points_[1]) { + return neighbors_[0]; + } + return neighbors_[1]; +} + +bool Triangle::GetConstrainedEdgeCCW(Point& p) +{ + if (&p == points_[0]) { + return constrained_edge[2]; + } else if (&p == points_[1]) { + return constrained_edge[0]; + } + return constrained_edge[1]; +} + +bool Triangle::GetConstrainedEdgeCW(Point& p) +{ + if (&p == points_[0]) { + return constrained_edge[1]; + } else if (&p == points_[1]) { + return constrained_edge[2]; + } + return constrained_edge[0]; +} + +void Triangle::SetConstrainedEdgeCCW(Point& p, bool ce) +{ + if (&p == points_[0]) { + constrained_edge[2] = ce; + } else if (&p == points_[1]) { + constrained_edge[0] = ce; + } else { + constrained_edge[1] = ce; + } +} + +void Triangle::SetConstrainedEdgeCW(Point& p, bool ce) +{ + if (&p == points_[0]) { + constrained_edge[1] = ce; + } else if (&p == points_[1]) { + constrained_edge[2] = ce; + } else { + constrained_edge[0] = ce; + } +} + +bool Triangle::GetDelunayEdgeCCW(Point& p) +{ + if (&p == points_[0]) { + return delaunay_edge[2]; + } else if (&p == points_[1]) { + return delaunay_edge[0]; + } + return delaunay_edge[1]; +} + +bool Triangle::GetDelunayEdgeCW(Point& p) +{ + if (&p == points_[0]) { + return delaunay_edge[1]; + } else if (&p == points_[1]) { + return delaunay_edge[2]; + } + return delaunay_edge[0]; +} + +void Triangle::SetDelunayEdgeCCW(Point& p, bool e) +{ + if (&p == points_[0]) { + delaunay_edge[2] = e; + } else if (&p == points_[1]) { + delaunay_edge[0] = e; + } else { + delaunay_edge[1] = e; + } +} + +void Triangle::SetDelunayEdgeCW(Point& p, bool e) +{ + if (&p == points_[0]) { + delaunay_edge[1] = e; + } else if (&p == points_[1]) { + delaunay_edge[2] = e; + } else { + delaunay_edge[0] = e; + } +} + +// The neighbor across to given point +Triangle& Triangle::NeighborAcross(Point& opoint) +{ + if (&opoint == points_[0]) { + return *neighbors_[0]; + } else if (&opoint == points_[1]) { + return *neighbors_[1]; + } + return *neighbors_[2]; +} + +void Triangle::DebugPrint() +{ + using namespace std; + cout << points_[0]->x << "," << points_[0]->y << " "; + cout << points_[1]->x << "," << points_[1]->y << " "; + cout << points_[2]->x << "," << points_[2]->y << endl; +} + +} + diff --git a/src/3rdparty/poly2tri/common/shapes.h b/src/3rdparty/poly2tri/common/shapes.h new file mode 100644 index 0000000..5b90ea6 --- /dev/null +++ b/src/3rdparty/poly2tri/common/shapes.h @@ -0,0 +1,325 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Include guard +#ifndef SHAPES_H +#define SHAPES_H + +#include +#include +#include +#include + +namespace p2t { + +struct Edge; + +struct Point { + + double x, y; + + /// Default constructor does nothing (for performance). + Point() + { + x = 0.0; + y = 0.0; + } + + /// The edges this point constitutes an upper ending point + std::vector edge_list; + + /// Construct using coordinates. + Point(double x, double y) : x(x), y(y) {} + + /// Set this point to all zeros. + void set_zero() + { + x = 0.0; + y = 0.0; + } + + /// Set this point to some specified coordinates. + void set(double x_, double y_) + { + x = x_; + y = y_; + } + + /// Negate this point. + Point operator -() const + { + Point v; + v.set(-x, -y); + return v; + } + + /// Add a point to this point. + void operator +=(const Point& v) + { + x += v.x; + y += v.y; + } + + /// Subtract a point from this point. + void operator -=(const Point& v) + { + x -= v.x; + y -= v.y; + } + + /// Multiply this point by a scalar. + void operator *=(double a) + { + x *= a; + y *= a; + } + + /// Get the length of this point (the norm). + double Length() const + { + return std::sqrt(x * x + y * y); + } + + /// Convert this point into a unit point. Returns the Length. + double Normalize() + { + double len = Length(); + x /= len; + y /= len; + return len; + } + +}; + +// Represents a simple polygon's edge +struct Edge { + + Point* p, *q; + + /// Constructor + Edge(Point& p1, Point& p2) : p(&p1), q(&p2) + { + if (p1.y > p2.y) { + q = &p1; + p = &p2; + } else if (p1.y == p2.y) { + if (p1.x > p2.x) { + q = &p1; + p = &p2; + } else if (p1.x == p2.x) { + // Repeat points + assert(false); + } + } + + q->edge_list.push_back(this); + } +}; + +// Triangle-based data structures are know to have better performance than quad-edge structures +// See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator" +// "Triangulations in CGAL" +class Triangle { +public: + +/// Constructor +Triangle(Point& a, Point& b, Point& c); + +/// Flags to determine if an edge is a Constrained edge +bool constrained_edge[3]; +/// Flags to determine if an edge is a Delauney edge +bool delaunay_edge[3]; + +Point* GetPoint(const int& index); +Point* PointCW(Point& point); +Point* PointCCW(Point& point); +Point* OppositePoint(Triangle& t, Point& p); + +Triangle* GetNeighbor(const int& index); +void MarkNeighbor(Point* p1, Point* p2, Triangle* t); +void MarkNeighbor(Triangle& t); + +void MarkConstrainedEdge(const int index); +void MarkConstrainedEdge(Edge& edge); +void MarkConstrainedEdge(Point* p, Point* q); + +int Index(const Point* p); +int EdgeIndex(const Point* p1, const Point* p2); + +Triangle* NeighborCW(Point& point); +Triangle* NeighborCCW(Point& point); +bool GetConstrainedEdgeCCW(Point& p); +bool GetConstrainedEdgeCW(Point& p); +void SetConstrainedEdgeCCW(Point& p, bool ce); +void SetConstrainedEdgeCW(Point& p, bool ce); +bool GetDelunayEdgeCCW(Point& p); +bool GetDelunayEdgeCW(Point& p); +void SetDelunayEdgeCCW(Point& p, bool e); +void SetDelunayEdgeCW(Point& p, bool e); + +bool Contains(Point* p); +bool Contains(const Edge& e); +bool Contains(Point* p, Point* q); +void Legalize(Point& point); +void Legalize(Point& opoint, Point& npoint); +/** + * Clears all references to all other triangles and points + */ +void Clear(); +void ClearNeighbor(Triangle *triangle ); +void ClearNeighbors(); +void ClearDelunayEdges(); + +inline bool IsInterior(); +inline void IsInterior(bool b); + +Triangle& NeighborAcross(Point& opoint); + +void DebugPrint(); + +private: + +/// Triangle points +Point* points_[3]; +/// Neighbor list +Triangle* neighbors_[3]; + +/// Has this triangle been marked as an interior triangle? +bool interior_; +}; + +inline bool cmp(const Point* a, const Point* b) +{ + if (a->y < b->y) { + return true; + } else if (a->y == b->y) { + // Make sure q is point with greater x value + if (a->x < b->x) { + return true; + } + } + return false; +} + +/// Add two points_ component-wise. +inline Point operator +(const Point& a, const Point& b) +{ + return Point(a.x + b.x, a.y + b.y); +} + +/// Subtract two points_ component-wise. +inline Point operator -(const Point& a, const Point& b) +{ + return Point(a.x - b.x, a.y - b.y); +} + +/// Multiply point by scalar +inline Point operator *(double s, const Point& a) +{ + return Point(s * a.x, s * a.y); +} + +inline bool operator ==(const Point& a, const Point& b) +{ + return a.x == b.x && a.y == b.y; +} + +inline bool operator !=(const Point& a, const Point& b) +{ + return !(a.x == b.x) && !(a.y == b.y); +} + +/// Peform the dot product on two vectors. +inline double Dot(const Point& a, const Point& b) +{ + return a.x * b.x + a.y * b.y; +} + +/// Perform the cross product on two vectors. In 2D this produces a scalar. +inline double Cross(const Point& a, const Point& b) +{ + return a.x * b.y - a.y * b.x; +} + +/// Perform the cross product on a point and a scalar. In 2D this produces +/// a point. +inline Point Cross(const Point& a, double s) +{ + return Point(s * a.y, -s * a.x); +} + +/// Perform the cross product on a scalar and a point. In 2D this produces +/// a point. +inline Point Cross(const double s, const Point& a) +{ + return Point(-s * a.y, s * a.x); +} + +inline Point* Triangle::GetPoint(const int& index) +{ + return points_[index]; +} + +inline Triangle* Triangle::GetNeighbor(const int& index) +{ + return neighbors_[index]; +} + +inline bool Triangle::Contains(Point* p) +{ + return p == points_[0] || p == points_[1] || p == points_[2]; +} + +inline bool Triangle::Contains(const Edge& e) +{ + return Contains(e.p) && Contains(e.q); +} + +inline bool Triangle::Contains(Point* p, Point* q) +{ + return Contains(p) && Contains(q); +} + +inline bool Triangle::IsInterior() +{ + return interior_; +} + +inline void Triangle::IsInterior(bool b) +{ + interior_ = b; +} + +} + +#endif + + diff --git a/src/3rdparty/poly2tri/common/utils.h b/src/3rdparty/poly2tri/common/utils.h new file mode 100644 index 0000000..8744b6d --- /dev/null +++ b/src/3rdparty/poly2tri/common/utils.h @@ -0,0 +1,127 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef UTILS_H +#define UTILS_H + +// Otherwise #defines like M_PI are undeclared under Visual Studio +#define _USE_MATH_DEFINES + +#include +#include + +#ifndef M_PI +#define M_PI (3.14159265358979323846) +#endif + +namespace p2t { + +const double PI_3div4 = 3 * M_PI / 4; +const double PI_div2 = 1.57079632679489661923; +const double EPSILON = 1e-12; + +enum Orientation { CW, CCW, COLLINEAR }; + +/** + * Forumla to calculate signed area
    + * Positive if CCW
    + * Negative if CW
    + * 0 if collinear
    + *
    + * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
    + *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
    + * 
    + */ +Orientation Orient2d(Point& pa, Point& pb, Point& pc) +{ + double detleft = (pa.x - pc.x) * (pb.y - pc.y); + double detright = (pa.y - pc.y) * (pb.x - pc.x); + double val = detleft - detright; + if (val > -EPSILON && val < EPSILON) { + return COLLINEAR; + } else if (val > 0) { + return CCW; + } + return CW; +} + +/* +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double pdx = pd.x; + double pdy = pd.y; + double adx = pa.x - pdx; + double ady = pa.y - pdy; + double bdx = pb.x - pdx; + double bdy = pb.y - pdy; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= EPSILON) { + return false; + } + + double cdx = pc.x - pdx; + double cdy = pc.y - pdy; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= EPSILON) { + return false; + } + + return true; +} + +*/ + +bool InScanArea(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double oadb = (pa.x - pb.x)*(pd.y - pb.y) - (pd.x - pb.x)*(pa.y - pb.y); + if (oadb >= -EPSILON) { + return false; + } + + double oadc = (pa.x - pc.x)*(pd.y - pc.y) - (pd.x - pc.x)*(pa.y - pc.y); + if (oadc <= EPSILON) { + return false; + } + return true; +} + +} + +#endif + diff --git a/src/3rdparty/poly2tri/poly2tri.h b/src/3rdparty/poly2tri/poly2tri.h new file mode 100644 index 0000000..042cb3d --- /dev/null +++ b/src/3rdparty/poly2tri/poly2tri.h @@ -0,0 +1,39 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef POLY2TRI_H +#define POLY2TRI_H + +#include "common/shapes.h" +#include "sweep/cdt.h" + +#endif + diff --git a/src/3rdparty/poly2tri/poly2tri.pro b/src/3rdparty/poly2tri/poly2tri.pro new file mode 100644 index 0000000..76f2779 --- /dev/null +++ b/src/3rdparty/poly2tri/poly2tri.pro @@ -0,0 +1,27 @@ +TARGET = poly2tri + +CONFIG += staticlib warn_off optimize_full + +load(qt_helper_lib) + +# workaround for QTBUG-31586 +contains(QT_CONFIG, c++11): CONFIG += c++11 + +gcc { + QMAKE_CFLAGS_OPTIMIZE_FULL += -ffast-math + !clang:!intel_icc:!rim_qcc: QMAKE_CXXFLAGS_WARN_ON += -Wno-error=return-type +} + +HEADERS += poly2tri.h \ + common/shapes.h \ + common/utils.h \ + sweep/advancing_front.h \ + sweep/cdt.h \ + sweep/sweep.h \ + sweep/sweep_context.h + +SOURCES += common/shapes.cpp \ + sweep/sweep_context.cpp \ + sweep/cdt.cpp \ + sweep/sweep.cpp \ + sweep/advancing_front.cpp diff --git a/src/3rdparty/poly2tri/qt_attribution.json b/src/3rdparty/poly2tri/qt_attribution.json new file mode 100644 index 0000000..fdc7c29 --- /dev/null +++ b/src/3rdparty/poly2tri/qt_attribution.json @@ -0,0 +1,13 @@ +{ + "Id": "poly2tri", + "Name": "Poly2Tri Polygon Triangulation Library", + "QDocModule": "qtlocation", + "QtUsage": "Used in the QML plugin of Qt Location.", + + "Description": "Poly2Tri is a sweepline constrained Delaunay Polygon Triangulation Library.", + "Homepage": "http://code.google.com/p/poly2tri/", + "LicenseId": "BSD-3-Clause", + "License": "BSD 3-clause \"New\" or \"Revised\" License", + "LicenseFile": "LICENSE", + "Copyright": "Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors" +} diff --git a/src/3rdparty/poly2tri/sweep/advancing_front.cpp b/src/3rdparty/poly2tri/sweep/advancing_front.cpp new file mode 100644 index 0000000..0377984 --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/advancing_front.cpp @@ -0,0 +1,109 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "advancing_front.h" + +namespace p2t { + +AdvancingFront::AdvancingFront(Node& head, Node& tail) +{ + head_ = &head; + tail_ = &tail; + search_node_ = &head; +} + +Node* AdvancingFront::LocateNode(const double& x) +{ + Node* node = search_node_; + + if (x < node->value) { + while ((node = node->prev) != NULL) { + if (x >= node->value) { + search_node_ = node; + return node; + } + } + } else { + while ((node = node->next) != NULL) { + if (x < node->value) { + search_node_ = node->prev; + return node->prev; + } + } + } + return NULL; +} + +Node* AdvancingFront::FindSearchNode(const double& x) +{ + (void)x; // suppress compiler warnings "unused parameter 'x'" + // TODO: implement BST index + return search_node_; +} + +Node* AdvancingFront::LocatePoint(const Point* point) +{ + const double px = point->x; + Node* node = FindSearchNode(px); + const double nx = node->point->x; + + if (px == nx) { + if (point != node->point) { + // We might have two nodes with same x value for a short time + if (point == node->prev->point) { + node = node->prev; + } else if (point == node->next->point) { + node = node->next; + } else { + assert(0); + } + } + } else if (px < nx) { + while ((node = node->prev) != NULL) { + if (point == node->point) { + break; + } + } + } else { + while ((node = node->next) != NULL) { + if (point == node->point) + break; + } + } + if (node) search_node_ = node; + return node; +} + +AdvancingFront::~AdvancingFront() +{ +} + +} + diff --git a/src/3rdparty/poly2tri/sweep/advancing_front.h b/src/3rdparty/poly2tri/sweep/advancing_front.h new file mode 100644 index 0000000..bab73d4 --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/advancing_front.h @@ -0,0 +1,118 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef ADVANCED_FRONT_H +#define ADVANCED_FRONT_H + +#include "../common/shapes.h" + +namespace p2t { + +struct Node; + +// Advancing front node +struct Node { + Point* point; + Triangle* triangle; + + Node* next; + Node* prev; + + double value; + + Node(Point& p) : point(&p), triangle(NULL), next(NULL), prev(NULL), value(p.x) + { + } + + Node(Point& p, Triangle& t) : point(&p), triangle(&t), next(NULL), prev(NULL), value(p.x) + { + } + +}; + +// Advancing front +class AdvancingFront { +public: + +AdvancingFront(Node& head, Node& tail); +// Destructor +~AdvancingFront(); + +Node* head(); +void set_head(Node* node); +Node* tail(); +void set_tail(Node* node); +Node* search(); +void set_search(Node* node); + +/// Locate insertion point along advancing front +Node* LocateNode(const double& x); + +Node* LocatePoint(const Point* point); + +private: + +Node* head_, *tail_, *search_node_; + +Node* FindSearchNode(const double& x); +}; + +inline Node* AdvancingFront::head() +{ + return head_; +} +inline void AdvancingFront::set_head(Node* node) +{ + head_ = node; +} + +inline Node* AdvancingFront::tail() +{ + return tail_; +} +inline void AdvancingFront::set_tail(Node* node) +{ + tail_ = node; +} + +inline Node* AdvancingFront::search() +{ + return search_node_; +} + +inline void AdvancingFront::set_search(Node* node) +{ + search_node_ = node; +} + +} + +#endif diff --git a/src/3rdparty/poly2tri/sweep/cdt.cpp b/src/3rdparty/poly2tri/sweep/cdt.cpp new file mode 100644 index 0000000..e0b3ec7 --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/cdt.cpp @@ -0,0 +1,72 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "cdt.h" + +namespace p2t { + +CDT::CDT(std::vector polyline) +{ + sweep_context_ = new SweepContext(polyline); + sweep_ = new Sweep; +} + +void CDT::AddHole(std::vector polyline) +{ + sweep_context_->AddHole(polyline); +} + +void CDT::AddPoint(Point* point) { + sweep_context_->AddPoint(point); +} + +void CDT::Triangulate() +{ + sweep_->Triangulate(*sweep_context_); +} + +std::vector CDT::GetTriangles() +{ + return sweep_context_->GetTriangles(); +} + +std::list CDT::GetMap() +{ + return sweep_context_->GetMap(); +} + +CDT::~CDT() +{ + delete sweep_context_; + delete sweep_; +} + +} + diff --git a/src/3rdparty/poly2tri/sweep/cdt.h b/src/3rdparty/poly2tri/sweep/cdt.h new file mode 100644 index 0000000..e7b703d --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/cdt.h @@ -0,0 +1,105 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef CDT_H +#define CDT_H + +#include "advancing_front.h" +#include "sweep_context.h" +#include "sweep.h" + +/** + * + * @author Mason Green + * + */ + +namespace p2t { + +class CDT +{ +public: + + /** + * Constructor - add polyline with non repeating points + * + * @param polyline + */ + CDT(std::vector polyline); + + /** + * Destructor - clean up memory + */ + ~CDT(); + + /** + * Add a hole + * + * @param polyline + */ + void AddHole(std::vector polyline); + + /** + * Add a steiner point + * + * @param point + */ + void AddPoint(Point* point); + + /** + * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points + */ + void Triangulate(); + + /** + * Get CDT triangles + */ + std::vector GetTriangles(); + + /** + * Get triangle map + */ + std::list GetMap(); + + private: + + /** + * Internals + */ + + SweepContext* sweep_context_; + Sweep* sweep_; + +}; + +} + +#endif diff --git a/src/3rdparty/poly2tri/sweep/sweep.cpp b/src/3rdparty/poly2tri/sweep/sweep.cpp new file mode 100644 index 0000000..954d2db --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/sweep.cpp @@ -0,0 +1,814 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include "sweep.h" +#include "sweep_context.h" +#include "advancing_front.h" +#include "../common/utils.h" + +namespace p2t { + +// Triangulate simple polygon with holes +void Sweep::Triangulate(SweepContext& tcx) +{ + tcx.InitTriangulation(); + tcx.CreateAdvancingFront(nodes_); + // Sweep points; build mesh + SweepPoints(tcx); + // Clean up + FinalizationPolygon(tcx); +} + +void Sweep::SweepPoints(SweepContext& tcx) +{ + for (int i = 1; i < tcx.point_count(); i++) { + Point& point = *tcx.GetPoint(i); + Node* node = &PointEvent(tcx, point); + for (unsigned int i = 0; i < point.edge_list.size(); i++) { + EdgeEvent(tcx, point.edge_list[i], node); + } + } +} + +void Sweep::FinalizationPolygon(SweepContext& tcx) +{ + // Get an Internal triangle to start with + Triangle* t = tcx.front()->head()->next->triangle; + Point* p = tcx.front()->head()->next->point; + while (!t->GetConstrainedEdgeCW(*p)) { + t = t->NeighborCCW(*p); + } + + // Collect interior triangles constrained by edges + tcx.MeshClean(*t); +} + +Node& Sweep::PointEvent(SweepContext& tcx, Point& point) +{ + Node& node = tcx.LocateNode(point); + Node& new_node = NewFrontTriangle(tcx, point, node); + + // Only need to check +epsilon since point never have smaller + // x value than node due to how we fetch nodes from the front + if (point.x <= node.point->x + EPSILON) { + Fill(tcx, node); + } + + //tcx.AddNode(new_node); + + FillAdvancingFront(tcx, new_node); + return new_node; +} + +void Sweep::EdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + tcx.edge_event.constrained_edge = edge; + tcx.edge_event.right = (edge->p->x > edge->q->x); + + if (IsEdgeSideOfTriangle(*node->triangle, *edge->p, *edge->q)) { + return; + } + + // For now we will do all needed filling + // TODO: integrate with flip process might give some better performance + // but for now this avoid the issue with cases that needs both flips and fills + FillEdgeEvent(tcx, edge, node); + EdgeEvent(tcx, *edge->p, *edge->q, node->triangle, *edge->q); +} + +void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point) +{ + if (IsEdgeSideOfTriangle(*triangle, ep, eq)) { + return; + } + + Point* p1 = triangle->PointCCW(point); + Orientation o1 = Orient2d(eq, *p1, ep); + if (o1 == COLLINEAR) { + if ( triangle->Contains(&eq, p1)) { + triangle->MarkConstrainedEdge(&eq, p1 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p1; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p1, triangle, *p1 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); + } + return; + } + + Point* p2 = triangle->PointCW(point); + Orientation o2 = Orient2d(eq, *p2, ep); + if (o2 == COLLINEAR) { + if ( triangle->Contains(&eq, p2)) { + triangle->MarkConstrainedEdge(&eq, p2 ); + // We are modifying the constraint maybe it would be better to + // not change the given constraint and just keep a variable for the new constraint + tcx.edge_event.constrained_edge->q = p2; + triangle = &triangle->NeighborAcross(point); + EdgeEvent( tcx, ep, *p2, triangle, *p2 ); + } else { + std::runtime_error("EdgeEvent - collinear points not supported"); + assert(0); + } + return; + } + + if (o1 == o2) { + // Need to decide if we are rotating CW or CCW to get to a triangle + // that will cross edge + if (o1 == CW) { + triangle = triangle->NeighborCCW(point); + } else{ + triangle = triangle->NeighborCW(point); + } + EdgeEvent(tcx, ep, eq, triangle, point); + } else { + // This triangle crosses constraint so lets flippin start! + FlipEdgeEvent(tcx, ep, eq, triangle, point); + } +} + +bool Sweep::IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq) +{ + int index = triangle.EdgeIndex(&ep, &eq); + + if (index != -1) { + triangle.MarkConstrainedEdge(index); + Triangle* t = triangle.GetNeighbor(index); + if (t) { + t->MarkConstrainedEdge(&ep, &eq); + } + return true; + } + return false; +} + +Node& Sweep::NewFrontTriangle(SweepContext& tcx, Point& point, Node& node) +{ + Triangle* triangle = new Triangle(point, *node.point, *node.next->point); + + triangle->MarkNeighbor(*node.triangle); + tcx.AddToMap(triangle); + + Node* new_node = new Node(point); + nodes_.push_back(new_node); + + new_node->next = node.next; + new_node->prev = &node; + node.next->prev = new_node; + node.next = new_node; + + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + + return *new_node; +} + +void Sweep::Fill(SweepContext& tcx, Node& node) +{ + Triangle* triangle = new Triangle(*node.prev->point, *node.point, *node.next->point); + + // TODO: should copy the constrained_edge value from neighbor triangles + // for now constrained_edge values are copied during the legalize + triangle->MarkNeighbor(*node.prev->triangle); + triangle->MarkNeighbor(*node.triangle); + + tcx.AddToMap(triangle); + + // Update the advancing front + node.prev->next = node.next; + node.next->prev = node.prev; + + // If it was legalized the triangle has already been mapped + if (!Legalize(tcx, *triangle)) { + tcx.MapTriangleToNodes(*triangle); + } + +} + +void Sweep::FillAdvancingFront(SweepContext& tcx, Node& n) +{ + + // Fill right holes + Node* node = n.next; + + while (node->next) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->next; + } + + // Fill left holes + node = n.prev; + + while (node->prev) { + // if HoleAngle exceeds 90 degrees then break. + if (LargeHole_DontFill(node)) break; + Fill(tcx, *node); + node = node->prev; + } + + // Fill right basins + if (n.next && n.next->next) { + double angle = BasinAngle(n); + if (angle < PI_3div4) { + FillBasin(tcx, n); + } + } +} + +// True if HoleAngle exceeds 90 degrees. +bool Sweep::LargeHole_DontFill(Node* node) { + + Node* nextNode = node->next; + Node* prevNode = node->prev; + if (!AngleExceeds90Degrees(node->point, nextNode->point, prevNode->point)) + return false; + + // Check additional points on front. + Node* next2Node = nextNode->next; + // "..Plus.." because only want angles on same side as point being added. + if ((next2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, next2Node->point, prevNode->point)) + return false; + + Node* prev2Node = prevNode->prev; + // "..Plus.." because only want angles on same side as point being added. + if ((prev2Node != NULL) && !AngleExceedsPlus90DegreesOrIsNegative(node->point, nextNode->point, prev2Node->point)) + return false; + + return true; +} + +bool Sweep::AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceeds90Degrees = ((angle > PI_div2) || (angle < -PI_div2)); + return exceeds90Degrees; +} + +bool Sweep::AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb) { + double angle = Angle(*origin, *pa, *pb); + bool exceedsPlus90DegreesOrIsNegative = (angle > PI_div2) || (angle < 0); + return exceedsPlus90DegreesOrIsNegative; +} + +double Sweep::Angle(Point& origin, Point& pa, Point& pb) { + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double px = origin.x; + double py = origin.y; + double ax = pa.x- px; + double ay = pa.y - py; + double bx = pb.x - px; + double by = pb.y - py; + double x = ax * by - ay * bx; + double y = ax * bx + ay * by; + double angle = atan2(x, y); + return angle; +} + +double Sweep::BasinAngle(Node& node) +{ + double ax = node.point->x - node.next->next->point->x; + double ay = node.point->y - node.next->next->point->y; + return atan2(ay, ax); +} + +double Sweep::HoleAngle(Node& node) +{ + /* Complex plane + * ab = cosA +i*sinA + * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx) + * atan2(y,x) computes the principal value of the argument function + * applied to the complex number x+iy + * Where x = ax*bx + ay*by + * y = ax*by - ay*bx + */ + double ax = node.next->point->x - node.point->x; + double ay = node.next->point->y - node.point->y; + double bx = node.prev->point->x - node.point->x; + double by = node.prev->point->y - node.point->y; + return atan2(ax * by - ay * bx, ax * bx + ay * by); +} + +bool Sweep::Legalize(SweepContext& tcx, Triangle& t) +{ + // To legalize a triangle we start by finding if any of the three edges + // violate the Delaunay condition + for (int i = 0; i < 3; i++) { + if (t.delaunay_edge[i]) + continue; + + Triangle* ot = t.GetNeighbor(i); + + if (ot) { + Point* p = t.GetPoint(i); + Point* op = ot->OppositePoint(t, *p); + int oi = ot->Index(op); + + // If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization) + // then we should not try to legalize + if (ot->constrained_edge[oi] || ot->delaunay_edge[oi]) { + t.constrained_edge[i] = ot->constrained_edge[oi]; + continue; + } + + bool inside = Incircle(*p, *t.PointCCW(*p), *t.PointCW(*p), *op); + + if (inside) { + // Lets mark this shared edge as Delaunay + t.delaunay_edge[i] = true; + ot->delaunay_edge[oi] = true; + + // Lets rotate shared edge one vertex CW to legalize it + RotateTrianglePair(t, *p, *ot, *op); + + // We now got one valid Delaunay Edge shared by two triangles + // This gives us 4 new edges to check for Delaunay + + // Make sure that triangle to node mapping is done only one time for a specific triangle + bool not_legalized = !Legalize(tcx, t); + if (not_legalized) { + tcx.MapTriangleToNodes(t); + } + + not_legalized = !Legalize(tcx, *ot); + if (not_legalized) + tcx.MapTriangleToNodes(*ot); + + // Reset the Delaunay edges, since they only are valid Delaunay edges + // until we add a new triangle or point. + // XXX: need to think about this. Can these edges be tried after we + // return to previous recursive level? + t.delaunay_edge[i] = false; + ot->delaunay_edge[oi] = false; + + // If triangle have been legalized no need to check the other edges since + // the recursive legalization will handles those so we can end here. + return true; + } + } + } + return false; +} + +bool Sweep::Incircle(Point& pa, Point& pb, Point& pc, Point& pd) +{ + double adx = pa.x - pd.x; + double ady = pa.y - pd.y; + double bdx = pb.x - pd.x; + double bdy = pb.y - pd.y; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + double oabd = adxbdy - bdxady; + + if (oabd <= 0) + return false; + + double cdx = pc.x - pd.x; + double cdy = pc.y - pd.y; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + double ocad = cdxady - adxcdy; + + if (ocad <= 0) + return false; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + + double alift = adx * adx + ady * ady; + double blift = bdx * bdx + bdy * bdy; + double clift = cdx * cdx + cdy * cdy; + + double det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd; + + return det > 0; +} + +void Sweep::RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op) +{ + Triangle* n1, *n2, *n3, *n4; + n1 = t.NeighborCCW(p); + n2 = t.NeighborCW(p); + n3 = ot.NeighborCCW(op); + n4 = ot.NeighborCW(op); + + bool ce1, ce2, ce3, ce4; + ce1 = t.GetConstrainedEdgeCCW(p); + ce2 = t.GetConstrainedEdgeCW(p); + ce3 = ot.GetConstrainedEdgeCCW(op); + ce4 = ot.GetConstrainedEdgeCW(op); + + bool de1, de2, de3, de4; + de1 = t.GetDelunayEdgeCCW(p); + de2 = t.GetDelunayEdgeCW(p); + de3 = ot.GetDelunayEdgeCCW(op); + de4 = ot.GetDelunayEdgeCW(op); + + t.Legalize(p, op); + ot.Legalize(op, p); + + // Remap delaunay_edge + ot.SetDelunayEdgeCCW(p, de1); + t.SetDelunayEdgeCW(p, de2); + t.SetDelunayEdgeCCW(op, de3); + ot.SetDelunayEdgeCW(op, de4); + + // Remap constrained_edge + ot.SetConstrainedEdgeCCW(p, ce1); + t.SetConstrainedEdgeCW(p, ce2); + t.SetConstrainedEdgeCCW(op, ce3); + ot.SetConstrainedEdgeCW(op, ce4); + + // Remap neighbors + // XXX: might optimize the markNeighbor by keeping track of + // what side should be assigned to what neighbor after the + // rotation. Now mark neighbor does lots of testing to find + // the right side. + t.ClearNeighbors(); + ot.ClearNeighbors(); + if (n1) ot.MarkNeighbor(*n1); + if (n2) t.MarkNeighbor(*n2); + if (n3) t.MarkNeighbor(*n3); + if (n4) ot.MarkNeighbor(*n4); + t.MarkNeighbor(ot); +} + +void Sweep::FillBasin(SweepContext& tcx, Node& node) +{ + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + tcx.basin.left_node = node.next->next; + } else { + tcx.basin.left_node = node.next; + } + + // Find the bottom and right node + tcx.basin.bottom_node = tcx.basin.left_node; + while (tcx.basin.bottom_node->next + && tcx.basin.bottom_node->point->y >= tcx.basin.bottom_node->next->point->y) { + tcx.basin.bottom_node = tcx.basin.bottom_node->next; + } + if (tcx.basin.bottom_node == tcx.basin.left_node) { + // No valid basin + return; + } + + tcx.basin.right_node = tcx.basin.bottom_node; + while (tcx.basin.right_node->next + && tcx.basin.right_node->point->y < tcx.basin.right_node->next->point->y) { + tcx.basin.right_node = tcx.basin.right_node->next; + } + if (tcx.basin.right_node == tcx.basin.bottom_node) { + // No valid basins + return; + } + + tcx.basin.width = tcx.basin.right_node->point->x - tcx.basin.left_node->point->x; + tcx.basin.left_highest = tcx.basin.left_node->point->y > tcx.basin.right_node->point->y; + + FillBasinReq(tcx, tcx.basin.bottom_node); +} + +void Sweep::FillBasinReq(SweepContext& tcx, Node* node) +{ + // if shallow stop filling + if (IsShallow(tcx, *node)) { + return; + } + + Fill(tcx, *node); + + if (node->prev == tcx.basin.left_node && node->next == tcx.basin.right_node) { + return; + } else if (node->prev == tcx.basin.left_node) { + Orientation o = Orient2d(*node->point, *node->next->point, *node->next->next->point); + if (o == CW) { + return; + } + node = node->next; + } else if (node->next == tcx.basin.right_node) { + Orientation o = Orient2d(*node->point, *node->prev->point, *node->prev->prev->point); + if (o == CCW) { + return; + } + node = node->prev; + } else { + // Continue with the neighbor node with lowest Y value + if (node->prev->point->y < node->next->point->y) { + node = node->prev; + } else { + node = node->next; + } + } + + FillBasinReq(tcx, node); +} + +bool Sweep::IsShallow(SweepContext& tcx, Node& node) +{ + double height; + + if (tcx.basin.left_highest) { + height = tcx.basin.left_node->point->y - node.point->y; + } else { + height = tcx.basin.right_node->point->y - node.point->y; + } + + // if shallow stop filling + if (tcx.basin.width > height) { + return true; + } + return false; +} + +void Sweep::FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + if (tcx.edge_event.right) { + FillRightAboveEdgeEvent(tcx, edge, node); + } else { + FillLeftAboveEdgeEvent(tcx, edge, node); + } +} + +void Sweep::FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->next->point->x < edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->next->point, *edge->p) == CCW) { + FillRightBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->next; + } + } +} + +void Sweep::FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x < edge->p->x) { + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else{ + // Convex + FillRightConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillRightBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.next); + if (node.next->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->point, *edge->p) == CCW) { + // Below + if (Orient2d(*node.point, *node.next->point, *node.next->next->point) == CCW) { + // Next is concave + FillRightConcaveEdgeEvent(tcx, edge, node); + } else { + // Next is convex + } + } + } + +} + +void Sweep::FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.next->point, *node.next->next->point, *node.next->next->next->point) == CCW) { + // Concave + FillRightConcaveEdgeEvent(tcx, edge, *node.next); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.next->next->point, *edge->p) == CCW) { + // Below + FillRightConvexEdgeEvent(tcx, edge, *node.next); + } else{ + // Above + } + } +} + +void Sweep::FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node) +{ + while (node->prev->point->x > edge->p->x) { + // Check if next node is below the edge + if (Orient2d(*edge->q, *node->prev->point, *edge->p) == CW) { + FillLeftBelowEdgeEvent(tcx, edge, *node); + } else { + node = node->prev; + } + } +} + +void Sweep::FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + if (node.point->x > edge->p->x) { + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else { + // Convex + FillLeftConvexEdgeEvent(tcx, edge, node); + // Retry this one + FillLeftBelowEdgeEvent(tcx, edge, node); + } + } +} + +void Sweep::FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + // Next concave or convex? + if (Orient2d(*node.prev->point, *node.prev->prev->point, *node.prev->prev->prev->point) == CW) { + // Concave + FillLeftConcaveEdgeEvent(tcx, edge, *node.prev); + } else{ + // Convex + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->prev->point, *edge->p) == CW) { + // Below + FillLeftConvexEdgeEvent(tcx, edge, *node.prev); + } else{ + // Above + } + } +} + +void Sweep::FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node) +{ + Fill(tcx, *node.prev); + if (node.prev->point != edge->p) { + // Next above or below edge? + if (Orient2d(*edge->q, *node.prev->point, *edge->p) == CW) { + // Below + if (Orient2d(*node.point, *node.prev->point, *node.prev->prev->point) == CW) { + // Next is concave + FillLeftConcaveEdgeEvent(tcx, edge, node); + } else{ + // Next is convex + } + } + } + +} + +void Sweep::FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p) +{ + Triangle& ot = t->NeighborAcross(p); + Point& op = *ot.OppositePoint(*t, p); + + if (&ot == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(p, *t->PointCCW(p), *t->PointCW(p), op)) { + // Lets rotate shared edge one vertex CW + RotateTrianglePair(*t, p, ot, op); + tcx.MapTriangleToNodes(*t); + tcx.MapTriangleToNodes(ot); + + if (p == eq && op == ep) { + if (eq == *tcx.edge_event.constrained_edge->q && ep == *tcx.edge_event.constrained_edge->p) { + t->MarkConstrainedEdge(&ep, &eq); + ot.MarkConstrainedEdge(&ep, &eq); + Legalize(tcx, *t); + Legalize(tcx, ot); + } else { + // XXX: I think one of the triangles should be legalized here? + } + } else { + Orientation o = Orient2d(eq, op, ep); + t = &NextFlipTriangle(tcx, (int)o, *t, ot, p, op); + FlipEdgeEvent(tcx, ep, eq, t, p); + } + } else { + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, *t, ot, newP); + EdgeEvent(tcx, ep, eq, t, p); + } +} + +Triangle& Sweep::NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op) +{ + if (o == CCW) { + // ot is not crossing edge after flip + int edge_index = ot.EdgeIndex(&p, &op); + ot.delaunay_edge[edge_index] = true; + Legalize(tcx, ot); + ot.ClearDelunayEdges(); + return t; + } + + // t is not crossing edge after flip + int edge_index = t.EdgeIndex(&p, &op); + + t.delaunay_edge[edge_index] = true; + Legalize(tcx, t); + t.ClearDelunayEdges(); + return ot; +} + +Point& Sweep::NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op) +{ + Orientation o2d = Orient2d(eq, op, ep); + if (o2d == CW) { + // Right + return *ot.PointCCW(op); + } else if (o2d == CCW) { + // Left + return *ot.PointCW(op); + } else{ + //throw new RuntimeException("[Unsupported] Opposing point on constrained edge"); + assert(0); + } +} + +void Sweep::FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, + Triangle& t, Point& p) +{ + Triangle& ot = t.NeighborAcross(p); + Point& op = *ot.OppositePoint(t, p); + + if (&t.NeighborAcross(p) == NULL) { + // If we want to integrate the fillEdgeEvent do it here + // With current implementation we should never get here + //throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle"); + assert(0); + } + + if (InScanArea(eq, *flip_triangle.PointCCW(eq), *flip_triangle.PointCW(eq), op)) { + // flip with new edge op->eq + FlipEdgeEvent(tcx, eq, op, &ot, op); + // TODO: Actually I just figured out that it should be possible to + // improve this by getting the next ot and op before the above + // flip and continue the flipScanEdgeEvent here + // set new ot and op here and loop back to inScanArea test + // also need to set a new flip_triangle first + // Turns out at first glance that this is somewhat complicated + // so it will have to wait. + } else{ + Point& newP = NextFlipPoint(ep, eq, ot, op); + FlipScanEdgeEvent(tcx, ep, eq, flip_triangle, ot, newP); + } +} + +Sweep::~Sweep() { + + // Clean up memory + for (size_t i = 0; i < nodes_.size(); i++) { + delete nodes_[i]; + } + +} + +} + diff --git a/src/3rdparty/poly2tri/sweep/sweep.h b/src/3rdparty/poly2tri/sweep/sweep.h new file mode 100644 index 0000000..9bb0b5d --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/sweep.h @@ -0,0 +1,285 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/** + * Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and + * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation', + * International Journal of Geographical Information Science + * + * "FlipScan" Constrained Edge Algorithm invented by Thomas Åhlén, thahlen@gmail.com + */ + +#ifndef SWEEP_H +#define SWEEP_H + +#include + +namespace p2t { + +class SweepContext; +struct Node; +struct Point; +struct Edge; +class Triangle; + +class Sweep +{ +public: + + /** + * Triangulate + * + * @param tcx + */ + void Triangulate(SweepContext& tcx); + + /** + * Destructor - clean up memory + */ + ~Sweep(); + +private: + + /** + * Start sweeping the Y-sorted point set from bottom to top + * + * @param tcx + */ + void SweepPoints(SweepContext& tcx); + + /** + * Find closes node to the left of the new point and + * create a new triangle. If needed new holes and basins + * will be filled to. + * + * @param tcx + * @param point + * @return + */ + Node& PointEvent(SweepContext& tcx, Point& point); + + /** + * + * + * @param tcx + * @param edge + * @param node + */ + void EdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangle, Point& point); + + /** + * Creates a new front triangle and legalize it + * + * @param tcx + * @param point + * @param node + * @return + */ + Node& NewFrontTriangle(SweepContext& tcx, Point& point, Node& node); + + /** + * Adds a triangle to the advancing front to fill a hole. + * @param tcx + * @param node - middle node, that is the bottom of the hole + */ + void Fill(SweepContext& tcx, Node& node); + + /** + * Returns true if triangle was legalized + */ + bool Legalize(SweepContext& tcx, Triangle& t); + + /** + * Requirement:
    + * 1. a,b and c form a triangle.
    + * 2. a and d is know to be on opposite side of bc
    + *
    +   *                a
    +   *                +
    +   *               / \
    +   *              /   \
    +   *            b/     \c
    +   *            +-------+
    +   *           /    d    \
    +   *          /           \
    +   * 
    + * Fact: d has to be in area B to have a chance to be inside the circle formed by + * a,b and c
    + * d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW
    + * This preknowledge gives us a way to optimize the incircle test + * @param a - triangle point, opposite d + * @param b - triangle point + * @param c - triangle point + * @param d - point opposite a + * @return true if d is inside circle, false if on circle edge + */ + bool Incircle(Point& pa, Point& pb, Point& pc, Point& pd); + + /** + * Rotates a triangle pair one vertex CW + *
    +   *       n2                    n2
    +   *  P +-----+             P +-----+
    +   *    | t  /|               |\  t |
    +   *    |   / |               | \   |
    +   *  n1|  /  |n3           n1|  \  |n3
    +   *    | /   |    after CW   |   \ |
    +   *    |/ oT |               | oT \|
    +   *    +-----+ oP            +-----+
    +   *       n4                    n4
    +   * 
    + */ + void RotateTrianglePair(Triangle& t, Point& p, Triangle& ot, Point& op); + + /** + * Fills holes in the Advancing Front + * + * + * @param tcx + * @param n + */ + void FillAdvancingFront(SweepContext& tcx, Node& n); + + // Decision-making about when to Fill hole. + // Contributed by ToolmakerSteve2 + bool LargeHole_DontFill(Node* node); + bool AngleExceeds90Degrees(Point* origin, Point* pa, Point* pb); + bool AngleExceedsPlus90DegreesOrIsNegative(Point* origin, Point* pa, Point* pb); + double Angle(Point& origin, Point& pa, Point& pb); + + /** + * + * @param node - middle node + * @return the angle between 3 front nodes + */ + double HoleAngle(Node& node); + + /** + * The basin angle is decided against the horizontal line [1,0] + */ + double BasinAngle(Node& node); + + /** + * Fills a basin that has formed on the Advancing Front to the right + * of given node.
    + * First we decide a left,bottom and right node that forms the + * boundaries of the basin. Then we do a reqursive fill. + * + * @param tcx + * @param node - starting node, this or next node will be left node + */ + void FillBasin(SweepContext& tcx, Node& node); + + /** + * Recursive algorithm to fill a Basin with triangles + * + * @param tcx + * @param node - bottom_node + * @param cnt - counter used to alternate on even and odd numbers + */ + void FillBasinReq(SweepContext& tcx, Node* node); + + bool IsShallow(SweepContext& tcx, Node& node); + + bool IsEdgeSideOfTriangle(Triangle& triangle, Point& ep, Point& eq); + + void FillEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void FillRightAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void FillRightBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillRightConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillRightConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillLeftAboveEdgeEvent(SweepContext& tcx, Edge* edge, Node* node); + + void FillLeftBelowEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillLeftConcaveEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FillLeftConvexEdgeEvent(SweepContext& tcx, Edge* edge, Node& node); + + void FlipEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* t, Point& p); + + /** + * After a flip we have two triangles and know that only one will still be + * intersecting the edge. So decide which to contiune with and legalize the other + * + * @param tcx + * @param o - should be the result of an orient2d( eq, op, ep ) + * @param t - triangle 1 + * @param ot - triangle 2 + * @param p - a point shared by both triangles + * @param op - another point shared by both triangles + * @return returns the triangle still intersecting the edge + */ + Triangle& NextFlipTriangle(SweepContext& tcx, int o, Triangle& t, Triangle& ot, Point& p, Point& op); + + /** + * When we need to traverse from one triangle to the next we need + * the point in current triangle that is the opposite point to the next + * triangle. + * + * @param ep + * @param eq + * @param ot + * @param op + * @return + */ + Point& NextFlipPoint(Point& ep, Point& eq, Triangle& ot, Point& op); + + /** + * Scan part of the FlipScan algorithm
    + * When a triangle pair isn't flippable we will scan for the next + * point that is inside the flip triangle scan area. When found + * we generate a new flipEdgeEvent + * + * @param tcx + * @param ep - last point on the edge we are traversing + * @param eq - first point on the edge we are traversing + * @param flipTriangle - the current triangle sharing the point eq with edge + * @param t + * @param p + */ + void FlipScanEdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle& flip_triangle, Triangle& t, Point& p); + + void FinalizationPolygon(SweepContext& tcx); + + std::vector nodes_; + +}; + +} + +#endif diff --git a/src/3rdparty/poly2tri/sweep/sweep_context.cpp b/src/3rdparty/poly2tri/sweep/sweep_context.cpp new file mode 100644 index 0000000..24dde11 --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/sweep_context.cpp @@ -0,0 +1,216 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "sweep_context.h" +#include +#include "advancing_front.h" + +namespace p2t { + +SweepContext::SweepContext(std::vector polyline) : + front_(0), + head_(0), + tail_(0), + af_head_(0), + af_middle_(0), + af_tail_(0) +{ + basin = Basin(); + edge_event = EdgeEvent(); + + points_ = polyline; + + InitEdges(points_); +} + +void SweepContext::AddHole(std::vector polyline) +{ + InitEdges(polyline); + for (unsigned int i = 0; i < polyline.size(); i++) { + points_.push_back(polyline[i]); + } +} + +void SweepContext::AddPoint(Point* point) { + points_.push_back(point); +} + +std::vector SweepContext::GetTriangles() +{ + return triangles_; +} + +std::list SweepContext::GetMap() +{ + return map_; +} + +void SweepContext::InitTriangulation() +{ + double xmax(points_[0]->x), xmin(points_[0]->x); + double ymax(points_[0]->y), ymin(points_[0]->y); + + // Calculate bounds. + for (unsigned int i = 0; i < points_.size(); i++) { + Point& p = *points_[i]; + if (p.x > xmax) + xmax = p.x; + if (p.x < xmin) + xmin = p.x; + if (p.y > ymax) + ymax = p.y; + if (p.y < ymin) + ymin = p.y; + } + + double dx = kAlpha * (xmax - xmin); + double dy = kAlpha * (ymax - ymin); + head_ = new Point(xmax + dx, ymin - dy); + tail_ = new Point(xmin - dx, ymin - dy); + + // Sort points along y-axis + std::sort(points_.begin(), points_.end(), cmp); + +} + +void SweepContext::InitEdges(std::vector polyline) +{ + int num_points = polyline.size(); + for (int i = 0; i < num_points; i++) { + int j = i < num_points - 1 ? i + 1 : 0; + edge_list.push_back(new Edge(*polyline[i], *polyline[j])); + } +} + +Point* SweepContext::GetPoint(const int& index) +{ + return points_[index]; +} + +void SweepContext::AddToMap(Triangle* triangle) +{ + map_.push_back(triangle); +} + +Node& SweepContext::LocateNode(Point& point) +{ + // TODO implement search tree + return *front_->LocateNode(point.x); +} + +void SweepContext::CreateAdvancingFront(std::vector nodes) +{ + + (void) nodes; + // Initial triangle + Triangle* triangle = new Triangle(*points_[0], *tail_, *head_); + + map_.push_back(triangle); + + af_head_ = new Node(*triangle->GetPoint(1), *triangle); + af_middle_ = new Node(*triangle->GetPoint(0), *triangle); + af_tail_ = new Node(*triangle->GetPoint(2)); + front_ = new AdvancingFront(*af_head_, *af_tail_); + + // TODO: More intuitive if head is middles next and not previous? + // so swap head and tail + af_head_->next = af_middle_; + af_middle_->next = af_tail_; + af_middle_->prev = af_head_; + af_tail_->prev = af_middle_; +} + +void SweepContext::RemoveNode(Node* node) +{ + delete node; +} + +void SweepContext::MapTriangleToNodes(Triangle& t) +{ + for (int i = 0; i < 3; i++) { + if (!t.GetNeighbor(i)) { + Node* n = front_->LocatePoint(t.PointCW(*t.GetPoint(i))); + if (n) + n->triangle = &t; + } + } +} + +void SweepContext::RemoveFromMap(Triangle* triangle) +{ + map_.remove(triangle); +} + +void SweepContext::MeshClean(Triangle& triangle) +{ + std::vector triangles; + triangles.push_back(&triangle); + + while(!triangles.empty()){ + Triangle *t = triangles.back(); + triangles.pop_back(); + + if (t != NULL && !t->IsInterior()) { + t->IsInterior(true); + triangles_.push_back(t); + for (int i = 0; i < 3; i++) { + if (!t->constrained_edge[i]) + triangles.push_back(t->GetNeighbor(i)); + } + } + } +} + +SweepContext::~SweepContext() +{ + + // Clean up memory + + delete head_; + delete tail_; + delete front_; + delete af_head_; + delete af_middle_; + delete af_tail_; + + typedef std::list type_list; + + for (type_list::iterator iter = map_.begin(); iter != map_.end(); ++iter) { + Triangle* ptr = *iter; + delete ptr; + } + + for (unsigned int i = 0; i < edge_list.size(); i++) { + delete edge_list[i]; + } + +} + +} diff --git a/src/3rdparty/poly2tri/sweep/sweep_context.h b/src/3rdparty/poly2tri/sweep/sweep_context.h new file mode 100644 index 0000000..c110a74 --- /dev/null +++ b/src/3rdparty/poly2tri/sweep/sweep_context.h @@ -0,0 +1,186 @@ +/* + * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors + * http://code.google.com/p/poly2tri/ + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Poly2Tri nor the names of its contributors may be + * used to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SWEEP_CONTEXT_H +#define SWEEP_CONTEXT_H + +#include +#include +#include + +namespace p2t { + +// Initial triangle factor, seed triangle will extend 30% of +// PointSet width to both left and right. +const double kAlpha = 0.3; + +struct Point; +class Triangle; +struct Node; +struct Edge; +class AdvancingFront; + +class SweepContext { +public: + +/// Constructor +SweepContext(std::vector polyline); +/// Destructor +~SweepContext(); + +void set_head(Point* p1); + +Point* head(); + +void set_tail(Point* p1); + +Point* tail(); + +int point_count(); + +Node& LocateNode(Point& point); + +void RemoveNode(Node* node); + +void CreateAdvancingFront(std::vector nodes); + +/// Try to map a node to all sides of this triangle that don't have a neighbor +void MapTriangleToNodes(Triangle& t); + +void AddToMap(Triangle* triangle); + +Point* GetPoint(const int& index); + +Point* GetPoints(); + +void RemoveFromMap(Triangle* triangle); + +void AddHole(std::vector polyline); + +void AddPoint(Point* point); + +AdvancingFront* front(); + +void MeshClean(Triangle& triangle); + +std::vector GetTriangles(); +std::list GetMap(); + +std::vector edge_list; + +struct Basin { + Node* left_node; + Node* bottom_node; + Node* right_node; + double width; + bool left_highest; + + Basin() : left_node(NULL), bottom_node(NULL), right_node(NULL), width(0.0), left_highest(false) + { + } + + void Clear() + { + left_node = NULL; + bottom_node = NULL; + right_node = NULL; + width = 0.0; + left_highest = false; + } +}; + +struct EdgeEvent { + Edge* constrained_edge; + bool right; + + EdgeEvent() : constrained_edge(NULL), right(false) + { + } +}; + +Basin basin; +EdgeEvent edge_event; + +private: + +friend class Sweep; + +std::vector triangles_; +std::list map_; +std::vector points_; + +// Advancing front +AdvancingFront* front_; +// head point used with advancing front +Point* head_; +// tail point used with advancing front +Point* tail_; + +Node *af_head_, *af_middle_, *af_tail_; + +void InitTriangulation(); +void InitEdges(std::vector polyline); + +}; + +inline AdvancingFront* SweepContext::front() +{ + return front_; +} + +inline int SweepContext::point_count() +{ + return int(points_.size()); +} + +inline void SweepContext::set_head(Point* p1) +{ + head_ = p1; +} + +inline Point* SweepContext::head() +{ + return head_; +} + +inline void SweepContext::set_tail(Point* p1) +{ + tail_ = p1; +} + +inline Point* SweepContext::tail() +{ + return tail_; +} + +} + +#endif diff --git a/src/3rdparty/zlib_dependency.pri b/src/3rdparty/zlib_dependency.pri new file mode 100644 index 0000000..2c714b3 --- /dev/null +++ b/src/3rdparty/zlib_dependency.pri @@ -0,0 +1,6 @@ +# zlib dependency satisfied by bundled 3rd party zlib or system zlib +qtConfig(system-zlib) { + QMAKE_USE_PRIVATE += zlib +} else { + QT_PRIVATE += zlib-private +} diff --git a/src/imports/imports.pro b/src/imports/imports.pro new file mode 100644 index 0000000..b640c79 --- /dev/null +++ b/src/imports/imports.pro @@ -0,0 +1,7 @@ +TEMPLATE = subdirs + +QT_FOR_CONFIG += location-private + +qtHaveModule(positioningquick): SUBDIRS += positioning +qtHaveModule(location): SUBDIRS += location +qtHaveModule(location):qtConfig(location-labs-plugin): SUBDIRS += locationlabs diff --git a/src/imports/location/location.cpp b/src/imports/location/location.cpp new file mode 100644 index 0000000..183d11d --- /dev/null +++ b/src/imports/location/location.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//Place includes +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_QtLocation); +#endif +} + +QT_BEGIN_NAMESPACE + + +class QtLocationDeclarativeModule: public QQmlExtensionPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid + FILE "plugin.json") + +public: + QtLocationDeclarativeModule(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + virtual void registerTypes(const char *uri) + { + if (QLatin1String(uri) == QLatin1String("QtLocation")) { + + // @uri QtLocation + int major = 5; + int minor = 0; + + // Register the 5.0 types + // 5.0 is silent and not advertised + + qmlRegisterType(uri, major, minor, "Plugin"); + qmlRegisterType(uri, major, minor, "PluginParameter"); + qmlRegisterUncreatableType(uri, major, minor, "PluginRequirements", + QStringLiteral("PluginRequirements is not intended instantiable by developer.")); + qmlRegisterType(uri, major, minor, "Map"); + + qmlRegisterUncreatableType(uri, major, minor, "GeoMapItemBase", + QStringLiteral("GeoMapItemBase is not intended instantiable by developer.")); + qmlRegisterType(uri, major, minor, "MapQuickItem"); + qmlRegisterType(uri, major, minor, "MapItemView"); + + qmlRegisterType(uri, major, minor, "GeocodeModel"); // geocoding and reverse geocoding + qmlRegisterType(uri, major, minor, "RouteModel"); + qmlRegisterType(uri, major, minor, "RouteQuery"); + qmlRegisterType(uri, major, minor, "Route"); // data type + qmlRegisterType(uri, major, minor, "RouteSegment"); + qmlRegisterType(uri, major, minor, "RouteManeuver"); + qmlRegisterUncreatableType(uri, major, minor, "MapPinchEvent", + QStringLiteral("(Map)PinchEvent is not intended instantiable by developer.")); + qmlRegisterUncreatableType(uri, major, minor, "MapGestureArea", + QStringLiteral("(Map)GestureArea is not intended instantiable by developer.")); + qmlRegisterUncreatableType(uri, major, minor, "MapType", + QStringLiteral("MapType is not intended instantiable by developer.")); + qmlRegisterType(uri, major, minor, "Category"); + qmlRegisterType(uri, major, minor, "EditorialModel"); + qmlRegisterType(uri, major, minor, "ImageModel"); + qmlRegisterType(uri, major, minor, "Place"); + qmlRegisterType(uri, major, minor, "Icon"); + qmlRegisterType(uri, major, minor, "Ratings"); + qmlRegisterType(uri, major, minor, "ReviewModel"); + qmlRegisterType(uri, major, minor, "Supplier"); + qmlRegisterType(uri, major, minor, "User"); + qmlRegisterType(uri, major, minor, "MapRectangle"); + qmlRegisterType(uri, major, minor, "MapCircle"); + qmlRegisterType(); + qmlRegisterType(uri, major, minor, "MapPolyline"); + qmlRegisterType(uri, major, minor, "MapPolygon"); + qmlRegisterType(uri, major, minor, "MapRoute"); + + qmlRegisterType(uri, major, minor, "CategoryModel"); + qmlRegisterType(uri, major, minor, "PlaceSearchModel"); + qmlRegisterType(uri, major, minor, "PlaceSearchSuggestionModel"); + qmlRegisterType(uri, major, minor, "PlaceAttribute"); + qmlRegisterUncreatableType(uri, major, minor, "ExtendedAttributes", "ExtendedAttributes instances cannot be instantiated. " + "Only Place types have ExtendedAttributes and they cannot be re-assigned " + "(but can be modified)."); + qmlRegisterType(uri, major, minor, "ContactDetail"); + qmlRegisterUncreatableType(uri, major, minor, "ContactDetails", "ContactDetails instances cannot be instantiated. " + "Only Place types have ContactDetails and they cannot " + "be re-assigned (but can be modified)."); + + // Introduction of 5.3 version; existing 5.0 exports automatically become available under 5.3 as well + // 5.3 is committed QML API despite missing release of QtLocation 5.3 + + minor = 5; + //TODO: this is broken QTBUG-50990 + qmlRegisterUncreatableType(uri, major, minor, "MapType", + QStringLiteral("MapType is not intended instantiable by developer.")); + minor = 6; + //TODO: this is broken QTBUG-50990 + qmlRegisterUncreatableType(uri, major, minor, "MapGestureArea", + QStringLiteral("(Map)GestureArea is not intended instantiable by developer.")); + + // Register the 5.8 types + minor = 8; + qmlRegisterType(uri, major, minor, "RouteManeuver"); + + // Register the 5.9 types + minor = 9; + qmlRegisterType(uri, major, minor, "MapParameter"); + qmlRegisterType(uri, major, minor, "MapCopyrightNotice"); + qmlRegisterType(uri, major, minor, "MapItemGroup"); + + // Register the 5.10 types + minor = 10; + qmlRegisterUncreatableType(uri, major, minor, "CameraCapabilities" + , QStringLiteral("CameraCapabilities is not intended instantiable by developer.")); + + // Register the 5.11 types + minor = 11; + qmlRegisterType(uri, major, minor, "RouteManeuver"); + qmlRegisterType(uri, major, minor, "Map"); + qmlRegisterUncreatableType(uri, major, minor, "GeoMapItemBase", + QStringLiteral("GeoMapItemBase is not intended instantiable by developer.")); + qmlRegisterType(uri, major, minor, "DynamicParameter"); + qmlRegisterType(uri, major, minor, "Route"); + qmlRegisterType(uri, major, minor, "RouteQuery"); + qmlRegisterType(uri, major, minor, "Plugin"); + qmlRegisterType(uri, major, minor, "Waypoint"); + + // Register the latest Qt version as QML type version + qmlRegisterModule(uri, QT_VERSION_MAJOR, QT_VERSION_MINOR); + + //registrations below are version independent + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + qRegisterMetaType(); + } else { + qDebug() << "Unsupported URI given to load location QML plugin: " << QLatin1String(uri); + } + } +}; + +#include "location.moc" + +QT_END_NAMESPACE diff --git a/src/imports/location/location.pro b/src/imports/location/location.pro new file mode 100644 index 0000000..dec1149 --- /dev/null +++ b/src/imports/location/location.pro @@ -0,0 +1,10 @@ +QT += quick-private network positioning-private location-private qml-private core-private gui-private + +SOURCES += \ + location.cpp + +load(qml_plugin) + +OTHER_FILES += \ + plugin.json \ + qmldir diff --git a/src/imports/location/plugin.json b/src/imports/location/plugin.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/src/imports/location/plugin.json @@ -0,0 +1,2 @@ +{ +} diff --git a/src/imports/location/plugins.qmltypes b/src/imports/location/plugins.qmltypes new file mode 100644 index 0000000..d2d9030 --- /dev/null +++ b/src/imports/location/plugins.qmltypes @@ -0,0 +1,1476 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable QtLocation 5.11' + +Module { + dependencies: ["QtQuick 2.8"] + Component { + name: "QDeclarativeCategory" + prototype: "QObject" + exports: ["QtLocation/Category 5.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "Visibility" + values: { + "UnspecifiedVisibility": 0, + "DeviceVisibility": 1, + "PrivateVisibility": 2, + "PublicVisibility": 4 + } + } + Enum { + name: "Status" + values: { + "Ready": 0, + "Saving": 1, + "Removing": 2, + "Error": 3 + } + } + Property { name: "category"; type: "QPlaceCategory" } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "categoryId"; type: "string" } + Property { name: "name"; type: "string" } + Property { name: "visibility"; type: "Visibility" } + Property { name: "icon"; type: "QDeclarativePlaceIcon"; isPointer: true } + Property { name: "status"; type: "Status"; isReadonly: true } + Method { name: "errorString"; type: "string" } + Method { + name: "save" + Parameter { name: "parentId"; type: "string" } + } + Method { name: "save" } + Method { name: "remove" } + } + Component { + name: "QDeclarativeCircleMapItem" + defaultProperty: "data" + prototype: "QDeclarativeGeoMapItemBase" + exports: ["QtLocation/MapCircle 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "center"; type: "QGeoCoordinate" } + Property { name: "radius"; type: "double" } + Property { name: "color"; type: "QColor" } + Property { + name: "border" + type: "QDeclarativeMapLineProperties" + isReadonly: true + isPointer: true + } + Signal { + name: "centerChanged" + Parameter { name: "center"; type: "QGeoCoordinate" } + } + Signal { + name: "radiusChanged" + Parameter { name: "radius"; type: "double" } + } + Signal { + name: "colorChanged" + Parameter { name: "color"; type: "QColor" } + } + } + Component { + name: "QDeclarativeContactDetail" + prototype: "QObject" + exports: ["QtLocation/ContactDetail 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "contactDetail"; type: "QPlaceContactDetail" } + Property { name: "label"; type: "string" } + Property { name: "value"; type: "string" } + } + Component { + name: "QDeclarativeContactDetails" + prototype: "QQmlPropertyMap" + exports: ["QtLocation/ContactDetails 5.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + } + Component { + name: "QDeclarativeGeoCameraCapabilities" + prototype: "QObject" + exports: ["QtLocation/CameraCapabilities 5.10"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "minimumZoomLevel"; type: "double"; isReadonly: true } + Property { name: "maximumZoomLevel"; type: "double"; isReadonly: true } + Property { name: "minimumTilt"; type: "double"; isReadonly: true } + Property { name: "maximumTilt"; type: "double"; isReadonly: true } + Property { name: "minimumFieldOfView"; type: "double"; isReadonly: true } + Property { name: "maximumFieldOfView"; type: "double"; isReadonly: true } + } + Component { + name: "QDeclarativeGeoManeuver" + prototype: "QObject" + exports: [ + "QtLocation/RouteManeuver 5.0", + "QtLocation/RouteManeuver 5.11", + "QtLocation/RouteManeuver 5.8" + ] + exportMetaObjectRevisions: [0, 11, 0] + Enum { + name: "Direction" + values: { + "NoDirection": 0, + "DirectionForward": 1, + "DirectionBearRight": 2, + "DirectionLightRight": 3, + "DirectionRight": 4, + "DirectionHardRight": 5, + "DirectionUTurnRight": 6, + "DirectionUTurnLeft": 7, + "DirectionHardLeft": 8, + "DirectionLeft": 9, + "DirectionLightLeft": 10, + "DirectionBearLeft": 11 + } + } + Property { name: "valid"; type: "bool"; isReadonly: true } + Property { name: "position"; type: "QGeoCoordinate"; isReadonly: true } + Property { name: "instructionText"; type: "string"; isReadonly: true } + Property { name: "direction"; type: "Direction"; isReadonly: true } + Property { name: "timeToNextInstruction"; type: "int"; isReadonly: true } + Property { name: "distanceToNextInstruction"; type: "double"; isReadonly: true } + Property { name: "waypoint"; type: "QGeoCoordinate"; isReadonly: true } + Property { name: "waypointValid"; type: "bool"; isReadonly: true } + Property { + name: "extendedAttributes" + revision: 11 + type: "QObject" + isReadonly: true + isPointer: true + } + } + Component { + name: "QDeclarativeGeoMap" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["QtLocation/Map 5.0", "QtLocation/Map 5.11"] + exportMetaObjectRevisions: [0, 11] + Property { name: "gesture"; type: "QQuickGeoMapGestureArea"; isReadonly: true; isPointer: true } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "minimumZoomLevel"; type: "double" } + Property { name: "maximumZoomLevel"; type: "double" } + Property { name: "zoomLevel"; type: "double" } + Property { name: "tilt"; type: "double" } + Property { name: "minimumTilt"; type: "double" } + Property { name: "maximumTilt"; type: "double" } + Property { name: "bearing"; type: "double" } + Property { name: "fieldOfView"; type: "double" } + Property { name: "minimumFieldOfView"; type: "double" } + Property { name: "maximumFieldOfView"; type: "double" } + Property { name: "activeMapType"; type: "QDeclarativeGeoMapType"; isPointer: true } + Property { + name: "supportedMapTypes" + type: "QDeclarativeGeoMapType" + isList: true + isReadonly: true + } + Property { name: "center"; type: "QGeoCoordinate" } + Property { name: "mapItems"; type: "QList"; isReadonly: true } + Property { name: "mapParameters"; type: "QList"; isReadonly: true } + Property { name: "error"; type: "QGeoServiceProvider::Error"; isReadonly: true } + Property { name: "errorString"; type: "string"; isReadonly: true } + Property { name: "visibleRegion"; type: "QGeoShape" } + Property { name: "copyrightsVisible"; type: "bool" } + Property { name: "color"; type: "QColor" } + Property { name: "mapReady"; type: "bool"; isReadonly: true } + Signal { + name: "pluginChanged" + Parameter { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + } + Signal { + name: "zoomLevelChanged" + Parameter { name: "zoomLevel"; type: "double" } + } + Signal { + name: "centerChanged" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Signal { + name: "copyrightLinkActivated" + Parameter { name: "link"; type: "string" } + } + Signal { + name: "copyrightsVisibleChanged" + Parameter { name: "visible"; type: "bool" } + } + Signal { + name: "colorChanged" + Parameter { name: "color"; type: "QColor" } + } + Signal { + name: "bearingChanged" + Parameter { name: "bearing"; type: "double" } + } + Signal { + name: "tiltChanged" + Parameter { name: "tilt"; type: "double" } + } + Signal { + name: "fieldOfViewChanged" + Parameter { name: "fieldOfView"; type: "double" } + } + Signal { + name: "minimumTiltChanged" + Parameter { name: "minimumTilt"; type: "double" } + } + Signal { + name: "maximumTiltChanged" + Parameter { name: "maximumTilt"; type: "double" } + } + Signal { + name: "minimumFieldOfViewChanged" + Parameter { name: "minimumFieldOfView"; type: "double" } + } + Signal { + name: "maximumFieldOfViewChanged" + Parameter { name: "maximumFieldOfView"; type: "double" } + } + Signal { + name: "copyrightsChanged" + Parameter { name: "copyrightsImage"; type: "QImage" } + } + Signal { + name: "copyrightsChanged" + Parameter { name: "copyrightsHtml"; type: "string" } + } + Signal { + name: "mapReadyChanged" + Parameter { name: "ready"; type: "bool" } + } + Signal { name: "mapObjectsChanged"; revision: 11 } + Method { + name: "setBearing" + Parameter { name: "bearing"; type: "double" } + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { + name: "alignCoordinateToPoint" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + Parameter { name: "point"; type: "QPointF" } + } + Method { + name: "removeMapItem" + Parameter { name: "item"; type: "QDeclarativeGeoMapItemBase"; isPointer: true } + } + Method { + name: "addMapItem" + Parameter { name: "item"; type: "QDeclarativeGeoMapItemBase"; isPointer: true } + } + Method { + name: "addMapItemGroup" + Parameter { name: "itemGroup"; type: "QDeclarativeGeoMapItemGroup"; isPointer: true } + } + Method { + name: "removeMapItemGroup" + Parameter { name: "itemGroup"; type: "QDeclarativeGeoMapItemGroup"; isPointer: true } + } + Method { + name: "removeMapItemView" + Parameter { name: "itemView"; type: "QDeclarativeGeoMapItemView"; isPointer: true } + } + Method { + name: "addMapItemView" + Parameter { name: "itemView"; type: "QDeclarativeGeoMapItemView"; isPointer: true } + } + Method { name: "clearMapItems" } + Method { + name: "addMapParameter" + Parameter { name: "parameter"; type: "QDeclarativeGeoMapParameter"; isPointer: true } + } + Method { + name: "removeMapParameter" + Parameter { name: "parameter"; type: "QDeclarativeGeoMapParameter"; isPointer: true } + } + Method { name: "clearMapParameters" } + Method { + name: "toCoordinate" + type: "QGeoCoordinate" + Parameter { name: "position"; type: "QPointF" } + Parameter { name: "clipToViewPort"; type: "bool" } + } + Method { + name: "toCoordinate" + type: "QGeoCoordinate" + Parameter { name: "position"; type: "QPointF" } + } + Method { + name: "fromCoordinate" + type: "QPointF" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + Parameter { name: "clipToViewPort"; type: "bool" } + } + Method { + name: "fromCoordinate" + type: "QPointF" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { name: "fitViewportToMapItems" } + Method { name: "fitViewportToVisibleMapItems" } + Method { + name: "pan" + Parameter { name: "dx"; type: "int" } + Parameter { name: "dy"; type: "int" } + } + Method { name: "prefetchData" } + Method { name: "clearData" } + } + Component { + name: "QDeclarativeGeoMapCopyrightNotice" + defaultProperty: "data" + prototype: "QQuickPaintedItem" + exports: ["QtLocation/MapCopyrightNotice 5.9"] + exportMetaObjectRevisions: [0] + Property { name: "mapSource"; type: "QDeclarativeGeoMap"; isPointer: true } + Property { name: "styleSheet"; type: "string" } + Signal { + name: "linkActivated" + Parameter { name: "link"; type: "string" } + } + Signal { + name: "backgroundColorChanged" + Parameter { name: "color"; type: "QColor" } + } + Signal { + name: "styleSheetChanged" + Parameter { name: "styleSheet"; type: "string" } + } + Signal { name: "copyrightsVisibleChanged" } + Method { + name: "copyrightsChanged" + Parameter { name: "copyrightsImage"; type: "QImage" } + } + Method { + name: "copyrightsChanged" + Parameter { name: "copyrightsHtml"; type: "string" } + } + Method { + name: "onCopyrightsStyleSheetChanged" + Parameter { name: "styleSheet"; type: "string" } + } + } + Component { + name: "QDeclarativeGeoMapItemBase" + defaultProperty: "data" + prototype: "QQuickItem" + exports: [ + "QtLocation/GeoMapItemBase 5.0", + "QtLocation/GeoMapItemBase 5.11" + ] + isCreatable: false + exportMetaObjectRevisions: [0, 11] + Property { name: "geoShape"; type: "QGeoShape"; isReadonly: true } + Signal { name: "mapItemOpacityChanged" } + Signal { name: "enterTransitionFinished"; revision: 11 } + Signal { name: "exitTransitionFinished"; revision: 11 } + } + Component { + name: "QDeclarativeGeoMapItemGroup" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["QtLocation/MapItemGroup 5.9"] + exportMetaObjectRevisions: [0] + } + Component { + name: "QDeclarativeGeoMapItemView" + prototype: "QObject" + exports: ["QtLocation/MapItemView 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "model"; type: "QVariant" } + Property { name: "delegate"; type: "QQmlComponent"; isPointer: true } + Property { name: "autoFitViewport"; type: "bool" } + } + Component { + name: "QDeclarativeGeoMapParameter" + prototype: "QGeoMapParameter" + exports: [ + "QtLocation/DynamicParameter 5.11", + "QtLocation/MapParameter 5.9" + ] + exportMetaObjectRevisions: [0, 0] + Signal { + name: "completed" + Parameter { type: "QDeclarativeGeoMapParameter"; isPointer: true } + } + } + Component { + name: "QDeclarativeGeoMapQuickItem" + defaultProperty: "data" + prototype: "QDeclarativeGeoMapItemBase" + exports: ["QtLocation/MapQuickItem 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "coordinate"; type: "QGeoCoordinate" } + Property { name: "anchorPoint"; type: "QPointF" } + Property { name: "zoomLevel"; type: "double" } + Property { name: "sourceItem"; type: "QQuickItem"; isPointer: true } + } + Component { + name: "QDeclarativeGeoMapType" + prototype: "QObject" + exports: ["QtLocation/MapType 5.0", "QtLocation/MapType 5.5"] + isCreatable: false + exportMetaObjectRevisions: [0, 1] + Enum { + name: "MapStyle" + values: { + "NoMap": 0, + "StreetMap": 1, + "SatelliteMapDay": 2, + "SatelliteMapNight": 3, + "TerrainMap": 4, + "HybridMap": 5, + "TransitMap": 6, + "GrayStreetMap": 7, + "PedestrianMap": 8, + "CarNavigationMap": 9, + "CycleMap": 10, + "CustomMap": 100 + } + } + Property { name: "style"; type: "MapStyle"; isReadonly: true } + Property { name: "name"; type: "string"; isReadonly: true } + Property { name: "description"; type: "string"; isReadonly: true } + Property { name: "mobile"; type: "bool"; isReadonly: true } + Property { name: "night"; revision: 1; type: "bool"; isReadonly: true } + Property { + name: "cameraCapabilities" + type: "QDeclarativeGeoCameraCapabilities" + isReadonly: true + isPointer: true + } + Property { name: "metadata"; type: "QVariantMap"; isReadonly: true } + } + Component { + name: "QDeclarativeGeoRoute" + prototype: "QObject" + exports: ["QtLocation/Route 5.0", "QtLocation/Route 5.11"] + exportMetaObjectRevisions: [0, 11] + Property { name: "bounds"; type: "QGeoRectangle"; isReadonly: true } + Property { name: "travelTime"; type: "int"; isReadonly: true } + Property { name: "distance"; type: "double"; isReadonly: true } + Property { name: "path"; type: "QJSValue" } + Property { name: "segments"; type: "QDeclarativeGeoRouteSegment"; isList: true; isReadonly: true } + Property { + name: "routeQuery" + revision: 11 + type: "QDeclarativeGeoRouteQuery" + isReadonly: true + isPointer: true + } + } + Component { + name: "QDeclarativeGeoRouteModel" + prototype: "QAbstractListModel" + exports: ["QtLocation/RouteModel 5.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Loading": 2, + "Error": 3 + } + } + Enum { + name: "RouteError" + values: { + "NoError": 0, + "EngineNotSetError": 1, + "CommunicationError": 2, + "ParseError": 3, + "UnsupportedOptionError": 4, + "UnknownError": 5, + "UnknownParameterError": 100, + "MissingRequiredParameterError": 101 + } + } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "query"; type: "QDeclarativeGeoRouteQuery"; isPointer: true } + Property { name: "count"; type: "int"; isReadonly: true } + Property { name: "autoUpdate"; type: "bool" } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "errorString"; type: "string"; isReadonly: true } + Property { name: "error"; type: "RouteError"; isReadonly: true } + Property { name: "measurementSystem"; type: "QLocale::MeasurementSystem" } + Signal { name: "routesChanged" } + Signal { name: "abortRequested" } + Method { name: "update" } + Method { + name: "get" + type: "QDeclarativeGeoRoute*" + Parameter { name: "index"; type: "int" } + } + Method { name: "reset" } + Method { name: "cancel" } + } + Component { + name: "QDeclarativeGeoRouteQuery" + defaultProperty: "quickChildren" + prototype: "QObject" + exports: ["QtLocation/RouteQuery 5.0", "QtLocation/RouteQuery 5.11"] + exportMetaObjectRevisions: [0, 11] + Enum { + name: "TravelMode" + values: { + "CarTravel": 1, + "PedestrianTravel": 2, + "BicycleTravel": 4, + "PublicTransitTravel": 8, + "TruckTravel": 16 + } + } + Enum { + name: "TravelModes" + values: { + "CarTravel": 1, + "PedestrianTravel": 2, + "BicycleTravel": 4, + "PublicTransitTravel": 8, + "TruckTravel": 16 + } + } + Enum { + name: "FeatureType" + values: { + "NoFeature": 0, + "TollFeature": 1, + "HighwayFeature": 2, + "PublicTransitFeature": 4, + "FerryFeature": 8, + "TunnelFeature": 16, + "DirtRoadFeature": 32, + "ParksFeature": 64, + "MotorPoolLaneFeature": 128, + "TrafficFeature": 256 + } + } + Enum { + name: "FeatureWeight" + values: { + "NeutralFeatureWeight": 0, + "PreferFeatureWeight": 1, + "RequireFeatureWeight": 2, + "AvoidFeatureWeight": 4, + "DisallowFeatureWeight": 8 + } + } + Enum { + name: "RouteOptimization" + values: { + "ShortestRoute": 1, + "FastestRoute": 2, + "MostEconomicRoute": 4, + "MostScenicRoute": 8 + } + } + Enum { + name: "RouteOptimizations" + values: { + "ShortestRoute": 1, + "FastestRoute": 2, + "MostEconomicRoute": 4, + "MostScenicRoute": 8 + } + } + Enum { + name: "SegmentDetail" + values: { + "NoSegmentData": 0, + "BasicSegmentData": 1 + } + } + Enum { + name: "SegmentDetails" + values: { + "NoSegmentData": 0, + "BasicSegmentData": 1 + } + } + Enum { + name: "ManeuverDetail" + values: { + "NoManeuvers": 0, + "BasicManeuvers": 1 + } + } + Enum { + name: "ManeuverDetails" + values: { + "NoManeuvers": 0, + "BasicManeuvers": 1 + } + } + Property { name: "numberAlternativeRoutes"; type: "int" } + Property { name: "travelModes"; type: "TravelModes" } + Property { name: "routeOptimizations"; type: "RouteOptimizations" } + Property { name: "segmentDetail"; type: "SegmentDetail" } + Property { name: "maneuverDetail"; type: "ManeuverDetail" } + Property { name: "waypoints"; type: "QVariantList" } + Property { name: "excludedAreas"; type: "QJSValue" } + Property { name: "featureTypes"; type: "QList"; isReadonly: true } + Property { name: "extraParameters"; revision: 11; type: "QVariantMap"; isReadonly: true } + Property { name: "quickChildren"; type: "QObject"; isList: true; isReadonly: true } + Signal { name: "queryDetailsChanged" } + Signal { name: "extraParametersChanged"; revision: 11 } + Method { name: "waypointObjects"; type: "QVariantList" } + Method { + name: "addWaypoint" + Parameter { name: "w"; type: "QVariant" } + } + Method { + name: "removeWaypoint" + Parameter { name: "waypoint"; type: "QVariant" } + } + Method { name: "clearWaypoints" } + Method { + name: "addExcludedArea" + Parameter { name: "area"; type: "QGeoRectangle" } + } + Method { + name: "removeExcludedArea" + Parameter { name: "area"; type: "QGeoRectangle" } + } + Method { name: "clearExcludedAreas" } + Method { + name: "setFeatureWeight" + Parameter { name: "featureType"; type: "FeatureType" } + Parameter { name: "featureWeight"; type: "FeatureWeight" } + } + Method { + name: "featureWeight" + type: "int" + Parameter { name: "featureType"; type: "FeatureType" } + } + Method { name: "resetFeatureWeights" } + } + Component { + name: "QDeclarativeGeoRouteSegment" + prototype: "QObject" + exports: ["QtLocation/RouteSegment 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "travelTime"; type: "int"; isReadonly: true } + Property { name: "distance"; type: "double"; isReadonly: true } + Property { name: "path"; type: "QJSValue"; isReadonly: true } + Property { name: "maneuver"; type: "QDeclarativeGeoManeuver"; isReadonly: true; isPointer: true } + } + Component { + name: "QDeclarativeGeoServiceProvider" + defaultProperty: "parameters" + prototype: "QObject" + exports: ["QtLocation/Plugin 5.0", "QtLocation/Plugin 5.11"] + exportMetaObjectRevisions: [0, 11] + Enum { + name: "RoutingFeature" + values: { + "NoRoutingFeatures": 0, + "OnlineRoutingFeature": 1, + "OfflineRoutingFeature": 2, + "LocalizedRoutingFeature": 4, + "RouteUpdatesFeature": 8, + "AlternativeRoutesFeature": 16, + "ExcludeAreasRoutingFeature": 32, + "AnyRoutingFeatures": -1 + } + } + Enum { + name: "RoutingFeatures" + values: { + "NoRoutingFeatures": 0, + "OnlineRoutingFeature": 1, + "OfflineRoutingFeature": 2, + "LocalizedRoutingFeature": 4, + "RouteUpdatesFeature": 8, + "AlternativeRoutesFeature": 16, + "ExcludeAreasRoutingFeature": 32, + "AnyRoutingFeatures": -1 + } + } + Enum { + name: "GeocodingFeature" + values: { + "NoGeocodingFeatures": 0, + "OnlineGeocodingFeature": 1, + "OfflineGeocodingFeature": 2, + "ReverseGeocodingFeature": 4, + "LocalizedGeocodingFeature": 8, + "AnyGeocodingFeatures": -1 + } + } + Enum { + name: "GeocodingFeatures" + values: { + "NoGeocodingFeatures": 0, + "OnlineGeocodingFeature": 1, + "OfflineGeocodingFeature": 2, + "ReverseGeocodingFeature": 4, + "LocalizedGeocodingFeature": 8, + "AnyGeocodingFeatures": -1 + } + } + Enum { + name: "MappingFeature" + values: { + "NoMappingFeatures": 0, + "OnlineMappingFeature": 1, + "OfflineMappingFeature": 2, + "LocalizedMappingFeature": 4, + "AnyMappingFeatures": -1 + } + } + Enum { + name: "MappingFeatures" + values: { + "NoMappingFeatures": 0, + "OnlineMappingFeature": 1, + "OfflineMappingFeature": 2, + "LocalizedMappingFeature": 4, + "AnyMappingFeatures": -1 + } + } + Enum { + name: "PlacesFeature" + values: { + "NoPlacesFeatures": 0, + "OnlinePlacesFeature": 1, + "OfflinePlacesFeature": 2, + "SavePlaceFeature": 4, + "RemovePlaceFeature": 8, + "SaveCategoryFeature": 16, + "RemoveCategoryFeature": 32, + "PlaceRecommendationsFeature": 64, + "SearchSuggestionsFeature": 128, + "LocalizedPlacesFeature": 256, + "NotificationsFeature": 512, + "PlaceMatchingFeature": 1024, + "AnyPlacesFeatures": -1 + } + } + Enum { + name: "PlacesFeatures" + values: { + "NoPlacesFeatures": 0, + "OnlinePlacesFeature": 1, + "OfflinePlacesFeature": 2, + "SavePlaceFeature": 4, + "RemovePlaceFeature": 8, + "SaveCategoryFeature": 16, + "RemoveCategoryFeature": 32, + "PlaceRecommendationsFeature": 64, + "SearchSuggestionsFeature": 128, + "LocalizedPlacesFeature": 256, + "NotificationsFeature": 512, + "PlaceMatchingFeature": 1024, + "AnyPlacesFeatures": -1 + } + } + Enum { + name: "NavigationFeatures" + values: { + "NoNavigationFeatures": 0, + "OnlineNavigationFeature": 1, + "OfflineNavigationFeature": 2, + "AnyNavigationFeatures": -1 + } + } + Property { name: "name"; type: "string" } + Property { name: "availableServiceProviders"; type: "QStringList"; isReadonly: true } + Property { + name: "parameters" + type: "QDeclarativeGeoServiceProviderParameter" + isList: true + isReadonly: true + } + Property { + name: "required" + type: "QDeclarativeGeoServiceProviderRequirements" + isPointer: true + } + Property { name: "locales"; type: "QStringList" } + Property { name: "preferred"; type: "QStringList" } + Property { name: "allowExperimental"; type: "bool" } + Property { name: "isAttached"; type: "bool"; isReadonly: true } + Signal { + name: "nameChanged" + Parameter { name: "name"; type: "string" } + } + Signal { name: "attached" } + Signal { + name: "preferredChanged" + Parameter { name: "preferences"; type: "QStringList" } + } + Signal { + name: "allowExperimentalChanged" + Parameter { name: "allow"; type: "bool" } + } + Method { + name: "supportsRouting" + type: "bool" + Parameter { name: "feature"; type: "RoutingFeatures" } + } + Method { name: "supportsRouting"; type: "bool" } + Method { + name: "supportsGeocoding" + type: "bool" + Parameter { name: "feature"; type: "GeocodingFeatures" } + } + Method { name: "supportsGeocoding"; type: "bool" } + Method { + name: "supportsMapping" + type: "bool" + Parameter { name: "feature"; type: "MappingFeatures" } + } + Method { name: "supportsMapping"; type: "bool" } + Method { + name: "supportsPlaces" + type: "bool" + Parameter { name: "feature"; type: "PlacesFeatures" } + } + Method { name: "supportsPlaces"; type: "bool" } + Method { + name: "supportsNavigation" + revision: 11 + type: "bool" + Parameter { name: "feature"; type: "NavigationFeature" } + } + Method { name: "supportsNavigation"; revision: 11; type: "bool" } + } + Component { + name: "QDeclarativeGeoServiceProviderParameter" + prototype: "QObject" + exports: ["QtLocation/PluginParameter 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "name"; type: "string" } + Property { name: "value"; type: "QVariant" } + Signal { + name: "nameChanged" + Parameter { name: "name"; type: "string" } + } + Signal { + name: "valueChanged" + Parameter { name: "value"; type: "QVariant" } + } + Signal { name: "initialized" } + } + Component { + name: "QDeclarativeGeoServiceProviderRequirements" + prototype: "QObject" + exports: ["QtLocation/PluginRequirements 5.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "mapping"; type: "QDeclarativeGeoServiceProvider::MappingFeatures" } + Property { name: "routing"; type: "QDeclarativeGeoServiceProvider::RoutingFeatures" } + Property { name: "geocoding"; type: "QDeclarativeGeoServiceProvider::GeocodingFeatures" } + Property { name: "places"; type: "QDeclarativeGeoServiceProvider::PlacesFeatures" } + Signal { + name: "mappingRequirementsChanged" + Parameter { name: "features"; type: "QDeclarativeGeoServiceProvider::MappingFeatures" } + } + Signal { + name: "routingRequirementsChanged" + Parameter { name: "features"; type: "QDeclarativeGeoServiceProvider::RoutingFeatures" } + } + Signal { + name: "geocodingRequirementsChanged" + Parameter { name: "features"; type: "QDeclarativeGeoServiceProvider::GeocodingFeatures" } + } + Signal { + name: "placesRequirementsChanged" + Parameter { name: "features"; type: "QDeclarativeGeoServiceProvider::PlacesFeatures" } + } + Signal { name: "requirementsChanged" } + Method { + name: "matches" + type: "bool" + Parameter { name: "provider"; type: "const QGeoServiceProvider"; isPointer: true } + } + } + Component { + name: "QDeclarativeGeoWaypoint" + defaultProperty: "quickChildren" + prototype: "QGeoCoordinateObject" + exports: ["QtLocation/Waypoint 5.11"] + exportMetaObjectRevisions: [0] + Property { name: "latitude"; type: "double" } + Property { name: "longitude"; type: "double" } + Property { name: "altitude"; type: "double" } + Property { name: "isValid"; type: "bool"; isReadonly: true } + Property { name: "bearing"; type: "double" } + Property { name: "metadata"; type: "QVariantMap"; isReadonly: true } + Property { name: "quickChildren"; type: "QObject"; isList: true; isReadonly: true } + Signal { name: "completed" } + Signal { name: "waypointDetailsChanged" } + Signal { name: "extraParametersChanged" } + } + Component { + name: "QDeclarativeGeocodeModel" + prototype: "QAbstractListModel" + exports: ["QtLocation/GeocodeModel 5.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Loading": 2, + "Error": 3 + } + } + Enum { + name: "GeocodeError" + values: { + "NoError": 0, + "EngineNotSetError": 1, + "CommunicationError": 2, + "ParseError": 3, + "UnsupportedOptionError": 4, + "CombinationError": 5, + "UnknownError": 6, + "UnknownParameterError": 100, + "MissingRequiredParameterError": 101 + } + } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "autoUpdate"; type: "bool" } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "errorString"; type: "string"; isReadonly: true } + Property { name: "count"; type: "int"; isReadonly: true } + Property { name: "limit"; type: "int" } + Property { name: "offset"; type: "int" } + Property { name: "query"; type: "QVariant" } + Property { name: "bounds"; type: "QVariant" } + Property { name: "error"; type: "GeocodeError"; isReadonly: true } + Signal { name: "locationsChanged" } + Method { name: "update" } + Method { + name: "get" + type: "QDeclarativeGeoLocation*" + Parameter { name: "index"; type: "int" } + } + Method { name: "reset" } + Method { name: "cancel" } + } + Component { + name: "QDeclarativeMapLineProperties" + prototype: "QObject" + Property { name: "width"; type: "double" } + Property { name: "color"; type: "QColor" } + Signal { + name: "widthChanged" + Parameter { name: "width"; type: "double" } + } + Signal { + name: "colorChanged" + Parameter { name: "color"; type: "QColor" } + } + } + Component { + name: "QDeclarativePlace" + prototype: "QObject" + exports: ["QtLocation/Place 5.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "Status" + values: { + "Ready": 0, + "Saving": 1, + "Fetching": 2, + "Removing": 3, + "Error": 4 + } + } + Enum { + name: "Visibility" + values: { + "UnspecifiedVisibility": 0, + "DeviceVisibility": 1, + "PrivateVisibility": 2, + "PublicVisibility": 4 + } + } + Property { name: "place"; type: "QPlace" } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "categories"; type: "QDeclarativeCategory"; isList: true; isReadonly: true } + Property { name: "location"; type: "QDeclarativeGeoLocation"; isPointer: true } + Property { name: "ratings"; type: "QDeclarativeRatings"; isPointer: true } + Property { name: "supplier"; type: "QDeclarativeSupplier"; isPointer: true } + Property { name: "icon"; type: "QDeclarativePlaceIcon"; isPointer: true } + Property { name: "name"; type: "string" } + Property { name: "placeId"; type: "string" } + Property { name: "attribution"; type: "string" } + Property { + name: "reviewModel" + type: "QDeclarativeReviewModel" + isReadonly: true + isPointer: true + } + Property { + name: "imageModel" + type: "QDeclarativePlaceImageModel" + isReadonly: true + isPointer: true + } + Property { + name: "editorialModel" + type: "QDeclarativePlaceEditorialModel" + isReadonly: true + isPointer: true + } + Property { name: "extendedAttributes"; type: "QObject"; isReadonly: true; isPointer: true } + Property { name: "contactDetails"; type: "QObject"; isReadonly: true; isPointer: true } + Property { name: "detailsFetched"; type: "bool"; isReadonly: true } + Property { name: "status"; type: "Status"; isReadonly: true } + Property { name: "primaryPhone"; type: "string"; isReadonly: true } + Property { name: "primaryFax"; type: "string"; isReadonly: true } + Property { name: "primaryEmail"; type: "string"; isReadonly: true } + Property { name: "primaryWebsite"; type: "QUrl"; isReadonly: true } + Property { name: "visibility"; type: "Visibility" } + Property { name: "favorite"; type: "QDeclarativePlace"; isPointer: true } + Method { name: "getDetails" } + Method { name: "save" } + Method { name: "remove" } + Method { name: "errorString"; type: "string" } + Method { + name: "copyFrom" + Parameter { name: "original"; type: "QDeclarativePlace"; isPointer: true } + } + Method { + name: "initializeFavorite" + Parameter { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + } + } + Component { + name: "QDeclarativePlaceAttribute" + prototype: "QObject" + exports: ["QtLocation/PlaceAttribute 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "attribute"; type: "QPlaceAttribute" } + Property { name: "label"; type: "string" } + Property { name: "text"; type: "string" } + } + Component { + name: "QDeclarativePlaceContentModel" + prototype: "QAbstractListModel" + Property { name: "place"; type: "QDeclarativePlace"; isPointer: true } + Property { name: "batchSize"; type: "int" } + Property { name: "totalCount"; type: "int"; isReadonly: true } + } + Component { + name: "QDeclarativePlaceEditorialModel" + prototype: "QDeclarativePlaceContentModel" + exports: ["QtLocation/EditorialModel 5.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "QDeclarativePlaceIcon" + prototype: "QObject" + exports: ["QtLocation/Icon 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "icon"; type: "QPlaceIcon" } + Property { name: "parameters"; type: "QObject"; isReadonly: true; isPointer: true } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Method { + name: "url" + type: "QUrl" + Parameter { name: "size"; type: "QSize" } + } + Method { name: "url"; type: "QUrl" } + } + Component { + name: "QDeclarativePlaceImageModel" + prototype: "QDeclarativePlaceContentModel" + exports: ["QtLocation/ImageModel 5.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "QDeclarativePlaceUser" + prototype: "QObject" + exports: ["QtLocation/User 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "user"; type: "QPlaceUser" } + Property { name: "userId"; type: "string" } + Property { name: "name"; type: "string" } + } + Component { + name: "QDeclarativePolygonMapItem" + defaultProperty: "data" + prototype: "QDeclarativeGeoMapItemBase" + exports: ["QtLocation/MapPolygon 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "path"; type: "QJSValue" } + Property { name: "color"; type: "QColor" } + Property { + name: "border" + type: "QDeclarativeMapLineProperties" + isReadonly: true + isPointer: true + } + Signal { + name: "colorChanged" + Parameter { name: "color"; type: "QColor" } + } + Method { + name: "addCoordinate" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { + name: "removeCoordinate" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + } + Component { + name: "QDeclarativePolylineMapItem" + defaultProperty: "data" + prototype: "QDeclarativeGeoMapItemBase" + exports: ["QtLocation/MapPolyline 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "path"; type: "QJSValue" } + Property { + name: "line" + type: "QDeclarativeMapLineProperties" + isReadonly: true + isPointer: true + } + Method { name: "pathLength"; type: "int" } + Method { + name: "addCoordinate" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { + name: "insertCoordinate" + Parameter { name: "index"; type: "int" } + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { + name: "replaceCoordinate" + Parameter { name: "index"; type: "int" } + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { + name: "coordinateAt" + type: "QGeoCoordinate" + Parameter { name: "index"; type: "int" } + } + Method { + name: "containsCoordinate" + type: "bool" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { + name: "removeCoordinate" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { + name: "removeCoordinate" + Parameter { name: "index"; type: "int" } + } + Method { + name: "setPath" + Parameter { name: "path"; type: "QGeoPath" } + } + } + Component { + name: "QDeclarativeRatings" + prototype: "QObject" + exports: ["QtLocation/Ratings 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "ratings"; type: "QPlaceRatings" } + Property { name: "average"; type: "double" } + Property { name: "maximum"; type: "double" } + Property { name: "count"; type: "int" } + } + Component { + name: "QDeclarativeRectangleMapItem" + defaultProperty: "data" + prototype: "QDeclarativeGeoMapItemBase" + exports: ["QtLocation/MapRectangle 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "topLeft"; type: "QGeoCoordinate" } + Property { name: "bottomRight"; type: "QGeoCoordinate" } + Property { name: "color"; type: "QColor" } + Property { + name: "border" + type: "QDeclarativeMapLineProperties" + isReadonly: true + isPointer: true + } + Signal { + name: "topLeftChanged" + Parameter { name: "topLeft"; type: "QGeoCoordinate" } + } + Signal { + name: "bottomRightChanged" + Parameter { name: "bottomRight"; type: "QGeoCoordinate" } + } + Signal { + name: "colorChanged" + Parameter { name: "color"; type: "QColor" } + } + } + Component { + name: "QDeclarativeReviewModel" + prototype: "QDeclarativePlaceContentModel" + exports: ["QtLocation/ReviewModel 5.0"] + exportMetaObjectRevisions: [0] + } + Component { + name: "QDeclarativeRouteMapItem" + defaultProperty: "data" + prototype: "QDeclarativePolylineMapItem" + exports: ["QtLocation/MapRoute 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "route"; type: "QDeclarativeGeoRoute"; isPointer: true } + Signal { + name: "routeChanged" + Parameter { name: "route"; type: "const QDeclarativeGeoRoute"; isPointer: true } + } + } + Component { + name: "QDeclarativeSearchModelBase" + prototype: "QAbstractListModel" + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Loading": 2, + "Error": 3 + } + } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "searchArea"; type: "QVariant" } + Property { name: "limit"; type: "int" } + Property { name: "previousPagesAvailable"; type: "bool"; isReadonly: true } + Property { name: "nextPagesAvailable"; type: "bool"; isReadonly: true } + Property { name: "status"; type: "Status"; isReadonly: true } + Method { name: "update" } + Method { name: "cancel" } + Method { name: "reset" } + Method { name: "errorString"; type: "string" } + Method { name: "previousPage" } + Method { name: "nextPage" } + } + Component { + name: "QDeclarativeSearchResultModel" + prototype: "QDeclarativeSearchModelBase" + exports: ["QtLocation/PlaceSearchModel 5.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "SearchResultType" + values: { + "UnknownSearchResult": 0, + "PlaceResult": 1, + "ProposedSearchResult": 2 + } + } + Enum { + name: "RelevanceHint" + values: { + "UnspecifiedHint": 0, + "DistanceHint": 1, + "LexicalPlaceNameHint": 2 + } + } + Property { name: "searchTerm"; type: "string" } + Property { name: "categories"; type: "QDeclarativeCategory"; isList: true; isReadonly: true } + Property { name: "recommendationId"; type: "string" } + Property { name: "relevanceHint"; type: "RelevanceHint" } + Property { name: "visibilityScope"; type: "QDeclarativePlace::Visibility" } + Property { name: "count"; type: "int"; isReadonly: true } + Property { name: "favoritesPlugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "favoritesMatchParameters"; type: "QVariantMap" } + Signal { name: "rowCountChanged" } + Signal { name: "dataChanged" } + Method { + name: "data" + type: "QVariant" + Parameter { name: "index"; type: "int" } + Parameter { name: "roleName"; type: "string" } + } + Method { + name: "updateWith" + Parameter { name: "proposedSearchIndex"; type: "int" } + } + } + Component { + name: "QDeclarativeSearchSuggestionModel" + prototype: "QDeclarativeSearchModelBase" + exports: ["QtLocation/PlaceSearchSuggestionModel 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "searchTerm"; type: "string" } + Property { name: "suggestions"; type: "QStringList"; isReadonly: true } + } + Component { + name: "QDeclarativeSupplier" + prototype: "QObject" + exports: ["QtLocation/Supplier 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "supplier"; type: "QPlaceSupplier" } + Property { name: "name"; type: "string" } + Property { name: "supplierId"; type: "string" } + Property { name: "url"; type: "QUrl" } + Property { name: "icon"; type: "QDeclarativePlaceIcon"; isPointer: true } + } + Component { + name: "QDeclarativeSupportedCategoriesModel" + prototype: "QAbstractItemModel" + exports: ["QtLocation/CategoryModel 5.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "Roles" + values: { + "CategoryRole": 256, + "ParentCategoryRole": 257 + } + } + Enum { + name: "Status" + values: { + "Null": 0, + "Ready": 1, + "Loading": 2, + "Error": 3 + } + } + Property { name: "plugin"; type: "QDeclarativeGeoServiceProvider"; isPointer: true } + Property { name: "hierarchical"; type: "bool" } + Property { name: "status"; type: "Status"; isReadonly: true } + Signal { name: "dataChanged" } + Method { name: "update" } + Method { + name: "data" + type: "QVariant" + Parameter { name: "index"; type: "QModelIndex" } + Parameter { name: "role"; type: "int" } + } + Method { name: "errorString"; type: "string" } + } + Component { + name: "QGeoCoordinateObject" + prototype: "QObject" + Property { name: "coordinate"; type: "QGeoCoordinate" } + } + Component { + name: "QGeoMapParameter" + prototype: "QObject" + Property { name: "type"; type: "string" } + Signal { + name: "propertyUpdated" + Parameter { name: "param"; type: "QGeoMapParameter"; isPointer: true } + Parameter { name: "propertyName"; type: "const char"; isPointer: true } + } + } + Component { + name: "QGeoMapPinchEvent" + prototype: "QObject" + exports: ["QtLocation/MapPinchEvent 5.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "center"; type: "QPointF"; isReadonly: true } + Property { name: "angle"; type: "double"; isReadonly: true } + Property { name: "point1"; type: "QPointF"; isReadonly: true } + Property { name: "point2"; type: "QPointF"; isReadonly: true } + Property { name: "pointCount"; type: "int"; isReadonly: true } + Property { name: "accepted"; type: "bool" } + } + Component { + name: "QQmlPropertyMap" + prototype: "QObject" + exports: ["QtLocation/ExtendedAttributes 5.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Signal { + name: "valueChanged" + Parameter { name: "key"; type: "string" } + Parameter { name: "value"; type: "QVariant" } + } + Method { name: "keys"; type: "QStringList" } + } + Component { + name: "QQuickGeoMapGestureArea" + defaultProperty: "data" + prototype: "QQuickItem" + exports: [ + "QtLocation/MapGestureArea 5.0", + "QtLocation/MapGestureArea 5.6" + ] + isCreatable: false + exportMetaObjectRevisions: [0, 1] + Enum { + name: "GeoMapGesture" + values: { + "NoGesture": 0, + "PinchGesture": 1, + "PanGesture": 2, + "FlickGesture": 4, + "RotationGesture": 8, + "TiltGesture": 16 + } + } + Enum { + name: "AcceptedGestures" + values: { + "NoGesture": 0, + "PinchGesture": 1, + "PanGesture": 2, + "FlickGesture": 4, + "RotationGesture": 8, + "TiltGesture": 16 + } + } + Property { name: "enabled"; type: "bool" } + Property { name: "pinchActive"; type: "bool"; isReadonly: true } + Property { name: "panActive"; type: "bool"; isReadonly: true } + Property { name: "rotationActive"; type: "bool"; isReadonly: true } + Property { name: "tiltActive"; type: "bool"; isReadonly: true } + Property { name: "acceptedGestures"; type: "AcceptedGestures" } + Property { name: "maximumZoomLevelChange"; type: "double" } + Property { name: "flickDeceleration"; type: "double" } + Property { name: "preventStealing"; revision: 1; type: "bool" } + Signal { + name: "pinchStarted" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { + name: "pinchUpdated" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { + name: "pinchFinished" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { name: "panStarted" } + Signal { name: "panFinished" } + Signal { name: "flickStarted" } + Signal { name: "flickFinished" } + Signal { + name: "rotationStarted" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { + name: "rotationUpdated" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { + name: "rotationFinished" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { + name: "tiltStarted" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { + name: "tiltUpdated" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + Signal { + name: "tiltFinished" + Parameter { name: "pinch"; type: "QGeoMapPinchEvent"; isPointer: true } + } + } +} diff --git a/src/imports/location/qmldir b/src/imports/location/qmldir new file mode 100644 index 0000000..37ecf66 --- /dev/null +++ b/src/imports/location/qmldir @@ -0,0 +1,4 @@ +module QtLocation +plugin declarative_location +classname QtLocationDeclarativeModule +typeinfo plugins.qmltypes diff --git a/src/imports/locationlabs/locationlabs.cpp b/src/imports/locationlabs/locationlabs.cpp new file mode 100644 index 0000000..c48fd7d --- /dev/null +++ b/src/imports/locationlabs/locationlabs.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_Qt_labs_location); +#endif +} + +QT_BEGIN_NAMESPACE + + +class QtLocationLabsDeclarativeModule: public QQmlExtensionPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid + FILE "plugin.json") + +public: + QtLocationLabsDeclarativeModule(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + virtual void registerTypes(const char *uri) + { + if (QLatin1String(uri) == QLatin1String("Qt.labs.location")) { + + // @uri QtLocationLabs + int major = 1; + int minor = 0; + + // Register the 1.0 labs types + qmlRegisterType(uri, major, minor, "MapIconObject"); + qmlRegisterType(uri, major, minor, "MapObjectView"); + qmlRegisterType(uri, major, minor, "MapRouteObject"); + qmlRegisterType(uri, major, minor, "MapCircleObject"); + qmlRegisterType(uri, major, minor, "MapPolygonObject"); + qmlRegisterType(uri, major, minor, "MapPolylineObject"); + qmlRegisterType(uri, major, minor, "Navigator"); + + // Register the latest Qt version as QML type version + qmlRegisterModule(uri, QT_VERSION_MAJOR, QT_VERSION_MINOR); + } else { + qDebug() << "Unsupported URI given to load location QML plugin: " << QLatin1String(uri); + } + } +}; + +#include "locationlabs.moc" + +QT_END_NAMESPACE diff --git a/src/imports/locationlabs/locationlabs.pro b/src/imports/locationlabs/locationlabs.pro new file mode 100644 index 0000000..db7f5ec --- /dev/null +++ b/src/imports/locationlabs/locationlabs.pro @@ -0,0 +1,16 @@ +QT += quick-private network positioning-private location-private qml-private core-private gui-private + +TARGET = locationlabsplugin +CXX_MODULE = $$TARGET +TARGETPATH = Qt/labs/location +IMPORT_VERSION = 5.11 + +SOURCES += \ + locationlabs.cpp + +#CONFIG += no_cxx_module +load(qml_plugin) + +OTHER_FILES += \ + plugin.json \ + qmldir diff --git a/src/imports/locationlabs/plugin.json b/src/imports/locationlabs/plugin.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/src/imports/locationlabs/plugin.json @@ -0,0 +1,2 @@ +{ +} diff --git a/src/imports/locationlabs/plugins.qmltypes b/src/imports/locationlabs/plugins.qmltypes new file mode 100644 index 0000000..d5e30c3 --- /dev/null +++ b/src/imports/locationlabs/plugins.qmltypes @@ -0,0 +1,77 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable Qt.labs.location 5.11' + +Module { + dependencies: ["QtQuick 2.8"] + Component { + name: "QGeoMapObject" + defaultProperty: "quickChildren" + prototype: "QParameterizableObject" + Enum { + name: "Type" + values: { + "InvalidType": 0, + "ViewType": 1, + "RouteType": 2, + "RectangleType": 3, + "CircleType": 4, + "PolylineType": 5, + "PolygonType": 6, + "IconType": 7, + "UserType": 256 + } + } + Property { name: "visible"; type: "bool" } + Property { name: "type"; type: "Type"; isReadonly: true } + Signal { name: "selected" } + Signal { name: "completed" } + } + Component { + name: "QMapObjectView" + defaultProperty: "quickChildren" + prototype: "QGeoMapObject" + exports: ["Qt.labs.location/MapObjectView 5.11"] + exportMetaObjectRevisions: [0] + Property { name: "model"; type: "QVariant" } + Property { name: "delegate"; type: "QQmlComponent"; isPointer: true } + Signal { + name: "modelChanged" + Parameter { name: "model"; type: "QVariant" } + } + Signal { + name: "delegateChanged" + Parameter { name: "delegate"; type: "QQmlComponent"; isPointer: true } + } + Method { + name: "addMapObject" + Parameter { name: "object"; type: "QGeoMapObject"; isPointer: true } + } + Method { + name: "removeMapObject" + Parameter { name: "object"; type: "QGeoMapObject"; isPointer: true } + } + } + Component { + name: "QMapRouteObject" + defaultProperty: "quickChildren" + prototype: "QGeoMapObject" + exports: ["Qt.labs.location/MapRouteObject 5.11"] + exportMetaObjectRevisions: [0] + Property { name: "route"; type: "QDeclarativeGeoRoute"; isPointer: true } + Signal { + name: "routeChanged" + Parameter { name: "route"; type: "QDeclarativeGeoRoute"; isPointer: true } + } + } + Component { + name: "QParameterizableObject" + defaultProperty: "quickChildren" + prototype: "QObject" + Property { name: "quickChildren"; type: "QObject"; isList: true; isReadonly: true } + } +} diff --git a/src/imports/locationlabs/qmldir b/src/imports/locationlabs/qmldir new file mode 100644 index 0000000..ddaf6eb --- /dev/null +++ b/src/imports/locationlabs/qmldir @@ -0,0 +1,4 @@ +module Qt.labs.location +plugin locationlabsplugin +classname QtLocationLabsDeclarativeModule +typeinfo plugins.qmltypes diff --git a/src/imports/positioning/locationsingleton.cpp b/src/imports/positioning/locationsingleton.cpp new file mode 100644 index 0000000..35b0d8d --- /dev/null +++ b/src/imports/positioning/locationsingleton.cpp @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "locationsingleton.h" + +static QGeoCoordinate parseCoordinate(const QJSValue &value, bool *ok) +{ + QGeoCoordinate c; + + if (value.isObject()) { + if (value.hasProperty(QStringLiteral("latitude"))) + c.setLatitude(value.property(QStringLiteral("latitude")).toNumber()); + if (value.hasProperty(QStringLiteral("longitude"))) + c.setLongitude(value.property(QStringLiteral("longitude")).toNumber()); + if (value.hasProperty(QStringLiteral("altitude"))) + c.setAltitude(value.property(QStringLiteral("altitude")).toNumber()); + + if (ok) + *ok = true; + } + + return c; +} + + +/*! + \qmltype QtPositioning + \instantiates LocationSingleton + \inqmlmodule QtPositioning + \since 5.2 + + \brief The QtPositioning global object provides useful functions for working with location-based + types in QML. + + \qml + import QtPositioning 5.2 + + Item { + property variant coordinate: QtPositioning.coordinate(-27.5, 153.1) + } + \endqml +*/ + +LocationSingleton::LocationSingleton(QObject *parent) +: QObject(parent) +{ +} + +/*! + \qmlmethod coordinate ::QtPositioning::coordinate() + + Constructs an invalid coordinate. + +*/ +QGeoCoordinate LocationSingleton::coordinate() const +{ + return QGeoCoordinate(); +} + +/*! + \qmlmethod coordinate QtPositioning::coordinate(real latitude, real longitue, real altitude) const + + Constructs a coordinate with the specified \a latitude, \a longitude and optional \a altitude. + Both \a latitude and \a longitude must be valid, otherwise an invalid coordinate is returned. + + \sa {coordinate} +*/ +QGeoCoordinate LocationSingleton::coordinate(double latitude, double longitude, double altitude) const +{ + return QGeoCoordinate(latitude, longitude, altitude); +} + +/*! + \qmlmethod geoshape QtPositioning::shape() const + + Constructs an invalid geoshape. + + \sa {geoshape} +*/ +QGeoShape LocationSingleton::shape() const +{ + return QGeoShape(); +} + +/*! + \qmlmethod georectangle QtPositioning::rectangle() const + + Constructs an invalid georectangle. + + \sa {georectangle} +*/ +QGeoRectangle LocationSingleton::rectangle() const +{ + return QGeoRectangle(); +} + +/*! + \qmlmethod georectangle QtPositioning::rectangle(coordinate center, real width, real height) const + + Constructs a georectangle centered at \a center with a width of \a width degrees and a hight of + \a height degrees. + + \sa {georectangle} +*/ +QGeoRectangle LocationSingleton::rectangle(const QGeoCoordinate ¢er, + double width, double height) const +{ + return QGeoRectangle(center, width, height); +} + +/*! + \qmlmethod georectangle QtPositioning::rectangle(coordinate topLeft, coordinate bottomRight) const + + Constructs a georectangle with its top left corner positioned at \a topLeft and its bottom + right corner positioned at \a {bottomLeft}. + + \sa {georectangle} +*/ +QGeoRectangle LocationSingleton::rectangle(const QGeoCoordinate &topLeft, + const QGeoCoordinate &bottomRight) const +{ + return QGeoRectangle(topLeft, bottomRight); +} + +/*! + \qmlmethod georectangle QtLocation5::QtLocation::rectangle(list coordinates) const + + Constructs a georectangle from the list of coordinates, the returned list is the smallest possible + containing all the coordinates. + + \sa {georectangle} +*/ +QGeoRectangle LocationSingleton::rectangle(const QVariantList &coordinates) const +{ + QList internalCoordinates; + for (int i = 0; i < coordinates.size(); i++) { + if (coordinates.at(i).canConvert()) + internalCoordinates << coordinates.at(i).value(); + } + return QGeoRectangle(internalCoordinates); +} + +/*! + \qmlmethod geocircle QtPositioning::circle() const + + Constructs an invalid geocircle. + + \sa {geocircle} +*/ +QGeoCircle LocationSingleton::circle() const +{ + return QGeoCircle(); +} + +/*! + \qmlmethod geocircle QtPositioning::circle(coordinate center, real radius) const + + Constructs a geocircle centered at \a center with a radius of \a radius meters. +*/ +QGeoCircle LocationSingleton::circle(const QGeoCoordinate ¢er, qreal radius) const +{ + return QGeoCircle(center, radius); +} + +/*! + \qmlmethod geopath QtPositioning::path() const + + Constructs an empty geopath. + + \sa {geopath} + \since 5.9 +*/ +QGeoPath LocationSingleton::path() const +{ + return QGeoPath(); +} + +QGeoPath LocationSingleton::path(const QJSValue &value, qreal width) const +{ + QList pathList; + + if (value.isArray()) { + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoCoordinate c = parseCoordinate(value.property(i), &ok); + + if (!ok || !c.isValid()) { + pathList.clear(); // aborting + break; + } + + pathList.append(c); + } + } + + return QGeoPath(pathList, width); +} + +/*! + \qmlmethod geopolygon QtPositioning::polygon() const + + Constructs an empty polygon. + + \sa {geopolygon} + \since 5.10 +*/ +QGeoPolygon LocationSingleton::polygon() const +{ + return QGeoPolygon(); +} + +/*! + \qmlmethod geopolygon QtPositioning::polygon(list coordinates) const + + Constructs a polygon from coordinates. + + \sa {geopolygon} + \since 5.10 +*/ +QGeoPolygon LocationSingleton::polygon(const QVariantList &coordinates) const +{ + QList internalCoordinates; + for (int i = 0; i < coordinates.size(); i++) { + if (coordinates.at(i).canConvert()) + internalCoordinates << coordinates.at(i).value(); + } + return QGeoPolygon(internalCoordinates); +} + +/*! + \qmlmethod geocircle QtPositioning::shapeToCircle(geoshape shape) const + + Converts \a shape to a geocircle. + + \sa {geocircle} + \since 5.5 +*/ +QGeoCircle LocationSingleton::shapeToCircle(const QGeoShape &shape) const +{ + return QGeoCircle(shape); +} + +/*! + \qmlmethod georectangle QtPositioning::shapeToRectangle(geoshape shape) const + + Converts \a shape to a georectangle. + + \sa {georectangle} + \since 5.5 +*/ +QGeoRectangle LocationSingleton::shapeToRectangle(const QGeoShape &shape) const +{ + return QGeoRectangle(shape); +} + +/*! + \qmlmethod geopath QtPositioning::shapeToPath(geoshape shape) const + + Converts \a shape to a geopath. + + \sa {geopath} + \since 5.9 +*/ +QGeoPath LocationSingleton::shapeToPath(const QGeoShape &shape) const +{ + return QGeoPath(shape); +} + +/*! + \qmlmethod geopolygon QtPositioning::shapeToPolygon(geoshape shape) const + + Converts \a shape to a polygon. + + \sa {geopolygon} + \since 5.10 +*/ +QGeoPolygon LocationSingleton::shapeToPolygon(const QGeoShape &shape) const +{ + return QGeoPolygon(shape); +} diff --git a/src/imports/positioning/locationsingleton.h b/src/imports/positioning/locationsingleton.h new file mode 100644 index 0000000..56d35b7 --- /dev/null +++ b/src/imports/positioning/locationsingleton.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOCATIONSINGLETON_H +#define LOCATIONSINGLETON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class LocationSingleton : public QObject +{ + Q_OBJECT + +public: + explicit LocationSingleton(QObject *parent = 0); + + Q_INVOKABLE QGeoCoordinate coordinate() const; + Q_INVOKABLE QGeoCoordinate coordinate(double latitude, double longitude, + double altitude = qQNaN()) const; + + Q_INVOKABLE QGeoShape shape() const; + + Q_INVOKABLE QGeoRectangle rectangle() const; + Q_INVOKABLE QGeoRectangle rectangle(const QGeoCoordinate ¢er, + double width, double height) const; + Q_INVOKABLE QGeoRectangle rectangle(const QGeoCoordinate &topLeft, + const QGeoCoordinate &bottomRight) const; + Q_INVOKABLE QGeoRectangle rectangle(const QVariantList &coordinates) const; + + Q_INVOKABLE QGeoCircle circle() const; + Q_INVOKABLE QGeoCircle circle(const QGeoCoordinate ¢er, qreal radius = -1.0) const; + + Q_INVOKABLE QGeoPath path() const; + Q_INVOKABLE QGeoPath path(const QJSValue &value, qreal width = 0.0) const; + + Q_INVOKABLE QGeoPolygon polygon() const; + Q_INVOKABLE QGeoPolygon polygon(const QVariantList &value) const; + + Q_INVOKABLE QGeoCircle shapeToCircle(const QGeoShape &shape) const; + Q_INVOKABLE QGeoRectangle shapeToRectangle(const QGeoShape &shape) const; + Q_INVOKABLE QGeoPath shapeToPath(const QGeoShape &shape) const; + Q_INVOKABLE QGeoPolygon shapeToPolygon(const QGeoShape &shape) const; +}; + +#endif // LOCATIONSINGLETON_H diff --git a/src/imports/positioning/plugin.json b/src/imports/positioning/plugin.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/src/imports/positioning/plugin.json @@ -0,0 +1,2 @@ +{ +} diff --git a/src/imports/positioning/plugins.qmltypes b/src/imports/positioning/plugins.qmltypes new file mode 100644 index 0000000..dab7280 --- /dev/null +++ b/src/imports/positioning/plugins.qmltypes @@ -0,0 +1,259 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump -nonrelocatable QtPositioning 5.11' + +Module { + dependencies: ["QtQuick 2.8"] + Component { + name: "LocationSingleton" + prototype: "QObject" + exports: ["QtPositioning/QtPositioning 5.0"] + isCreatable: false + isSingleton: true + exportMetaObjectRevisions: [0] + Method { name: "coordinate"; type: "QGeoCoordinate" } + Method { + name: "coordinate" + type: "QGeoCoordinate" + Parameter { name: "latitude"; type: "double" } + Parameter { name: "longitude"; type: "double" } + Parameter { name: "altitude"; type: "double" } + } + Method { + name: "coordinate" + type: "QGeoCoordinate" + Parameter { name: "latitude"; type: "double" } + Parameter { name: "longitude"; type: "double" } + } + Method { name: "shape"; type: "QGeoShape" } + Method { name: "rectangle"; type: "QGeoRectangle" } + Method { + name: "rectangle" + type: "QGeoRectangle" + Parameter { name: "center"; type: "QGeoCoordinate" } + Parameter { name: "width"; type: "double" } + Parameter { name: "height"; type: "double" } + } + Method { + name: "rectangle" + type: "QGeoRectangle" + Parameter { name: "topLeft"; type: "QGeoCoordinate" } + Parameter { name: "bottomRight"; type: "QGeoCoordinate" } + } + Method { + name: "rectangle" + type: "QGeoRectangle" + Parameter { name: "coordinates"; type: "QVariantList" } + } + Method { name: "circle"; type: "QGeoCircle" } + Method { + name: "circle" + type: "QGeoCircle" + Parameter { name: "center"; type: "QGeoCoordinate" } + Parameter { name: "radius"; type: "double" } + } + Method { + name: "circle" + type: "QGeoCircle" + Parameter { name: "center"; type: "QGeoCoordinate" } + } + Method { name: "path"; type: "QGeoPath" } + Method { + name: "path" + type: "QGeoPath" + Parameter { name: "value"; type: "QJSValue" } + Parameter { name: "width"; type: "double" } + } + Method { + name: "path" + type: "QGeoPath" + Parameter { name: "value"; type: "QJSValue" } + } + Method { name: "polygon"; type: "QGeoPath" } + Method { + name: "polygon" + type: "QGeoPath" + Parameter { name: "value"; type: "QVariantList" } + } + Method { + name: "shapeToCircle" + type: "QGeoCircle" + Parameter { name: "shape"; type: "QGeoShape" } + } + Method { + name: "shapeToRectangle" + type: "QGeoRectangle" + Parameter { name: "shape"; type: "QGeoShape" } + } + Method { + name: "shapeToPath" + type: "QGeoPath" + Parameter { name: "shape"; type: "QGeoShape" } + } + Method { + name: "shapeToPolygon" + type: "QGeoPolygon" + Parameter { name: "shape"; type: "QGeoShape" } + } + } + Component { + name: "QDeclarativeGeoAddress" + prototype: "QObject" + exports: ["QtPositioning/Address 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "address"; type: "QGeoAddress" } + Property { name: "text"; type: "string" } + Property { name: "country"; type: "string" } + Property { name: "countryCode"; type: "string" } + Property { name: "state"; type: "string" } + Property { name: "county"; type: "string" } + Property { name: "city"; type: "string" } + Property { name: "district"; type: "string" } + Property { name: "street"; type: "string" } + Property { name: "postalCode"; type: "string" } + Property { name: "isTextGenerated"; type: "bool"; isReadonly: true } + } + Component { + name: "QDeclarativeGeoLocation" + prototype: "QObject" + exports: ["QtPositioning/Location 5.0"] + exportMetaObjectRevisions: [0] + Property { name: "location"; type: "QGeoLocation" } + Property { name: "address"; type: "QDeclarativeGeoAddress"; isPointer: true } + Property { name: "coordinate"; type: "QGeoCoordinate" } + Property { name: "boundingBox"; type: "QGeoRectangle" } + } + Component { + name: "QDeclarativePosition" + prototype: "QObject" + exports: [ + "QtPositioning/Position 5.0", + "QtPositioning/Position 5.3", + "QtPositioning/Position 5.4" + ] + exportMetaObjectRevisions: [0, 1, 2] + Property { name: "latitudeValid"; type: "bool"; isReadonly: true } + Property { name: "longitudeValid"; type: "bool"; isReadonly: true } + Property { name: "altitudeValid"; type: "bool"; isReadonly: true } + Property { name: "coordinate"; type: "QGeoCoordinate"; isReadonly: true } + Property { name: "timestamp"; type: "QDateTime"; isReadonly: true } + Property { name: "speed"; type: "double"; isReadonly: true } + Property { name: "speedValid"; type: "bool"; isReadonly: true } + Property { name: "horizontalAccuracy"; type: "double" } + Property { name: "verticalAccuracy"; type: "double" } + Property { name: "horizontalAccuracyValid"; type: "bool"; isReadonly: true } + Property { name: "verticalAccuracyValid"; type: "bool"; isReadonly: true } + Property { name: "directionValid"; revision: 1; type: "bool"; isReadonly: true } + Property { name: "direction"; revision: 1; type: "double"; isReadonly: true } + Property { name: "verticalSpeedValid"; revision: 1; type: "bool"; isReadonly: true } + Property { name: "verticalSpeed"; revision: 1; type: "double"; isReadonly: true } + Property { name: "magneticVariation"; revision: 2; type: "double"; isReadonly: true } + Property { name: "magneticVariationValid"; revision: 2; type: "bool"; isReadonly: true } + Signal { name: "directionValidChanged"; revision: 1 } + Signal { name: "directionChanged"; revision: 1 } + Signal { name: "verticalSpeedValidChanged"; revision: 1 } + Signal { name: "verticalSpeedChanged"; revision: 1 } + Signal { name: "magneticVariationChanged"; revision: 2 } + Signal { name: "magneticVariationValidChanged"; revision: 2 } + } + Component { + name: "QDeclarativePositionSource" + prototype: "QObject" + exports: ["QtPositioning/PositionSource 5.0"] + exportMetaObjectRevisions: [0] + Enum { + name: "PositioningMethod" + values: { + "NoPositioningMethods": 0, + "SatellitePositioningMethods": 255, + "NonSatellitePositioningMethods": -256, + "AllPositioningMethods": -1 + } + } + Enum { + name: "PositioningMethods" + values: { + "NoPositioningMethods": 0, + "SatellitePositioningMethods": 255, + "NonSatellitePositioningMethods": -256, + "AllPositioningMethods": -1 + } + } + Enum { + name: "SourceError" + values: { + "AccessError": 0, + "ClosedError": 1, + "UnknownSourceError": 2, + "NoError": 3, + "SocketError": 100 + } + } + Property { name: "position"; type: "QDeclarativePosition"; isReadonly: true; isPointer: true } + Property { name: "active"; type: "bool" } + Property { name: "valid"; type: "bool"; isReadonly: true } + Property { name: "nmeaSource"; type: "QUrl" } + Property { name: "updateInterval"; type: "int" } + Property { name: "supportedPositioningMethods"; type: "PositioningMethods"; isReadonly: true } + Property { name: "preferredPositioningMethods"; type: "PositioningMethods" } + Property { name: "sourceError"; type: "SourceError"; isReadonly: true } + Property { name: "name"; type: "string" } + Signal { name: "validityChanged" } + Signal { name: "updateTimeout" } + Method { name: "update" } + Method { name: "start" } + Method { name: "stop" } + } + Component { + name: "QGeoShape" + exports: ["QtPositioning/GeoShape 5.0"] + isCreatable: false + exportMetaObjectRevisions: [0] + Enum { + name: "ShapeType" + values: { + "UnknownType": 0, + "RectangleType": 1, + "CircleType": 2, + "PathType": 3, + "PolygonType": 4 + } + } + Property { name: "type"; type: "ShapeType"; isReadonly: true } + Property { name: "isValid"; type: "bool"; isReadonly: true } + Property { name: "isEmpty"; type: "bool"; isReadonly: true } + Method { + name: "contains" + type: "bool" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { name: "boundingGeoRectangle"; type: "QGeoRectangle" } + Method { name: "center"; type: "QGeoCoordinate" } + Method { + name: "extendShape" + Parameter { name: "coordinate"; type: "QGeoCoordinate" } + } + Method { name: "toString"; type: "string" } + } + Component { + name: "QQuickGeoCoordinateAnimation" + prototype: "QQuickPropertyAnimation" + exports: ["QtPositioning/CoordinateAnimation 5.3"] + exportMetaObjectRevisions: [0] + Enum { + name: "Direction" + values: { + "Shortest": 0, + "West": 1, + "East": 2 + } + } + Property { name: "from"; type: "QGeoCoordinate" } + Property { name: "to"; type: "QGeoCoordinate" } + Property { name: "direction"; type: "Direction" } + } +} diff --git a/src/imports/positioning/positioning.cpp b/src/imports/positioning/positioning.cpp new file mode 100644 index 0000000..6af79b5 --- /dev/null +++ b/src/imports/positioning/positioning.cpp @@ -0,0 +1,633 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include +#include + +#include +#include + +#include "qquickgeocoordinateanimation_p.h" +#include "locationsingleton.h" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_QtPositioning); +#endif +} + +QT_BEGIN_NAMESPACE + +/*! + \qmlbasictype coordinate + \inqmlmodule QtPositioning + \ingroup qml-QtPositioning5-basictypes + \since 5.2 + + \brief The coordinate type represents and stores a geographic position. + + This type is a QML representation of \l QGeoCoordinate and represents a geographic + position in the form of \l {latitude}, \l longitude and \l altitude attributes. + The \l latitude attribute specifies the number of + decimal degrees above and below the equator. A positive latitude indicates the Northern + Hemisphere and a negative latitude indicates the Southern Hemisphere. The \l longitude + attribute specifies the number of decimal degrees east and west. A positive longitude + indicates the Eastern Hemisphere and a negative longitude indicates the Western Hemisphere. + The \l altitude attribute specifies the number of meters above sea level. Together, these + attributes specify a 3-dimensional position anywhere on or near the Earth's surface. + + The \l isValid attribute can be used to test if a coordinate is valid. A coordinate is + considered valid if it has a valid latitude and longitude. A valid altitude is not required. + The latitude must be between -90 and 90 inclusive and the longitude must be between -180 and + 180 inclusive. + + The \c coordinate type is used by many other types in the Qt Location module, for specifying + the position of an object on a Map, the current position of a device and many other tasks. + They also feature a number of important utility methods that make otherwise complex + calculations simple to use, such as \l {atDistanceAndAzimuth}(). + + \section1 Accuracy + + The latitude, longitude and altitude attributes stored in the coordinate type are represented + as doubles, giving them approximately 16 decimal digits of precision -- enough to specify + micrometers. The calculations performed in coordinate's methods such as \l {azimuthTo}() and + \l {distanceTo}() also use doubles for all intermediate values, but the inherent inaccuracies in + their spherical Earth model dominate the amount of error in their output. + + \section1 Example Usage + + Use properties of type \l variant to store a \c {coordinate}. To create a \c coordinate use + one of the methods described below. In all cases, specifying the \l altitude attribute is + optional. + + To create a \c coordinate value, use the \l{QtPositioning::coordinate}{QtPositioning.coordinate()} + function: + + \qml + import QtPositioning 5.2 + + Location { coordinate: QtPositioning.coordinate(-27.5, 153.1) } + \endqml + + or as separate \l latitude, \l longitude and \l altitude components: + + \qml + Location { + coordinate { + latitude: -27.5 + longitude: 153.1 + } + } + \endqml + + When integrating with C++, note that any QGeoCoordinate value passed into QML from C++ is + automatically converted into a \c coordinate value, and vice-versa. + + \section1 Properties + + \section2 latitude + + \code + real latitude + \endcode + + This property holds the latitude value of the geographical position + (decimal degrees). A positive latitude indicates the Northern Hemisphere, + and a negative latitude indicates the Southern Hemisphere. + If the property has not been set, its default value is NaN. + + For more details see the \l {QGeoCoordinate::latitude} property + + \section2 longitude + + \code + real longitude + \endcode + + This property holds the longitude value of the geographical position + (decimal degrees). A positive longitude indicates the Eastern Hemisphere, + and a negative longitude indicates the Western Hemisphere + If the property has not been set, its default value is NaN. + + For more details see the \l {QGeoCoordinate::longitude} property + + \section2 altitude + + \code + real altitude + \endcode + + This property holds the altitude value (meters above sea level). + If the property has not been set, its default value is NaN. + + For more details see the \l {QGeoCoordinate::altitude} property + + \section2 isValid + + \code + bool isValid + \endcode + + This property holds the current validity of the coordinate. Coordinates + are considered valid if they have been set with a valid latitude and + longitude (altitude is not required). + + The latitude must be between -90 to 90 inclusive to be considered valid, + and the longitude must be between -180 to 180 inclusive to be considered + valid. + + This is a read-only property. + + \section1 Methods + + \section2 distanceTo() + + \code + real distanceTo(coordinate other) + \endcode + + Returns the distance (in meters) from this coordinate to the coordinate specified by \a other. + Altitude is not used in the calculation. + + This calculation returns the great-circle distance between the two coordinates, with an + assumption that the Earth is spherical for the purpose of this calculation. + + \section2 azimuthTo() + + \code + real azimuth(coordinate other) + \endcode + + Returns the azimuth (or bearing) in degrees from this coordinate to the coordinate specified by + \a other. Altitude is not used in the calculation. + + There is an assumption that the Earth is spherical for the purpose of this calculation. + + \section2 atDistanceAndAzimuth() + + \code + coordinate atDistanceAndAzimuth(real distance, real azimuth) + \endcode + + Returns the coordinate that is reached by traveling \a distance metres from this coordinate at + \a azimuth degrees along a great-circle. + + There is an assumption that the Earth is spherical for the purpose of this calculation. +*/ + +/*! + \qmlbasictype geoshape + \inqmlmodule QtPositioning + \ingroup qml-QtPositioning5-basictypes + \since 5.2 + + \brief A geoshape type represents an abstract geographic area. + + This type is a QML representation of \l QGeoShape which is an abstract geographic area. + It includes attributes and methods common to all geographic areas. To create objects + that represent a valid geographic area use \l {georectangle} or \l {geocircle}. + + The \l isValid attribute can be used to test if the geoshape represents a valid geographic + area. + + The \l isEmpty attribute can be used to test if the geoshape represents a region with a + geometrical area of 0. + + The \l {contains}() method can be used to test if a \l {coordinate} is + within the geoshape. + + \section1 Example Usage + + Use properties of type \l variant to store a \c {geoshape}. To create a \c geoshape use one + of the methods described below. + + To create a \c geoshape value, specify it as a "shape()" string: + + \qml + import QtPositioning + + Item { + property variant region: "shape()" + } + \endqml + + or with the \l {QtPositioning::shape}{QtPositioning.shape()} function: + + \qml + import QtPositioning 5.2 + + Item { + property variant region: QtPositioning.shape() + } + \endqml + + When integrating with C++, note that any QGeoShape value passed into QML from C++ is + automatically converted into a \c geoshape value, and vice-versa. + + \section1 Properties + + \section2 isEmpty + + \code + bool isEmpty + \endcode + + Returns whether this geoshape is empty. An empty geoshape is a region which has + a geometrical area of 0. + + \section2 isValid + + \code + bool isValid + \endcode + + Returns whether this geoshape is valid. + + A geoshape is considered to be invalid if some of the data that is required to + unambiguously describe the geoshape has not been set or has been set to an + unsuitable value. + + \section2 type + + \code + ShapeType type + \endcode + + Returns the current type of the shape. + + \list + \li \c GeoShape.UnknownType - The shape's type is not known. + \li \c GeoShape.RectangleType - The shape is a \l georectangle. + \li \c GeoShape.CircleType - The shape is a \l geocircle. + \li \c GeoShape.PathType - The shape is a \l geopath. (Since Qt 5.9) + \li \c GeoShape.PolygonType - The shape is a \l geopolygon. (Since Qt 5.10) + \endlist + + This QML property was introduced by Qt 5.5. + + \section1 Methods + + \section2 contains() + + \code + bool contains(coordinate coord) + \endcode + + Returns true if the \l {QtPositioning::coordinate}{coordinate} specified by \a coord is within + this geoshape; Otherwise returns false. +*/ + +/*! + \qmlbasictype georectangle + \inqmlmodule QtPositioning + \ingroup qml-QtPositioning5-basictypes + \since 5.2 + + \brief The georectangle type represents a rectangular geographic area. + + The \c georectangle type is a \l {geoshape} that represents a + rectangular geographic area. The type is direct representation of a \l QGeoRectangle. + It is defined by a pair of \l {coordinate}{coordinates} which represent the top-left + and bottom-right corners of the \c {georectangle}. The coordinates are accessible + from the \l topLeft and \l bottomRight attributes. + + A \c georectangle is considered invalid if the top-left or bottom-right coordinates are invalid + or if the top-left coordinate is south of the bottom-right coordinate. + + The coordinates of the four corners of the \c georectangle can be accessed with the + \l {topLeft}, \l {topRight}, \l {bottomLeft} and \l {bottomRight} attributes. The \l center + attribute can be used to get the coordinate of the center of the \c georectangle. The \l width + and \l height attributes can be used to get the width and height of the \c georectangle in + degrees. Setting one of these attributes will cause the other attributes to be adjusted + accordingly. + + \section1 Limitations + + A \c georectangle can never cross the poles. + + If the height or center of a \c georectangle is adjusted such that it would cross one of the + poles the height is modified such that the \c georectangle touches but does not cross the pole + and that the center coordinate is still in the center of the \c georectangle. + + \section1 Example Usage + + Use properties of type \l variant to store a \c {georectangle}. To create a \c georectangle + value, use the \l {QtPositioning::rectangle}{QtPositioning.rectangle()} function: + + \qml + import QtPositioning 5.2 + + Item { + property variant region: QtPositioning.rectangle(QtPositioning.coordinate(-27.5, 153.1), QtPositioning.coordinate(-27.6, 153.2)) + } + \endqml + + When integrating with C++, note that any QGeoRectangle value passed into QML from C++ is + automatically converted into a \c georectangle value, and vice-versa. + + \section1 Properties + + \section2 bottomLeft + + \code + coordinate bottomLeft + \endcode + + This property holds the bottom left coordinate of this georectangle. + + \section2 bottomRight + + \code + coordinate bottomRight + \endcode + + This property holds the bottom right coordinate of this georectangle. + + \section2 center + + \code + coordinate center + \endcode + + This property holds the center coordinate of this georectangle. For more details + see \l {QGeoRectangle::setCenter()}. + + \section2 height + + \code + double height + \endcode + + This property holds the height of this georectangle (in degrees). For more details + see \l {QGeoRectangle::setHeight()}. + + \note If the georectangle is invalid, it is not possible to set the height. QtPositioning + releases prior to Qt 5.5 permitted the setting of the height even on invalid georectangles. + + \section2 topLeft + + \code + coordinate topLeft + \endcode + + This property holds the top left coordinate of this georectangle. + + \section2 topRight + + \code + coordinate topRight + \endcode + + This property holds the top right coordinate of this georectangle. + + \section2 width + + \code + double width + \endcode + + This property holds the width of this georectangle (in degrees). For more details + see \l {QGeoRectangle::setWidth()}. + + \note If the georectangle is invalid, it is not possible to set the width. QtPositioning + releases prior to Qt 5.5 permitted the setting of the width even on invalid georectangles. +*/ + +/*! + \qmlbasictype geocircle + \inqmlmodule QtPositioning + \ingroup qml-QtPositioning5-basictypes + \since 5.2 + + \brief The geocircle type represents a circular geographic area. + + The \c geocircle type is a \l {geoshape} that represents a circular + geographic area. It is a direct representation of a \l QGeoCircle and is defined + in terms of a \l {coordinate} which specifies the \l center of the circle and + a qreal which specifies the \l radius of the circle in meters. + + The circle is considered invalid if the \l center coordinate is invalid or if + the \l radius is less than zero. + + \section1 Example Usage + + Use properties of type \l variant to store a \c {geocircle}. To create a \c geocircle value, + use the \l {QtPositioning::circle}{QtPositioning.circle()} function: + + \qml + import QtPositioning 5.2 + + Item { + property variant region: QtPositioning.circle(QtPositioning.coordinate(-27.5, 153.1), 1000) + } + \endqml + + When integrating with C++, note that any QGeoCircle value passed into QML from C++ is + automatically converted into a \c geocircle value, and vise-versa. + + \section1 Properties + + \section2 center + + \code + coordinate radius + \endcode + + This property holds the coordinate of the center of the geocircle. + + \section2 radius + + \code + real radius + \endcode + + This property holds the radius of the geocircle in meters. + + The default value for the radius is -1 indicating an invalid geocircle area. +*/ + +/*! + \qmlbasictype geopath + \inqmlmodule QtPositioning + \ingroup qml-QtPositioning5-basictypes + \since 5.9 + + \brief The geopath type represents a geographic path. + + The \c geopath type is a \l {geoshape} that represents a geographic + path. It is a direct representation of a \l QGeoPath and is defined + in terms of a \l {path} which holds the list of geo coordinates in the + path. + + The path is considered invalid if it is empty. + + When integrating with C++, note that any QGeoPath value passed into QML from C++ is + automatically converted into a \c geopath value, and vice versa. + + \section1 Properties + + \section2 path + + This property holds the list of coordinates defining the path. + + \section2 width + + This property holds the width of the path in meters. This is currently only used + when calling the \l {contains}() method. + + The default value for the width is 0. +*/ + +/*! + \qmlbasictype geopolygon + \inqmlmodule QtPositioning + \ingroup qml-QtPositioning5-basictypes + \since 5.10 + + \brief The geopolygon type represents a geographic polygon. + + The \c geopolygon type is a \l [QML] geoshape that represents a geographic + polygon. It is a direct representation of QGeoPolygon and is defined in + terms of a \l path which holds a list of geo coordinates in the polygon. + + The polygon is considered invalid if its path holds less than three + coordinates. + + When integrating with C++, note that any QGeoPolygon value passed into QML + is automatically converted into a \c geopolygon, and vice versa. + + \section1 Properties + + \section2 path + + This property holds the list of coordinates defining the polygon. +*/ + +static QObject *singleton_type_factory(QQmlEngine *engine, QJSEngine *jsEngine) +{ + Q_UNUSED(engine) + Q_UNUSED(jsEngine) + + return new LocationSingleton; +} + +class QtPositioningDeclarativeModule: public QQmlExtensionPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid + FILE "plugin.json") + +public: + QtPositioningDeclarativeModule(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + virtual void registerTypes(const char *uri) + { + if (QLatin1String(uri) == QStringLiteral("QtPositioning")) { + + // @uri QtPositioning 5.0 + + int major = 5; + int minor = 0; + + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + qRegisterMetaType(); + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + qRegisterMetaType(); + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + qRegisterMetaType(); + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + + qRegisterAnimationInterpolator(q_coordinateInterpolator); + + // Register the 5.0 types + // 5.0 is silent and not advertised + qmlRegisterSingletonType(uri, major, minor, "QtPositioning", singleton_type_factory); + qmlRegisterValueTypeEnums(uri, major, minor, "GeoShape"); + qmlRegisterType(uri, major, minor, "Position"); + qmlRegisterType(uri, major, minor, "PositionSource"); + qmlRegisterType(uri, major, minor, "Address"); + qmlRegisterType(uri, major, minor, "Location"); + + // Register the 5.3 types + // Introduction of 5.3 version; existing 5.0 exports become automatically available under 5.3 + minor = 3; + qmlRegisterType(uri, major, minor, "CoordinateAnimation"); + qmlRegisterType(uri, major, minor, "Position"); + + minor = 4; + qmlRegisterType(uri, major, minor, "Position"); + + // Register the latest Qt version as QML type version + qmlRegisterModule(uri, QT_VERSION_MAJOR, QT_VERSION_MINOR); + } else { + qDebug() << "Unsupported URI given to load positioning QML plugin: " << QLatin1String(uri); + } + } +}; + +#include "positioning.moc" + +QT_END_NAMESPACE diff --git a/src/imports/positioning/positioning.pro b/src/imports/positioning/positioning.pro new file mode 100644 index 0000000..f238728 --- /dev/null +++ b/src/imports/positioning/positioning.pro @@ -0,0 +1,12 @@ +QT += quick-private positioning-private positioningquick-private qml-private core-private + +INCLUDEPATH *= $$PWD + +HEADERS += $$files(*.h) +SOURCES += $$files(*.cpp) + +load(qml_plugin) + +OTHER_FILES += \ + plugin.json \ + qmldir diff --git a/src/imports/positioning/qmldir b/src/imports/positioning/qmldir new file mode 100644 index 0000000..fc4ebf8 --- /dev/null +++ b/src/imports/positioning/qmldir @@ -0,0 +1,4 @@ +module QtPositioning +plugin declarative_positioning +classname QtPositioningDeclarativeModule +typeinfo plugins.qmltypes diff --git a/src/imports/positioning/qquickgeocoordinateanimation.cpp b/src/imports/positioning/qquickgeocoordinateanimation.cpp new file mode 100644 index 0000000..b00f187 --- /dev/null +++ b/src/imports/positioning/qquickgeocoordinateanimation.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickgeocoordinateanimation_p.h" +#include "qquickgeocoordinateanimation_p_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype CoordinateAnimation + \instantiates QQuickGeoCoordinateAnimation + \inherits PropertyAnimation + \inqmlmodule QtPositioning + \since 5.3 + + \brief A PropertyAnimation for geo coordinate properties. + + A specialized \l{PropertyAnimation} that defines an animation + between two \l{coordinate}{coordinates}. + + By default, a \l{latitude} of the \l{coordinate} is animated in the direction of shortest + (geodesic) distance between those coordinates. Since CoordinateAnimation uses Mercator + map projection, the \l{latitude} animation is always between -90 and 90 degrees. + The \l{longitude} animation path is not limited and can go over 180 degrees + in both west and east directions. + + The \l{direction} property can be set to specify the direction in which the \l{longitude} + animation should occur. + + \sa {Animation and Transitions in Qt Quick} +*/ + +QVariant q_coordinateInterpolator(const QGeoCoordinate &from, const QGeoCoordinate &to, qreal progress) +{ + if (from == to) { + if (progress < 0.5) { + return QVariant::fromValue(from); + } else { + return QVariant::fromValue(to); + } + } + + QGeoCoordinate result = QWebMercator::coordinateInterpolation(from, to, progress); + + return QVariant::fromValue(result); +} + +QVariant q_coordinateShortestInterpolator(const QGeoCoordinate &from, const QGeoCoordinate &to, qreal progress) +{ + const QGeoMercatorCoordinatePrivate* fromMercator = + static_cast(QGeoCoordinatePrivate::get(&from)); + const QGeoMercatorCoordinatePrivate* toMercator = + static_cast(QGeoCoordinatePrivate::get(&to)); + + double toX = toMercator->m_mercatorX; + double toY = toMercator->m_mercatorY; + double fromX = fromMercator->m_mercatorX; + double fromY = fromMercator->m_mercatorY; + double x; + if (0.5 < qAbs(toX - fromX)) { + // handle dateline crossing + double ex = toX; + double sx = fromX; + if (ex < sx) + sx -= 1.0; + else if (sx < ex) + ex -= 1.0; + + x = fromX + (toX - fromX) * progress; + + if (x < 0.0) + x += 1.0; + + } else { + x = fromX + (toX - fromX) * progress; + } + + double y = fromY + (toY - fromY) * progress; + + QGeoCoordinate result = QWebMercator::mercatorToCoord(QDoubleVector2D(x, y)); + result.setAltitude(from.altitude() + (to.altitude() - from.altitude()) * progress); + return QVariant::fromValue(result); +} + +QVariant q_coordinateWestInterpolator(const QGeoCoordinate &from, const QGeoCoordinate &to, qreal progress) +{ + const QGeoMercatorCoordinatePrivate* fromMercator = + static_cast(QGeoCoordinatePrivate::get(&from)); + const QGeoMercatorCoordinatePrivate* toMercator = + static_cast(QGeoCoordinatePrivate::get(&to)); + + double toX = toMercator->m_mercatorX; + double toY = toMercator->m_mercatorY; + double fromX = fromMercator->m_mercatorX; + double fromY = fromMercator->m_mercatorY; + double diff = toX - fromX; + + while (diff < 0.0) { + toX += 1.0; + diff += 1.0; + } + + double x = fromX + (toX - fromX) * progress; + double y = fromY + (toY - fromY) * progress; + + while (x > 1.0) + x -= 1.0; + + QGeoCoordinate result = QWebMercator::mercatorToCoord(QDoubleVector2D(x, y)); + result.setAltitude(from.altitude() + (to.altitude() - from.altitude()) * progress); + + return QVariant::fromValue(result); +} + +QVariant q_coordinateEastInterpolator(const QGeoCoordinate &from, const QGeoCoordinate &to, qreal progress) +{ + const QGeoMercatorCoordinatePrivate* fromMercator = + static_cast(QGeoCoordinatePrivate::get(&from)); + const QGeoMercatorCoordinatePrivate* toMercator = + static_cast(QGeoCoordinatePrivate::get(&to)); + + double toX = toMercator->m_mercatorX; + double toY = toMercator->m_mercatorY; + double fromX = fromMercator->m_mercatorX; + double fromY = fromMercator->m_mercatorY; + double diff = toX - fromX; + + while (diff > 0.0) { + toX -= 1.0; + diff -= 1.0; + } + + double x = fromX + (toX - fromX) * progress; + double y = fromY + (toY - fromY) * progress; + + while (x < 0.0) + x += 1.0; + + QGeoCoordinate result = QWebMercator::mercatorToCoord(QDoubleVector2D(x, y)); + result.setAltitude(from.altitude() + (to.altitude() - from.altitude()) * progress); + + return QVariant::fromValue(result); +} + +QQuickGeoCoordinateAnimation::QQuickGeoCoordinateAnimation(QObject *parent) + : QQuickPropertyAnimation(*(new QQuickGeoCoordinateAnimationPrivate), parent) + +{ + Q_D(QQuickGeoCoordinateAnimation); + d->interpolatorType = qMetaTypeId(); + d->defaultToInterpolatorType = true; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); +} + +QQuickGeoCoordinateAnimation::~QQuickGeoCoordinateAnimation() +{ +} + +/*! + \qmlproperty coordinate CoordinateAnimation::from + This property holds the coordinate where the animation should begin. +*/ +QGeoCoordinate QQuickGeoCoordinateAnimation::from() const +{ + Q_D(const QQuickGeoCoordinateAnimation); + return d->from.value(); +} + +void QQuickGeoCoordinateAnimation::setFrom(const QGeoCoordinate &f) +{ + QGeoMercatorCoordinatePrivate *mercator = new QGeoMercatorCoordinatePrivate(); + QDoubleVector2D fromVector = QWebMercator::coordToMercator(f); + mercator->lat = f.latitude(); + mercator->lng = f.longitude(); + mercator->alt = f.altitude(); + mercator->m_mercatorX = fromVector.x(); + mercator->m_mercatorY = fromVector.y(); + QGeoCoordinate from(*mercator); + QQuickPropertyAnimation::setFrom(QVariant::fromValue(from)); +} + +/*! + \qmlproperty coordinate CoordinateAnimation::to + This property holds the coordinate where the animation should end. +*/ +QGeoCoordinate QQuickGeoCoordinateAnimation::to() const +{ + Q_D(const QQuickGeoCoordinateAnimation); + return d->to.value(); +} + +void QQuickGeoCoordinateAnimation::setTo(const QGeoCoordinate &t) +{ + QGeoMercatorCoordinatePrivate *mercator = new QGeoMercatorCoordinatePrivate(); + QDoubleVector2D toVector = QWebMercator::coordToMercator(t); + mercator->lat = t.latitude(); + mercator->lng = t.longitude(); + mercator->alt = t.altitude(); + mercator->m_mercatorX = toVector.x(); + mercator->m_mercatorY = toVector.y(); + QGeoCoordinate to(*mercator); + QQuickPropertyAnimation::setTo(QVariant::fromValue(to)); +} + +/*! + \qmlproperty enumeration CoordinateAnimation::direction + This property holds the direction of the \l{longitude} animation of the \l{coordinate}. + + Possible values are: + + \list + \li CoordinateAnimation.Shortest (default) - the longitude animation goes in the direction + that produces the shortest animation path. + \li CoordinateAnimation.West - the longitude animation always goes into western direction + and may cross the date line. + \li CoordinateAnimation.East - the longitude animation always goes into eastern direction + and may cross the date line. + \endlist + \since 5.5 +*/ + + +QQuickGeoCoordinateAnimation::Direction QQuickGeoCoordinateAnimation::direction() const +{ + Q_D(const QQuickGeoCoordinateAnimation); + return d->m_direction; +} + +void QQuickGeoCoordinateAnimation::setDirection(QQuickGeoCoordinateAnimation::Direction direction) +{ + Q_D( QQuickGeoCoordinateAnimation); + if (d->m_direction == direction) + return; + + d->m_direction = direction; + switch (direction) { + case West: + d->interpolator = reinterpret_cast(reinterpret_cast(&q_coordinateWestInterpolator)); + break; + case East: + d->interpolator = reinterpret_cast(reinterpret_cast(&q_coordinateEastInterpolator)); + break; + case Shortest: + default: + d->interpolator = reinterpret_cast(reinterpret_cast(&q_coordinateShortestInterpolator)); + break; + } + emit directionChanged(); + +} + +QQuickGeoCoordinateAnimationPrivate::QQuickGeoCoordinateAnimationPrivate(): + m_direction(QQuickGeoCoordinateAnimation::Shortest) +{ +} + +QT_END_NAMESPACE diff --git a/src/imports/positioning/qquickgeocoordinateanimation_p.h b/src/imports/positioning/qquickgeocoordinateanimation_p.h new file mode 100644 index 0000000..95f8dc0 --- /dev/null +++ b/src/imports/positioning/qquickgeocoordinateanimation_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKGEOCOORDINATEANIMATION_P_H +#define QQUICKGEOCOORDINATEANIMATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickGeoCoordinateAnimationPrivate; + +class QQuickGeoCoordinateAnimation : public QQuickPropertyAnimation +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QQuickGeoCoordinateAnimation) + Q_PROPERTY(QGeoCoordinate from READ from WRITE setFrom) + Q_PROPERTY(QGeoCoordinate to READ to WRITE setTo) + Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged) + +public: + enum Direction { + Shortest, + West, + East + }; + Q_ENUM(Direction) + + QQuickGeoCoordinateAnimation(QObject *parent=0); + ~QQuickGeoCoordinateAnimation(); + + QGeoCoordinate from() const; + void setFrom(const QGeoCoordinate &); + + QGeoCoordinate to() const; + void setTo(const QGeoCoordinate &); + + Direction direction() const; + void setDirection(Direction direction); + +Q_SIGNALS: + void directionChanged(); +}; + +QVariant q_coordinateInterpolator(const QGeoCoordinate &from, const QGeoCoordinate &to, qreal progress); + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QQuickGeoCoordinateAnimation) + +#endif // QQUICKCOORDINATEANIMATION_P_H diff --git a/src/imports/positioning/qquickgeocoordinateanimation_p_p.h b/src/imports/positioning/qquickgeocoordinateanimation_p_p.h new file mode 100644 index 0000000..946a38c --- /dev/null +++ b/src/imports/positioning/qquickgeocoordinateanimation_p_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKGEOCOORDINATEANIMATION_P_P_H +#define QQUICKGEOCOORDINATEANIMATION_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qquickgeocoordinateanimation_p.h" +#include + +QT_BEGIN_NAMESPACE + +class QQuickGeoCoordinateAnimationPrivate : public QQuickPropertyAnimationPrivate +{ + Q_DECLARE_PUBLIC(QQuickGeoCoordinateAnimation) +public: + QQuickGeoCoordinateAnimationPrivate(); + QQuickGeoCoordinateAnimation::Direction m_direction; +}; + +QT_END_NAMESPACE + +#endif // QQUICKGEOCOORDINATEANIMATION_P_P_H diff --git a/src/location/configure.json b/src/location/configure.json new file mode 100644 index 0000000..bf15f0b --- /dev/null +++ b/src/location/configure.json @@ -0,0 +1,77 @@ +{ + "module": "location", + "depends": [ + "gui" + ], + + "features": { + "location-labs-plugin": { + "label": "Qt.labs.location experimental QML plugin", + "purpose": "Provides experimental QtLocation QML types", + "section": "Location", + "output": [ "privateFeature" ] + }, + "geoservices_osm": { + "label": "OpenStreetMap", + "purpose": "Provides access to OpenStreetMap geoservices", + "section": "Location", + "condition": "features.concurrent", + "output": [ "privateFeature" ] + }, + "geoservices_here": { + "label": "HERE", + "purpose": "Provides access to HERE geoservices", + "section": "Location", + "output": [ "privateFeature" ] + }, + "geoservices_esri": { + "label": "Esri", + "purpose": "Provides access to Esri geoservices", + "section": "Location", + "output": [ "privateFeature" ] + }, + "geoservices_mapbox": { + "label": "Mapbox", + "purpose": "Provides access to Mapbox geoservices", + "section": "Location", + "output": [ "privateFeature" ] + }, + "geoservices_mapboxgl": { + "label": "MapboxGL", + "purpose": "Provides access to the Mapbox vector maps", + "section": "Location", + "condition": [ + "features.opengl", + "features.c++14", + "!config.qnx && !config.intel_icc && (!config.win32 || config.mingw)" + ], + "output": [ "privateFeature" ] + }, + "geoservices_itemsoverlay": { + "label": "Itemsoverlay", + "purpose": "Provides access to the itemsoverlay maps", + "section": "Location", + "output": [ "privateFeature" ] + } + }, + + "summary": [ + { + "section": "Qt Location", + "entries": [ + "location-labs-plugin", + { + "section": "Geoservice plugins", + "entries": [ + "geoservices_osm", + "geoservices_here", + "geoservices_esri", + "geoservices_mapbox", + "geoservices_mapboxgl", + "geoservices_itemsoverlay" + ] + } + ] + } + ] +} diff --git a/src/location/declarativemaps/declarativemaps.pri b/src/location/declarativemaps/declarativemaps.pri new file mode 100644 index 0000000..3cd7a52 --- /dev/null +++ b/src/location/declarativemaps/declarativemaps.pri @@ -0,0 +1,63 @@ +QT += quick-private network positioning-private qml-private core-private gui-private + +INCLUDEPATH += declarativemaps + +PRIVATE_HEADERS += \ + declarativemaps/error_messages_p.h \ + declarativemaps/qdeclarativegeomapitemview_p.h \ + declarativemaps/qdeclarativegeoserviceprovider_p.h \ + declarativemaps/qdeclarativegeocodemodel_p.h \ + declarativemaps/qdeclarativegeoroutemodel_p.h \ + declarativemaps/qdeclarativegeoroute_p.h \ + declarativemaps/qdeclarativegeoroutesegment_p.h \ + declarativemaps/qdeclarativegeomaneuver_p.h \ + declarativemaps/qdeclarativegeomap_p.h \ + declarativemaps/qdeclarativegeomaptype_p.h \ + declarativemaps/qdeclarativegeomapitembase_p.h \ + declarativemaps/qdeclarativegeomapquickitem_p.h \ + declarativemaps/qdeclarativecirclemapitem_p.h \ + declarativemaps/qdeclarativerectanglemapitem_p.h \ + declarativemaps/qdeclarativepolygonmapitem_p.h \ + declarativemaps/qdeclarativepolylinemapitem_p.h \ + declarativemaps/qdeclarativeroutemapitem_p.h \ + declarativemaps/qdeclarativegeomapparameter_p.h \ + declarativemaps/qgeomapitemgeometry_p.h \ + declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h \ + declarativemaps/locationvaluetypehelper_p.h \ + declarativemaps/qquickgeomapgesturearea_p.h \ + declarativemaps/qdeclarativegeomapitemgroup_p.h \ + declarativemaps/qparameterizableobject_p.h \ + declarativemaps/qgeomapobject_p.h \ + declarativemaps/qgeomapobject_p_p.h \ + ../imports/positioning/qquickgeocoordinateanimation_p.h + +SOURCES += \ + declarativemaps/qdeclarativegeomapitemview.cpp \ + declarativemaps/qdeclarativegeoserviceprovider.cpp \ + declarativemaps/qdeclarativegeocodemodel.cpp \ + declarativemaps/qdeclarativegeoroutemodel.cpp \ + declarativemaps/qdeclarativegeoroute.cpp \ + declarativemaps/qdeclarativegeoroutesegment.cpp \ + declarativemaps/qdeclarativegeomaneuver.cpp \ + declarativemaps/qdeclarativegeomap.cpp \ + declarativemaps/qdeclarativegeomaptype.cpp \ + declarativemaps/qdeclarativegeomapitembase.cpp \ + declarativemaps/qdeclarativegeomapquickitem.cpp \ + declarativemaps/qdeclarativecirclemapitem.cpp \ + declarativemaps/qdeclarativerectanglemapitem.cpp \ + declarativemaps/qdeclarativepolygonmapitem.cpp \ + declarativemaps/qdeclarativepolylinemapitem.cpp \ + declarativemaps/qdeclarativeroutemapitem.cpp \ + declarativemaps/qdeclarativegeomapparameter.cpp \ + declarativemaps/qgeomapitemgeometry.cpp \ + declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp \ + declarativemaps/error_messages.cpp \ + declarativemaps/locationvaluetypehelper.cpp \ + declarativemaps/qquickgeomapgesturearea.cpp \ + declarativemaps/qparameterizableobject.cpp \ + declarativemaps/qdeclarativegeomapitemgroup.cpp \ + declarativemaps/qgeomapobject.cpp \ + ../imports/positioning/qquickgeocoordinateanimation.cpp + +load(qt_build_paths) +LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lpoly2tri$$qtPlatformTargetSuffix() -lclip2tri$$qtPlatformTargetSuffix() diff --git a/src/location/declarativemaps/error_messages.cpp b/src/location/declarativemaps/error_messages.cpp new file mode 100644 index 0000000..7b51b7a --- /dev/null +++ b/src/location/declarativemaps/error_messages.cpp @@ -0,0 +1,52 @@ +/*************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "error_messages_p.h" + +QT_BEGIN_NAMESPACE + +const char CONTEXT_NAME[] = "QtLocationQML"; + +//to-be-translated error string + +const char PLUGIN_PROPERTY_NOT_SET[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin property is not set."); +const char PLUGIN_ERROR[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin Error (%1): %2"); +const char PLUGIN_PROVIDER_ERROR[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin Error (%1): Could not instantiate provider"); +const char PLUGIN_NOT_VALID[] = QT_TRANSLATE_NOOP("QtLocationQML", "Plugin is not valid"); +const char CATEGORIES_NOT_INITIALIZED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Unable to initialize categories"); +const char UNABLE_TO_MAKE_REQUEST[] = QT_TRANSLATE_NOOP("QtLocationQML", "Unable to create request"); +const char INDEX_OUT_OF_RANGE[] = QT_TRANSLATE_NOOP("QtLocationQML", "Index '%1' out of range"); + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/error_messages_p.h b/src/location/declarativemaps/error_messages_p.h new file mode 100644 index 0000000..cdd531d --- /dev/null +++ b/src/location/declarativemaps/error_messages_p.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ERROR_MESSAGES_H +#define ERROR_MESSAGES_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include + +QT_BEGIN_NAMESPACE + +extern const char CONTEXT_NAME[]; +extern const char PLUGIN_PROPERTY_NOT_SET[]; +extern const char PLUGIN_ERROR[]; +extern const char PLUGIN_PROVIDER_ERROR[]; +extern const char PLUGIN_NOT_VALID[]; +extern const char CATEGORIES_NOT_INITIALIZED[]; +extern const char UNABLE_TO_MAKE_REQUEST[]; +extern const char INDEX_OUT_OF_RANGE[]; + +QT_END_NAMESPACE + +#endif // ERROR_MESSAGES_H diff --git a/src/location/declarativemaps/locationvaluetypehelper.cpp b/src/location/declarativemaps/locationvaluetypehelper.cpp new file mode 100644 index 0000000..cda37b5 --- /dev/null +++ b/src/location/declarativemaps/locationvaluetypehelper.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "locationvaluetypehelper_p.h" +#include +#include +#include + + +QGeoCoordinate parseCoordinate(const QJSValue &value, bool *ok) +{ + QGeoCoordinate c; + if (ok) + *ok = false; + + if (value.isObject()) { + if (value.hasProperty(QStringLiteral("latitude"))) + c.setLatitude(value.property(QStringLiteral("latitude")).toNumber()); + if (value.hasProperty(QStringLiteral("longitude"))) + c.setLongitude(value.property(QStringLiteral("longitude")).toNumber()); + if (value.hasProperty(QStringLiteral("altitude"))) + c.setAltitude(value.property(QStringLiteral("altitude")).toNumber()); + + if (ok) + *ok = true; + } + + return c; +} + +QGeoCoordinate parseCoordinate(const QVariant &value, bool *ok) +{ + QGeoCoordinate c; + if (ok) + *ok = false; + + if (value.canConvert()) { + c = value.value(); + if (ok) + *ok = true; + } else if (value.type() == QVariant::Map) { + const QVariantMap &map = value.toMap(); + + if (map.contains(QStringLiteral("latitude"))) + c.setLatitude(map.value(QStringLiteral("latitude")).toDouble()); + if (map.contains(QStringLiteral("longitude"))) + c.setLongitude(map.value(QStringLiteral("longitude")).toDouble()); + if (map.contains(QStringLiteral("altitude"))) + c.setAltitude(map.value(QStringLiteral("altitude")).toDouble()); + + if (ok) + *ok = c.isValid(); // Not considering the case where the map is valid but containing NaNs. + } + + return c; +} + +QGeoRectangle parseRectangle(const QJSValue &value, bool *ok) +{ + QGeoRectangle r; + + *ok = false; + + if (value.isObject()) { + if (value.hasProperty(QStringLiteral("bottomLeft"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("bottomLeft")), ok); + if (*ok) + r.setBottomLeft(c); + } + if (value.hasProperty(QStringLiteral("bottomRight"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("bottomRight")), ok); + if (*ok) + r.setBottomRight(c); + } + if (value.hasProperty(QStringLiteral("topLeft"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("topLeft")), ok); + if (*ok) + r.setTopLeft(c); + } + if (value.hasProperty(QStringLiteral("topRight"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("topRight")), ok); + if (*ok) + r.setTopRight(c); + } + if (value.hasProperty(QStringLiteral("center"))) { + QGeoCoordinate c = parseCoordinate(value.property(QStringLiteral("center")), ok); + if (*ok) + r.setCenter(c); + } + if (value.hasProperty(QStringLiteral("height"))) + r.setHeight(value.property(QStringLiteral("height")).toNumber()); + if (value.hasProperty(QStringLiteral("width"))) + r.setWidth(value.property(QStringLiteral("width")).toNumber()); + } + + return r; +} + +QGeoCircle parseCircle(const QJSValue &value, bool *ok) +{ + QGeoCircle c; + + *ok = false; + + if (value.isObject()) { + if (value.hasProperty(QStringLiteral("center"))) { + QGeoCoordinate coord = parseCoordinate(value.property(QStringLiteral("center")), ok); + if (*ok) + c.setCenter(coord); + } + if (value.hasProperty(QStringLiteral("radius"))) + c.setRadius(value.property(QStringLiteral("radius")).toNumber()); + } + + return c; +} + +QJSValue fromList(const QObject *object, const QList &list) +{ + QQmlContext *context = QQmlEngine::contextForObject(object); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped pathArray(scope, v4->newArrayObject(list.length())); + int i = 0; + for (const auto &val : list) { + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(val))); + pathArray->putIndexed(i++, cv); + } + + return QJSValue(v4, pathArray.asReturnedValue()); +} + +QList toList(const QObject *object, const QJSValue &value) +{ + if (!value.isArray()) + return {}; + + QList pathList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoCoordinate c = parseCoordinate(value.property(i), &ok); + + if (!ok || !c.isValid()) { + qmlWarning(object) << "Unsupported path type"; + return {}; + } + + pathList.append(c); + } + + return pathList; +} diff --git a/src/location/declarativemaps/locationvaluetypehelper_p.h b/src/location/declarativemaps/locationvaluetypehelper_p.h new file mode 100644 index 0000000..79b4b6f --- /dev/null +++ b/src/location/declarativemaps/locationvaluetypehelper_p.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOCATION_VALUE_TYPE_HELPER +#define LOCATION_VALUE_TYPE_HELPER + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QGeoCoordinate Q_LOCATION_PRIVATE_EXPORT parseCoordinate(const QJSValue &value, bool *ok = nullptr); +QGeoCoordinate Q_LOCATION_PRIVATE_EXPORT parseCoordinate(const QVariant &value, bool *ok = nullptr); +QGeoRectangle Q_LOCATION_PRIVATE_EXPORT parseRectangle(const QJSValue &value, bool *ok); +QGeoCircle Q_LOCATION_PRIVATE_EXPORT parseCircle(const QJSValue &value, bool *ok); +QJSValue Q_LOCATION_PRIVATE_EXPORT fromList(const QObject *object, const QList &list); +QList Q_LOCATION_PRIVATE_EXPORT toList(const QObject *object, const QJSValue &value); +#endif diff --git a/src/location/declarativemaps/mapitemviewdelegateincubator_p.h b/src/location/declarativemaps/mapitemviewdelegateincubator_p.h new file mode 100644 index 0000000..6ee9f6a --- /dev/null +++ b/src/location/declarativemaps/mapitemviewdelegateincubator_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Jolla Ltd, author: Aaron McCarthy +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef MAPITEMVIEWDELEGATEINCUBATOR_H +#define MAPITEMVIEWDELEGATEINCUBATOR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoMapItemView; +class QDeclarativeGeoMapItemViewItemData; + +class Q_LOCATION_PRIVATE_EXPORT MapItemViewDelegateIncubator : public QQmlIncubator +{ +public: + MapItemViewDelegateIncubator(QDeclarativeGeoMapItemView *view, QDeclarativeGeoMapItemViewItemData *itemData, bool batched = true); + +protected: + void statusChanged(Status status) override; + +private: + QDeclarativeGeoMapItemView *m_view; + QDeclarativeGeoMapItemViewItemData *m_itemData; + bool m_batched; + + friend class QDeclarativeGeoMapItemView; +}; + +QT_END_NAMESPACE + +#endif // MAPITEMVIEWDELEGATEINCUBATOR_H diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem.cpp b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp new file mode 100644 index 0000000..608260c --- /dev/null +++ b/src/location/declarativemaps/qdeclarativecirclemapitem.cpp @@ -0,0 +1,723 @@ +/*************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecirclemapitem_p.h" +#include "qdeclarativepolygonmapitem_p.h" + +#include "qwebmercator_p.h" +#include + +#include +#include + +#include +#include +#include +#include + +#include "qdoublevector2d_p.h" +#include "qlocationutils_p.h" +#include "qgeocircle.h" + +/* poly2tri triangulator includes */ +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapCircle + \instantiates QDeclarativeCircleMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.5 + + \brief The MapCircle type displays a geographic circle on a Map. + + The MapCircle type displays a geographic circle on a Map, which + consists of all points that are within a set distance from one + central point. Depending on map projection, a geographic circle + may not always be a perfect circle on the screen: for instance, in + the Mercator projection, circles become ovoid in shape as they near + the poles. To display a perfect screen circle around a point, use a + MapQuickItem containing a relevant Qt Quick type instead. + + By default, the circle is displayed as a 1 pixel black border with + no fill. To change its appearance, use the color, border.color + and border.width properties. + + Internally, a MapCircle is implemented as a many-sided polygon. To + calculate the radius points it uses a spherical model of the Earth, + similar to the atDistanceAndAzimuth method of the \l {coordinate} + type. These two things can occasionally have implications for the + accuracy of the circle's shape, depending on position and map + projection. + + \note Dragging a MapCircle (through the use of \l MouseArea) + causes new points to be generated at the same distance (in meters) + from the center. This is in contrast to other map items which store + their dimensions in terms of latitude and longitude differences between + vertices. + + \section2 Performance + + MapCircle performance is almost equivalent to that of a MapPolygon with + the same number of vertices. There is a small amount of additional + overhead with respect to calculating the vertices first. + + Like the other map objects, MapCircle is normally drawn without a smooth + appearance. Setting the opacity property will force the object to be + blended, which decreases performance considerably depending on the graphics + hardware in use. + + \section2 Example Usage + + The following snippet shows a map containing a MapCircle, centered at + the coordinate (-27, 153) with a radius of 5km. The circle is + filled in green, with a 3 pixel black border. + + \code + Map { + MapCircle { + center { + latitude: -27.5 + longitude: 153.0 + } + radius: 5000.0 + color: 'green' + border.width: 3 + } + } + \endcode + + \image api-mapcircle.png +*/ + +static const int CircleSamples = 128; + +struct Vertex +{ + QVector2D position; +}; + +QGeoMapCircleGeometry::QGeoMapCircleGeometry() +{ +} + +/*! + \internal +*/ +void QGeoMapCircleGeometry::updateScreenPointsInvert(const QList &circlePath, const QGeoMap &map) +{ + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + // Not checking for !screenDirty anymore, as everything is now recalculated. + clear(); + if (map.viewportWidth() == 0 || map.viewportHeight() == 0 || circlePath.size() < 3) // a circle requires at least 3 points; + return; + + /* + * No special case for no tilting as these items are very rare, and usually at most one per map. + * + * Approach: + * 1) subtract the circle from a rectangle filling the whole map, *in wrapped mercator space* + * 2) clip the resulting geometries against the visible region, *in wrapped mercator space* + * 3) create a QPainterPath with each of the resulting polygons projected to screen + * 4) use qTriangulate() to triangulate the painter path + */ + + // 1) + const double topLati = QLocationUtils::mercatorMaxLatitude(); + const double bottomLati = -(QLocationUtils::mercatorMaxLatitude()); + const double leftLongi = QLocationUtils::mapLeftLongitude(map.cameraData().center().longitude()); + const double rightLongi = QLocationUtils::mapRightLongitude(map.cameraData().center().longitude()); + + srcOrigin_ = QGeoCoordinate(topLati,leftLongi); + const QDoubleVector2D tl = p.geoToWrappedMapProjection(QGeoCoordinate(topLati,leftLongi)); + const QDoubleVector2D tr = p.geoToWrappedMapProjection(QGeoCoordinate(topLati,rightLongi)); + const QDoubleVector2D br = p.geoToWrappedMapProjection(QGeoCoordinate(bottomLati,rightLongi)); + const QDoubleVector2D bl = p.geoToWrappedMapProjection(QGeoCoordinate(bottomLati,leftLongi)); + + QList fill; + fill << tl << tr << br << bl; + + QList hole; + for (const QDoubleVector2D &c: circlePath) + hole << p.wrapMapProjection(c); + + c2t::clip2tri clipper; + clipper.addSubjectPath(QClipperUtils::qListToPath(fill), true); + clipper.addClipPolygon(QClipperUtils::qListToPath(hole)); + Paths difference = clipper.execute(c2t::clip2tri::Difference, QtClipperLib::pftEvenOdd, QtClipperLib::pftEvenOdd); + + // 2) + QDoubleVector2D lb = p.geoToWrappedMapProjection(srcOrigin_); + QList > clippedPaths; + const QList &visibleRegion = p.visibleGeometry(); + if (visibleRegion.size()) { + clipper.clearClipper(); + for (const Path &p: difference) + clipper.addSubjectPath(p, true); + clipper.addClipPolygon(QClipperUtils::qListToPath(visibleRegion)); + Paths res = clipper.execute(c2t::clip2tri::Intersection, QtClipperLib::pftEvenOdd, QtClipperLib::pftEvenOdd); + clippedPaths = QClipperUtils::pathsToQList(res); + + // 2.1) update srcOrigin_ with the point with minimum X/Y + lb = QDoubleVector2D(qInf(), qInf()); + for (const QList &path: clippedPaths) { + for (const QDoubleVector2D &p: path) { + if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) { + lb = p; + } + } + } + if (qIsInf(lb.x())) + return; + + // Prevent the conversion to and from clipper from introducing negative offsets which + // in turn will make the geometry wrap around. + lb.setX(qMax(tl.x(), lb.x())); + srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb)); + } else { + clippedPaths = QClipperUtils::pathsToQList(difference); + } + + //3) + QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(lb); + + QPainterPath ppi; + for (const QList &path: clippedPaths) { + QDoubleVector2D lastAddedPoint; + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); + //point = point - origin; // Do this using ppi.translate() + + if (i == 0) { + ppi.moveTo(point.toPointF()); + lastAddedPoint = point; + } else { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + ppi.lineTo(point.toPointF()); + lastAddedPoint = point; + } + } + } + ppi.closeSubpath(); + } + ppi.translate(-1 * origin.toPointF()); + + QTriangleSet ts = qTriangulate(ppi); + qreal *vx = ts.vertices.data(); + + screenIndices_.reserve(ts.indices.size()); + screenVertices_.reserve(ts.vertices.size()); + + if (ts.indices.type() == QVertexIndexVector::UnsignedInt) { + const quint32 *ix = reinterpret_cast(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); ++i) + screenIndices_ << ix[i]; + } else { + const quint16 *ix = reinterpret_cast(ts.indices.data()); + for (int i = 0; i < (ts.indices.size()/3*3); ++i) + screenIndices_ << ix[i]; + } + for (int i = 0; i < (ts.vertices.size()/2*2); i += 2) + screenVertices_ << QPointF(vx[i], vx[i + 1]); + + screenBounds_ = ppi.boundingRect(); + sourceBounds_ = screenBounds_; +} + +bool QDeclarativeCircleMapItem::crossEarthPole(const QGeoCoordinate ¢er, qreal distance) +{ + qreal poleLat = 90; + QGeoCoordinate northPole = QGeoCoordinate(poleLat, center.longitude()); + QGeoCoordinate southPole = QGeoCoordinate(-poleLat, center.longitude()); + // approximate using great circle distance + qreal distanceToNorthPole = center.distanceTo(northPole); + qreal distanceToSouthPole = center.distanceTo(southPole); + if (distanceToNorthPole < distance || distanceToSouthPole < distance) + return true; + return false; +} + +void QDeclarativeCircleMapItem::calculatePeripheralPoints(QList &path, + const QGeoCoordinate ¢er, + qreal distance, + int steps, + QGeoCoordinate &leftBound) +{ + // Calculate points based on great-circle distance + // Calculation is the same as GeoCoordinate's atDistanceAndAzimuth function + // but tweaked here for computing multiple points + + // pre-calculations + steps = qMax(steps, 3); + qreal centerLon = center.longitude(); + qreal minLon = centerLon; + qreal latRad = QLocationUtils::radians(center.latitude()); + qreal lonRad = QLocationUtils::radians(centerLon); + qreal cosLatRad = std::cos(latRad); + qreal sinLatRad = std::sin(latRad); + qreal ratio = (distance / QLocationUtils::earthMeanRadius()); + qreal cosRatio = std::cos(ratio); + qreal sinRatio = std::sin(ratio); + qreal sinLatRad_x_cosRatio = sinLatRad * cosRatio; + qreal cosLatRad_x_sinRatio = cosLatRad * sinRatio; + int idx = 0; + for (int i = 0; i < steps; ++i) { + qreal azimuthRad = 2 * M_PI * i / steps; + qreal resultLatRad = std::asin(sinLatRad_x_cosRatio + + cosLatRad_x_sinRatio * std::cos(azimuthRad)); + qreal resultLonRad = lonRad + std::atan2(std::sin(azimuthRad) * cosLatRad_x_sinRatio, + cosRatio - sinLatRad * std::sin(resultLatRad)); + qreal lat2 = QLocationUtils::degrees(resultLatRad); + qreal lon2 = QLocationUtils::wrapLong(QLocationUtils::degrees(resultLonRad)); + + path << QGeoCoordinate(lat2, lon2, center.altitude()); + // Consider only points in the left half of the circle for the left bound. + if (azimuthRad > M_PI) { + if (lon2 > centerLon) // if point and center are on different hemispheres + lon2 -= 360; + if (lon2 < minLon) { + minLon = lon2; + idx = i; + } + } + } + leftBound = path.at(idx); +} + +QDeclarativeCircleMapItem::QDeclarativeCircleMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true), + updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&border_, SIGNAL(colorChanged(QColor)), + this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&border_, SIGNAL(widthChanged(qreal)), + this, SLOT(markSourceDirtyAndUpdate())); + + // assume that circles are not self-intersecting + // to speed up processing + // FIXME: unfortunately they self-intersect at the poles due to current drawing method + // so the line is commented out until fixed + //geometry_.setAssumeSimple(true); + +} + +QDeclarativeCircleMapItem::~QDeclarativeCircleMapItem() +{ +} + +/*! + \qmlpropertygroup Location::MapCircle::border + \qmlproperty int MapCircle::border.width + \qmlproperty color MapCircle::border.color + + This property is part of the border group property. + The border property holds the width and color used to draw the border of the circle. + The width is in pixels and is independent of the zoom level of the map. + + The default values correspond to a black border with a width of 1 pixel. + For no line, use a width of 0 or a transparent color. +*/ +QDeclarativeMapLineProperties *QDeclarativeCircleMapItem::border() +{ + return &border_; +} + +void QDeclarativeCircleMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +void QDeclarativeCircleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (!map) + return; + updateCirclePath(); + markSourceDirtyAndUpdate(); +} + +/*! + \qmlproperty coordinate MapCircle::center + + This property holds the central point about which the circle is defined. + + \sa radius +*/ +void QDeclarativeCircleMapItem::setCenter(const QGeoCoordinate ¢er) +{ + if (circle_.center() == center) + return; + + circle_.setCenter(center); + updateCirclePath(); + markSourceDirtyAndUpdate(); + emit centerChanged(center); +} + +QGeoCoordinate QDeclarativeCircleMapItem::center() +{ + return circle_.center(); +} + +/*! + \qmlproperty color MapCircle::color + + This property holds the fill color of the circle when drawn. For no fill, + use a transparent color. +*/ +void QDeclarativeCircleMapItem::setColor(const QColor &color) +{ + if (color_ == color) + return; + color_ = color; + dirtyMaterial_ = true; + update(); + emit colorChanged(color_); +} + +QColor QDeclarativeCircleMapItem::color() const +{ + return color_; +} + +/*! + \qmlproperty real MapCircle::radius + + This property holds the radius of the circle, in meters on the ground. + + \sa center +*/ +void QDeclarativeCircleMapItem::setRadius(qreal radius) +{ + if (circle_.radius() == radius) + return; + + circle_.setRadius(radius); + updateCirclePath(); + markSourceDirtyAndUpdate(); + emit radiusChanged(radius); +} + +qreal QDeclarativeCircleMapItem::radius() const +{ + return circle_.radius(); +} + +/*! + \qmlproperty real MapCircle::opacity + + This property holds the opacity of the item. Opacity is specified as a + number between 0 (fully transparent) and 1 (fully opaque). The default is 1. + + An item with 0 opacity will still receive mouse events. To stop mouse events, set the + visible property of the item to false. +*/ + +/*! + \internal +*/ +QSGNode *QDeclarativeCircleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + MapPolygonNode *node = static_cast(oldNode); + + if (!node) + node = new MapPolygonNode(); + + //TODO: update only material + if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { + node->update(color_, border_.color(), &geometry_, &borderGeometry_); + geometry_.setPreserveGeometry(false); + borderGeometry_.setPreserveGeometry(false); + geometry_.markClean(); + borderGeometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +/*! + \internal +*/ +void QDeclarativeCircleMapItem::updatePolish() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + if (!circle_.isValid()) { + geometry_.clear(); + borderGeometry_.clear(); + setWidth(0); + setHeight(0); + return; + } + + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + QScopedValueRollback rollback(updatingGeometry_); + updatingGeometry_ = true; + + QList circlePath = circlePath_; + + int pathCount = circlePath.size(); + bool preserve = preserveCircleGeometry(circlePath, circle_.center(), circle_.radius(), p); + // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft()); + // to fix QTBUG-62154 + geometry_.setPreserveGeometry(true, leftBound_); // to set the geoLeftBound_ + geometry_.setPreserveGeometry(preserve, leftBound_); + + bool invertedCircle = false; + if (crossEarthPole(circle_.center(), circle_.radius()) && circlePath.size() == pathCount) { + geometry_.updateScreenPointsInvert(circlePath, *map()); // invert fill area for really huge circles + invertedCircle = true; + } else { + geometry_.updateSourcePoints(*map(), circlePath); + geometry_.updateScreenPoints(*map(), border_.width()); + } + + borderGeometry_.clear(); + QList geoms; + geoms << &geometry_; + + if (border_.color() != Qt::transparent && border_.width() > 0) { + QList closedPath = circlePath; + closedPath << closedPath.first(); + + if (invertedCircle) { + closedPath = circlePath_; + closedPath << closedPath.first(); + std::reverse(closedPath.begin(), closedPath.end()); + } + + borderGeometry_.setPreserveGeometry(true, leftBound_); + borderGeometry_.setPreserveGeometry(preserve, leftBound_); + + // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. + const QGeoCoordinate &geometryOrigin = geometry_.origin(); + + borderGeometry_.srcPoints_.clear(); + borderGeometry_.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); + borderGeometry_.updateScreenPoints(*map(), border_.width()); + geoms << &borderGeometry_; + } else { + borderGeometry_.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + + if (invertedCircle || !preserve) { + setWidth(combined.width()); + setHeight(combined.height()); + setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); + } else { + setWidth(combined.width() + 2 * border_.width()); + setHeight(combined.height() + 2 * border_.width()); + } + + // No offsetting here, even in normal case, because first point offset is already translated + setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); +} + +/*! + \internal +*/ +void QDeclarativeCircleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + markSourceDirtyAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativeCircleMapItem::updateCirclePath() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + QList path; + calculatePeripheralPoints(path, circle_.center(), circle_.radius(), CircleSamples, leftBound_); + circlePath_.clear(); + for (const QGeoCoordinate &c : path) + circlePath_ << p.geoToMapProjection(c); +} + +/*! + \internal +*/ +bool QDeclarativeCircleMapItem::contains(const QPointF &point) const +{ + return (geometry_.contains(point) || borderGeometry_.contains(point)); +} + +const QGeoShape &QDeclarativeCircleMapItem::geoShape() const +{ + return circle_; +} + +QGeoMap::ItemType QDeclarativeCircleMapItem::itemType() const +{ + return QGeoMap::MapCircle; +} + +/*! + \internal +*/ +void QDeclarativeCircleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (!map() || !circle_.isValid() || updatingGeometry_ || newGeometry == oldGeometry) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + + QDoubleVector2D newPoint = QDoubleVector2D(x(),y()) + QDoubleVector2D(width(), height()) / 2; + QGeoCoordinate newCoordinate = map()->geoProjection().itemPositionToCoordinate(newPoint, false); + if (newCoordinate.isValid()) + setCenter(newCoordinate); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +bool QDeclarativeCircleMapItem::preserveCircleGeometry (QList &path, + const QGeoCoordinate ¢er, qreal distance, const QGeoProjectionWebMercator &p) +{ + // if circle crosses north/south pole, then don't preserve circular shape, + if ( crossEarthPole(center, distance)) { + updateCirclePathForRendering(path, center, distance, p); + return false; + } + return true; + +} + +/* + * A workaround for circle path to be drawn correctly using a polygon geometry + * This method generates a polygon like + * _____________ + * | | + * \ / + * | | + * / \ + * | | + * ------------- + * + * or a polygon like + * + * ______________ + * | ____ | + * \__/ \__/ + */ +void QDeclarativeCircleMapItem::updateCirclePathForRendering(QList &path, + const QGeoCoordinate ¢er, + qreal distance, const QGeoProjectionWebMercator &p) +{ + const qreal poleLat = 90; + const qreal distanceToNorthPole = center.distanceTo(QGeoCoordinate(poleLat, 0)); + const qreal distanceToSouthPole = center.distanceTo(QGeoCoordinate(-poleLat, 0)); + bool crossNorthPole = distanceToNorthPole < distance; + bool crossSouthPole = distanceToSouthPole < distance; + + QList wrapPathIndex; + QDoubleVector2D prev = p.wrapMapProjection(path.at(0)); + + for (int i = 1; i <= path.count(); ++i) { + int index = i % path.count(); + QDoubleVector2D point = p.wrapMapProjection(path.at(index)); + double diff = qAbs(point.x() - prev.x()); + if (diff > 0.5) { + continue; + } + } + + // find the points in path where wrapping occurs + for (int i = 1; i <= path.count(); ++i) { + int index = i % path.count(); + QDoubleVector2D point = p.wrapMapProjection(path.at(index)); + if ( (qAbs(point.x() - prev.x())) >= 0.5 ) { + wrapPathIndex << index; + if (wrapPathIndex.size() == 2 || !(crossNorthPole && crossSouthPole)) + break; + } + prev = point; + } + // insert two additional coords at top/bottom map corners of the map for shape + // to be drawn correctly + if (wrapPathIndex.size() > 0) { + qreal newPoleLat = 0; // 90 latitude + QDoubleVector2D wrapCoord = path.at(wrapPathIndex[0]); + if (wrapPathIndex.size() == 2) { + QDoubleVector2D wrapCoord2 = path.at(wrapPathIndex[1]); + if (wrapCoord2.y() < wrapCoord.y()) + newPoleLat = 1; // -90 latitude + } else if (center.latitude() < 0) { + newPoleLat = 1; // -90 latitude + } + for (int i = 0; i < wrapPathIndex.size(); ++i) { + int index = wrapPathIndex[i] == 0 ? 0 : wrapPathIndex[i] + i*2; + int prevIndex = (index - 1) < 0 ? (path.count() - 1): index - 1; + QDoubleVector2D coord0 = path.at(prevIndex); + QDoubleVector2D coord1 = path.at(index); + coord0.setY(newPoleLat); + coord1.setY(newPoleLat); + path.insert(index ,coord1); + path.insert(index, coord0); + newPoleLat = 1.0 - newPoleLat; + } + } +} + +////////////////////////////////////////////////////////////////////// + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativecirclemapitem_p.h b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h new file mode 100644 index 0000000..2e8c56f --- /dev/null +++ b/src/location/declarativemaps/qdeclarativecirclemapitem_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECIRCLEMAPITEM_H +#define QDECLARATIVECIRCLEMAPITEM_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapCircleGeometry : public QGeoMapPolygonGeometry +{ +public: + QGeoMapCircleGeometry(); + + void updateScreenPointsInvert(const QList &circlePath, const QGeoMap &map); +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCircleMapItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) + Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + +public: + explicit QDeclarativeCircleMapItem(QQuickItem *parent = 0); + ~QDeclarativeCircleMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override; + + QGeoCoordinate center(); + void setCenter(const QGeoCoordinate ¢er); + + qreal radius() const; + void setRadius(qreal radius); + + QColor color() const; + void setColor(const QColor &color); + + QDeclarativeMapLineProperties *border(); + + bool contains(const QPointF &point) const override; + const QGeoShape &geoShape() const override; + QGeoMap::ItemType itemType() const override; + + static bool crossEarthPole(const QGeoCoordinate ¢er, qreal distance); + static void calculatePeripheralPoints(QList &path, const QGeoCoordinate ¢er, + qreal distance, int steps, QGeoCoordinate &leftBound); + static bool preserveCircleGeometry(QList &path, const QGeoCoordinate ¢er, + qreal distance, const QGeoProjectionWebMercator &p); + static void updateCirclePathForRendering(QList &path, const QGeoCoordinate ¢er, + qreal distance, const QGeoProjectionWebMercator &p); + +Q_SIGNALS: + void centerChanged(const QGeoCoordinate ¢er); + void radiusChanged(qreal radius); + void colorChanged(const QColor &color); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void updatePolish() override; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; + +private: + void updateCirclePath(); + +private: + QGeoCircle circle_; + QDeclarativeMapLineProperties border_; + QColor color_; + QList circlePath_; + QGeoCoordinate leftBound_; + bool dirtyMaterial_; + QGeoMapCircleGeometry geometry_; + QGeoMapPolylineGeometry borderGeometry_; + bool updatingGeometry_; +}; + +////////////////////////////////////////////////////////////////////// + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeCircleMapItem) + +#endif /* QDECLARATIVECIRCLEMAPITEM_H */ diff --git a/src/location/declarativemaps/qdeclarativegeocodemodel.cpp b/src/location/declarativemaps/qdeclarativegeocodemodel.cpp new file mode 100644 index 0000000..43ce95a --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeocodemodel.cpp @@ -0,0 +1,732 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeocodemodel_p.h" +#include "error_messages_p.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype GeocodeModel + \instantiates QDeclarativeGeocodeModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-geocoding + \since QtLocation 5.5 + + \brief The GeocodeModel type provides support for searching operations + related to geographic information. + + The GeocodeModel type is used as part of a model/view grouping to + match addresses or search strings with geographic locations. How the + geographic locations generated are used or displayed is decided by any + Views attached to the GeocodeModel (for example a \l MapItemView or \l{ListView}). + + Like \l Map and \l RouteModel, all the data for a GeocodeModel to work + comes from a services plugin. This is contained in the \l{plugin} property, + and this must be set before the GeocodeModel can do any useful work. + + Once the plugin is set, the \l{query} property can be used to specify the + address or search string to match. If \l{autoUpdate} is enabled, the Model + will update its output automatically. Otherwise, the \l{update} method may + be used. By default, autoUpdate is disabled. + + The data stored and returned in the GeocodeModel consists of \l [QML] {Location} + objects, as a list with the role name "locationData". See the documentation + for \l [QML] {Location} for further details on its structure and contents. + + \section2 Example Usage + + The following snippet is two-part, showing firstly the declaration of + objects, and secondly a short piece of procedural code using it. We set + the geocodeModel's \l{autoUpdate} property to false, and call \l{update} once + the query is set up. In this case, as we use a string value in \l{query}, + only one update would occur, even with autoUpdate enabled. However, if we + provided an \l{Address} object we may inadvertently trigger multiple + requests whilst setting its properties. + + \code + Plugin { + id: aPlugin + } + + GeocodeModel { + id: geocodeModel + plugin: aPlugin + autoUpdate: false + } + \endcode + + \code + { + geocodeModel.query = "53 Brandl St, Eight Mile Plains, Australia" + geocodeModel.update() + } + \endcode +*/ + +/*! + \qmlsignal QtLocation::GeocodeModel::locationsChanged() + + This signal is emitted when locations in the model have changed. + + \sa count +*/ + + +QDeclarativeGeocodeModel::QDeclarativeGeocodeModel(QObject *parent) +: QAbstractListModel(parent), autoUpdate_(false), complete_(false), reply_(0), plugin_(0), + status_(QDeclarativeGeocodeModel::Null), error_(QDeclarativeGeocodeModel::NoError), + address_(0), limit_(-1), offset_(0) +{ +} + +QDeclarativeGeocodeModel::~QDeclarativeGeocodeModel() +{ + qDeleteAll(declarativeLocations_); + declarativeLocations_.clear(); + delete reply_; +} + +/*! + \internal + From QQmlParserStatus +*/ +void QDeclarativeGeocodeModel::componentComplete() +{ + complete_ = true; + if (autoUpdate_) + update(); +} + +/*! + \qmlmethod void QtLocation::GeocodeModel::update() + + Instructs the GeocodeModel to update its data. This is most useful + when \l autoUpdate is disabled, to force a refresh when the query + has been changed. +*/ +void QDeclarativeGeocodeModel::update() +{ + if (!complete_) + return; + + if (!plugin_) { + setError(EngineNotSetError, tr("Cannot geocode, plugin not set.")); + return; + } + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QGeoCodingManager *geocodingManager = serviceProvider->geocodingManager(); + if (!geocodingManager) { + setError(EngineNotSetError, tr("Cannot geocode, geocode manager not set.")); + return; + } + if (!coordinate_.isValid() && (!address_ || address_->address().isEmpty()) && + (searchString_.isEmpty())) { + setError(ParseError, tr("Cannot geocode, valid query not set.")); + return; + } + abortRequest(); // abort possible previous requests + setError(NoError, QString()); + + if (coordinate_.isValid()) { + setStatus(QDeclarativeGeocodeModel::Loading); + reply_ = geocodingManager->reverseGeocode(coordinate_, boundingArea_); + if (reply_->isFinished()) { + if (reply_->error() == QGeoCodeReply::NoError) { + geocodeFinished(reply_); + } else { + geocodeError(reply_, reply_->error(), reply_->errorString()); + } + } + } else if (address_) { + setStatus(QDeclarativeGeocodeModel::Loading); + reply_ = geocodingManager->geocode(address_->address(), boundingArea_); + if (reply_->isFinished()) { + if (reply_->error() == QGeoCodeReply::NoError) { + geocodeFinished(reply_); + } else { + geocodeError(reply_, reply_->error(), reply_->errorString()); + } + } + } else if (!searchString_.isEmpty()) { + setStatus(QDeclarativeGeocodeModel::Loading); + reply_ = geocodingManager->geocode(searchString_, limit_, offset_, boundingArea_); + if (reply_->isFinished()) { + if (reply_->error() == QGeoCodeReply::NoError) { + geocodeFinished(reply_); + } else { + geocodeError(reply_, reply_->error(), reply_->errorString()); + } + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::abortRequest() +{ + if (reply_) { + reply_->abort(); + reply_->deleteLater(); + reply_ = 0; + } +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::queryContentChanged() +{ + if (autoUpdate_) + update(); +} + +/*! + \internal + From QAbstractListModel +*/ +int QDeclarativeGeocodeModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return declarativeLocations_.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativeGeocodeModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + if (index.row() >= declarativeLocations_.count()) + return QVariant(); + if (role == QDeclarativeGeocodeModel::LocationRole) { + QObject *locationObject = declarativeLocations_.at(index.row()); + Q_ASSERT(locationObject); + return QVariant::fromValue(locationObject); + } + return QVariant(); +} + +QHash QDeclarativeGeocodeModel::roleNames() const +{ + QHash roleNames = QAbstractItemModel::roleNames(); + roleNames.insert(LocationRole, "locationData"); + return roleNames; +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (plugin_ == plugin) + return; + + reset(); // reset the model + plugin_ = plugin; + if (complete_) + emit pluginChanged(); + + if (!plugin) + return; + + if (plugin_->isAttached()) { + pluginReady(); + } else { + connect(plugin_, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::pluginReady() +{ + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + QGeoCodingManager *geocodingManager = serviceProvider->geocodingManager(); + + if (serviceProvider->error() != QGeoServiceProvider::NoError) { + QDeclarativeGeocodeModel::GeocodeError newError = UnknownError; + switch (serviceProvider->error()) { + case QGeoServiceProvider::NotSupportedError: + newError = EngineNotSetError; break; + case QGeoServiceProvider::UnknownParameterError: + newError = UnknownParameterError; break; + case QGeoServiceProvider::MissingRequiredParameterError: + newError = MissingRequiredParameterError; break; + case QGeoServiceProvider::ConnectionError: + newError = CommunicationError; break; + default: + break; + } + + setError(newError, serviceProvider->errorString()); + return; + } + + if (!geocodingManager) { + setError(EngineNotSetError,tr("Plugin does not support (reverse) geocoding.")); + return; + } + + connect(geocodingManager, SIGNAL(finished(QGeoCodeReply*)), + this, SLOT(geocodeFinished(QGeoCodeReply*))); + connect(geocodingManager, SIGNAL(error(QGeoCodeReply*,QGeoCodeReply::Error,QString)), + this, SLOT(geocodeError(QGeoCodeReply*,QGeoCodeReply::Error,QString))); + + if (complete_ && autoUpdate_) + update(); +} + +/*! + \qmlproperty Plugin QtLocation::GeocodeModel::plugin + + This property holds the plugin that provides the actual geocoding service. + Note that all plugins do not necessarily provide geocoding (could for example provide + only routing or maps). + + \sa Plugin +*/ + +QDeclarativeGeoServiceProvider *QDeclarativeGeocodeModel::plugin() const +{ + return plugin_; +} + +void QDeclarativeGeocodeModel::setBounds(const QVariant &boundingArea) +{ + QGeoShape s; + + if (boundingArea.userType() == qMetaTypeId()) + s = boundingArea.value(); + else if (boundingArea.userType() == qMetaTypeId()) + s = boundingArea.value(); + else if (boundingArea.userType() == qMetaTypeId()) + s = boundingArea.value(); + + + if (boundingArea_ == s) + return; + + boundingArea_ = s; + emit boundsChanged(); +} + +/*! + \qmlproperty geoshape QtLocation::GeocodeModel::bounds + + This property holds the bounding area used to limit the results to those + within the area. This is particularly useful if query is only partially filled out, + as the service will attempt to (reverse) geocode all matches for the specified data. + + Accepted types are \l {georectangle} and + \l {geocircle}. +*/ +QVariant QDeclarativeGeocodeModel::bounds() const +{ + if (boundingArea_.type() == QGeoShape::RectangleType) + return QVariant::fromValue(QGeoRectangle(boundingArea_)); + else if (boundingArea_.type() == QGeoShape::CircleType) + return QVariant::fromValue(QGeoCircle(boundingArea_)); + else if (boundingArea_.type() == QGeoShape::PolygonType) + return QVariant::fromValue(QGeoPolygon(boundingArea_)); + else + return QVariant::fromValue(boundingArea_); +} + +void QDeclarativeGeocodeModel::geocodeFinished(QGeoCodeReply *reply) +{ + if (reply != reply_ || reply->error() != QGeoCodeReply::NoError) + return; + + reply->deleteLater(); + reply_ = 0; + int oldCount = declarativeLocations_.count(); + setLocations(reply->locations()); + setError(NoError, QString()); + setStatus(QDeclarativeGeocodeModel::Ready); + emit locationsChanged(); + if (oldCount != declarativeLocations_.count()) + emit countChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::geocodeError(QGeoCodeReply *reply, + QGeoCodeReply::Error error, + const QString &errorString) +{ + if (reply != reply_) + return; + + reply->deleteLater(); + reply_ = 0; + int oldCount = declarativeLocations_.count(); + if (oldCount > 0) { + // Reset the model + setLocations(reply->locations()); + emit locationsChanged(); + emit countChanged(); + } + setError(static_cast(error), errorString); + setStatus(QDeclarativeGeocodeModel::Error); +} + +/*! + \qmlproperty enumeration QtLocation::GeocodeModel::status + + This read-only property holds the current status of the model. + + \list + \li GeocodeModel.Null - No geocode requests have been issued or \l reset has been called. + \li GeocodeModel.Ready - Geocode request(s) have finished successfully. + \li GeocodeModel.Loading - Geocode request has been issued but not yet finished + \li GeocodeModel.Error - Geocoding error has occurred, details are in \l error and \l errorString + \endlist +*/ + +QDeclarativeGeocodeModel::Status QDeclarativeGeocodeModel::status() const +{ + return status_; +} + +void QDeclarativeGeocodeModel::setStatus(QDeclarativeGeocodeModel::Status status) +{ + if (status_ == status) + return; + status_ = status; + emit statusChanged(); +} + +/*! + \qmlproperty enumeration QtLocation::GeocodeModel::error + + This read-only property holds the latest error value of the geocoding request. + + \list + \li GeocodeModel.NoError - No error has occurred. + \li GeocodeModel.CombinationError - An error occurred while results where being combined from multiple sources. + \li GeocodeModel.CommunicationError - An error occurred while communicating with the service provider. + \li GeocodeModel.EngineNotSetError - The model's plugin property was not set or there is no geocoding manager associated with the plugin. + \li GeocodeModel.MissingRequiredParameterError - A required parameter was not specified. + \li GeocodeModel.ParseError - The response from the service provider was in an unrecognizable format. + \li GeocodeModel.UnknownError - An error occurred which does not fit into any of the other categories. + \li GeocodeModel.UnknownParameterError - The plugin did not recognize one of the parameters it was given. + \li GeocodeModel.UnsupportedOptionError - The requested operation is not supported by the geocoding provider. + This may happen when the loaded engine does not support a particular geocoding request + such as reverse geocoding. + \endlist +*/ + +QDeclarativeGeocodeModel::GeocodeError QDeclarativeGeocodeModel::error() const +{ + return error_; +} + +void QDeclarativeGeocodeModel::setError(GeocodeError error, const QString &errorString) +{ + if (error_ == error && errorString_ == errorString) + return; + error_ = error; + errorString_ = errorString; + emit errorChanged(); +} + +/*! + \qmlproperty string QtLocation::GeocodeModel::errorString + + This read-only property holds the textual presentation of the latest geocoding error. + If no error has occurred or the model has been reset, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. +*/ + +QString QDeclarativeGeocodeModel::errorString() const +{ + return errorString_; +} + +/*! + \internal +*/ +void QDeclarativeGeocodeModel::setLocations(const QList &locations) +{ + beginResetModel(); + qDeleteAll(declarativeLocations_); + declarativeLocations_.clear(); + for (int i = 0; i < locations.count(); ++i) { + QDeclarativeGeoLocation *location = new QDeclarativeGeoLocation(locations.at(i), this); + declarativeLocations_.append(location); + } + endResetModel(); +} + +/*! + \qmlproperty int QtLocation::GeocodeModel::count + + This property holds how many locations the model currently has. + Amongst other uses, you can use this value when accessing locations + via the GeocodeModel::get -method. +*/ + +int QDeclarativeGeocodeModel::count() const +{ + return declarativeLocations_.count(); +} + +/*! + \qmlmethod Location QtLocation::GeocodeModel::get(int) + + Returns the \l [QML] {Location} at given index. Use \l count property to check the + amount of locations available. The locations are indexed from zero, so the accessible range + is 0...(count - 1). + + If you access out of bounds, a zero (null object) is returned and a warning is issued. +*/ + +QDeclarativeGeoLocation *QDeclarativeGeocodeModel::get(int index) +{ + if (index < 0 || index >= declarativeLocations_.count()) { + qmlWarning(this) << QStringLiteral("Index '%1' out of range").arg(index); + return 0; + } + return declarativeLocations_.at(index); +} + +/*! + \qmlproperty int QtLocation::GeocodeModel::limit + + This property holds the maximum number of results. The limit and \l offset values are only + applicable with free string geocoding (that is they are not considered when using addresses + or coordinates in the search query). + + If limit is -1 the entire result set will be returned, otherwise at most limit results will be + returned. The limit and \l offset results can be used together to implement paging. +*/ + +int QDeclarativeGeocodeModel::limit() const +{ + return limit_; +} + +void QDeclarativeGeocodeModel::setLimit(int limit) +{ + if (limit == limit_) + return; + limit_ = limit; + if (autoUpdate_) { + update(); + } + emit limitChanged(); +} + +/*! + \qmlproperty int QtLocation::GeocodeModel::offset + + This property tells not to return the first 'offset' number of the results. The \l limit and + offset values are only applicable with free string geocoding (that is they are not considered + when using addresses or coordinates in the search query). + + The \l limit and offset results can be used together to implement paging. +*/ + +int QDeclarativeGeocodeModel::offset() const +{ + return offset_; +} + +void QDeclarativeGeocodeModel::setOffset(int offset) +{ + if (offset == offset_) + return; + offset_ = offset; + if (autoUpdate_) { + update(); + } + emit offsetChanged(); +} + +/*! + \qmlmethod void QtLocation::GeocodeModel::reset() + + Resets the model. All location data is cleared, any outstanding requests + are aborted and possible errors are cleared. Model status will be set + to GeocodeModel.Null +*/ + +void QDeclarativeGeocodeModel::reset() +{ + beginResetModel(); + if (!declarativeLocations_.isEmpty()) { + setLocations(QList()); + emit countChanged(); + } + endResetModel(); + + abortRequest(); + setError(NoError, QString()); + setStatus(QDeclarativeGeocodeModel::Null); +} + +/*! + \qmlmethod void QtLocation::GeocodeModel::cancel() + + Cancels any outstanding requests and clears errors. Model status will be set to either + GeocodeModel.Null or GeocodeModel.Ready. +*/ +void QDeclarativeGeocodeModel::cancel() +{ + abortRequest(); + setError(NoError, QString()); + setStatus(declarativeLocations_.isEmpty() ? Null : Ready); +} + +/*! + \qmlproperty QVariant QtLocation::GeocodeModel::query + + This property holds the data of the geocoding request. + The property accepts three types of queries which determine both the data and + the type of action to be performed: + + \list + \li Address - Geocoding (address to coordinate) + \li \l {coordinate} - Reverse geocoding (coordinate to address) + \li string - Geocoding (address to coordinate) + \endlist +*/ + +QVariant QDeclarativeGeocodeModel::query() const +{ + return queryVariant_; +} + +void QDeclarativeGeocodeModel::setQuery(const QVariant &query) +{ + if (query == queryVariant_) + return; + + if (query.userType() == qMetaTypeId()) { + if (address_) { + address_->disconnect(this); + address_ = 0; + } + searchString_.clear(); + + coordinate_ = query.value(); + } else if (query.type() == QVariant::String) { + searchString_ = query.toString(); + if (address_) { + address_->disconnect(this); + address_ = 0; + } + coordinate_ = QGeoCoordinate(); + } else if (QObject *object = query.value()) { + if (QDeclarativeGeoAddress *address = qobject_cast(object)) { + if (address_) + address_->disconnect(this); + coordinate_ = QGeoCoordinate(); + searchString_.clear(); + + address_ = address; + connect(address_, SIGNAL(countryChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(countryCodeChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(stateChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(countyChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(cityChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(districtChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(streetChanged()), this, SLOT(queryContentChanged())); + connect(address_, SIGNAL(postalCodeChanged()), this, SLOT(queryContentChanged())); + } else { + qmlWarning(this) << QStringLiteral("Unsupported query type for geocode model ") + << QStringLiteral("(coordinate, string and Address supported)."); + return; + } + } else { + qmlWarning(this) << QStringLiteral("Unsupported query type for geocode model ") + << QStringLiteral("(coordinate, string and Address supported)."); + return; + } + + queryVariant_ = query; + emit queryChanged(); + if (autoUpdate_) + update(); +} + +/*! + \qmlproperty bool QtLocation::GeocodeModel::autoUpdate + + This property controls whether the Model automatically updates in response + to changes in its attached query. The default value of this property + is false. + + If setting this value to 'true' and using an Address or + \l {coordinate} as the query, note that any change at all in the + object's properties will trigger a new request to be sent. If you are adjusting many + properties of the object whilst autoUpdate is enabled, this can generate large numbers of + useless (and later discarded) requests. +*/ + +bool QDeclarativeGeocodeModel::autoUpdate() const +{ + return autoUpdate_; +} + +void QDeclarativeGeocodeModel::setAutoUpdate(bool update) +{ + if (autoUpdate_ == update) + return; + autoUpdate_ = update; + emit autoUpdateChanged(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeocodemodel_p.h b/src/location/declarativemaps/qdeclarativegeocodemodel_p.h new file mode 100644 index 0000000..6c8f533 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeocodemodel_p.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOCODEMODEL_H +#define QDECLARATIVEGEOCODEMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +class QGeoServiceProvider; +class QGeoCodingManager; +class QDeclarativeGeoLocation; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeocodeModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(Status) + Q_ENUMS(GeocodeError) + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate NOTIFY autoUpdateChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged) + Q_PROPERTY(int offset READ offset WRITE setOffset NOTIFY offsetChanged) + Q_PROPERTY(QVariant query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(QVariant bounds READ bounds WRITE setBounds NOTIFY boundsChanged) + Q_PROPERTY(GeocodeError error READ error NOTIFY errorChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + enum Status { + Null, + Ready, + Loading, + Error + }; + + enum GeocodeError { + NoError = QGeoCodeReply::NoError, + EngineNotSetError = QGeoCodeReply::EngineNotSetError, //TODO Qt6 consider merge with NotSupportedError + CommunicationError = QGeoCodeReply::CommunicationError, //TODO Qt6 merge with Map's ConnectionError + ParseError = QGeoCodeReply::ParseError, + UnsupportedOptionError = QGeoCodeReply::UnsupportedOptionError, //TODO Qt6 consider rename UnsupportedOperationError + CombinationError = QGeoCodeReply::CombinationError, + UnknownError = QGeoCodeReply::UnknownError, + //we leave gap for future QGeoCodeReply errors + + //QGeoServiceProvider related errors start here + UnknownParameterError = 100, + MissingRequiredParameterError + }; + + enum Roles { + LocationRole = Qt::UserRole + 1 + }; + + explicit QDeclarativeGeocodeModel(QObject *parent = 0); + virtual ~QDeclarativeGeocodeModel(); + + // From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + // From QAbstractListModel + virtual int rowCount(const QModelIndex &parent) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QHash roleNames() const; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setBounds(const QVariant &boundingArea); + QVariant bounds() const; + + Status status() const; + QString errorString() const; + GeocodeError error() const; + + bool autoUpdate() const; + void setAutoUpdate(bool update); + + int count() const; + Q_INVOKABLE QDeclarativeGeoLocation *get(int index); + + int limit() const; + void setLimit(int limit); + int offset() const; + void setOffset(int offset); + + QVariant query() const; + void setQuery(const QVariant &query); + Q_INVOKABLE void reset(); + Q_INVOKABLE void cancel(); + +Q_SIGNALS: + void countChanged(); + void pluginChanged(); + void statusChanged(); + void errorChanged(); //emitted also for errorString notification + void locationsChanged(); + void autoUpdateChanged(); + void boundsChanged(); + void queryChanged(); + void limitChanged(); + void offsetChanged(); + +public Q_SLOTS: + void update(); + +protected Q_SLOTS: + void queryContentChanged(); + void geocodeFinished(QGeoCodeReply *reply); + void geocodeError(QGeoCodeReply *reply, + QGeoCodeReply::Error error, + const QString &errorString); + void pluginReady(); + +protected: + QGeoCodingManager *searchManager(); + void setStatus(Status status); + void setError(GeocodeError error, const QString &errorString); + bool autoUpdate_; + bool complete_; + +private: + void setLocations(const QList &locations); + void abortRequest(); + QGeoCodeReply *reply_; + + QDeclarativeGeoServiceProvider *plugin_; + QGeoShape boundingArea_; + + QList declarativeLocations_; + + Status status_; + QString errorString_; + GeocodeError error_; + QVariant queryVariant_; + QGeoCoordinate coordinate_; + QDeclarativeGeoAddress *address_; + QString searchString_; + + int limit_; + int offset_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomaneuver.cpp b/src/location/declarativemaps/qdeclarativegeomaneuver.cpp new file mode 100644 index 0000000..2c5a0f9 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaneuver.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomaneuver_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RouteManeuver + \instantiates QDeclarativeGeoManeuver + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since QtLocation 5.5 + + \brief The RouteManeuver type represents the information relevant to the + point at which two RouteSegments meet. + + RouteSegment instances can be thought of as edges on a routing + graph, with RouteManeuver instances as optional labels attached to the + vertices of the graph. + + The most interesting information held in a RouteManeuver instance is + normally the textual navigation to provide and the position at which to + provide it, accessible by \l instructionText and \l position respectively. + + \section1 Example + + The following QML snippet demonstrates how to print information about a + route maneuver: + + \snippet declarative/routing.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/routing.qml RouteManeuver +*/ + +QDeclarativeGeoManeuver::QDeclarativeGeoManeuver(QObject *parent) + : QObject(parent) +{ +} + +QDeclarativeGeoManeuver::QDeclarativeGeoManeuver(const QGeoManeuver &maneuver, QObject *parent) + : QObject(parent), + maneuver_(maneuver) +{ +} + +QDeclarativeGeoManeuver::~QDeclarativeGeoManeuver() {} + +/*! + \qmlproperty bool RouteManeuver::valid + + This read-only property holds whether this maneuver is valid or not. + + Invalid maneuvers are used when there is no information + that needs to be attached to the endpoint of a QGeoRouteSegment instance. +*/ + +bool QDeclarativeGeoManeuver::valid() const +{ + return maneuver_.isValid(); +} + +/*! + \qmlproperty coordinate RouteManeuver::position + + This read-only property holds where the \l instructionText should be displayed. + +*/ + +QGeoCoordinate QDeclarativeGeoManeuver::position() const +{ + return maneuver_.position(); +} + +/*! + \qmlproperty string RouteManeuver::instructionText + + This read-only property holds textual navigation instruction. +*/ + +QString QDeclarativeGeoManeuver::instructionText() const +{ + return maneuver_.instructionText(); +} + +/*! + \qmlproperty enumeration RouteManeuver::direction + + Describes the change in direction associated with the instruction text + that is associated with a RouteManeuver. + + \list + \li RouteManeuver.NoDirection - There is no direction associated with the instruction text + \li RouteManeuver.DirectionForward - The instruction indicates that the direction of travel does not need to change + \li RouteManeuver.DirectionBearRight - The instruction indicates that the direction of travel should bear to the right + \li RouteManeuver.DirectionLightRight - The instruction indicates that a light turn to the right is required + \li RouteManeuver.DirectionRight - The instruction indicates that a turn to the right is required + \li RouteManeuver.DirectionHardRight - The instruction indicates that a hard turn to the right is required + \li RouteManeuver.DirectionUTurnRight - The instruction indicates that a u-turn to the right is required + \li RouteManeuver.DirectionUTurnLeft - The instruction indicates that a u-turn to the left is required + \li RouteManeuver.DirectionHardLeft - The instruction indicates that a hard turn to the left is required + \li RouteManeuver.DirectionLeft - The instruction indicates that a turn to the left is required + \li RouteManeuver.DirectionLightLeft - The instruction indicates that a light turn to the left is required + \li RouteManeuver.DirectionBearLeft - The instruction indicates that the direction of travel should bear to the left + \endlist +*/ + +QDeclarativeGeoManeuver::Direction QDeclarativeGeoManeuver::direction() const +{ + return QDeclarativeGeoManeuver::Direction(maneuver_.direction()); +} + +/*! + \qmlproperty int RouteManeuver::timeToNextInstruction + + This read-only property holds the estimated time it will take to travel + from the point at which the associated instruction was issued and the + point that the next instruction should be issued, in seconds. +*/ + +int QDeclarativeGeoManeuver::timeToNextInstruction() const +{ + return maneuver_.timeToNextInstruction(); +} + +/*! + \qmlproperty real RouteManeuver::distanceToNextInstruction + + This read-only property holds the distance, in meters, between the point at which + the associated instruction was issued and the point that the next instruction should + be issued. +*/ + +qreal QDeclarativeGeoManeuver::distanceToNextInstruction() const +{ + return maneuver_.distanceToNextInstruction(); +} + +/*! + \qmlproperty coordinate RouteManeuver::waypoint + + This property holds the waypoint associated with this maneuver. + All maneuvers do not have a waypoint associated with them, this + can be checked with \l waypointValid. + +*/ + +QGeoCoordinate QDeclarativeGeoManeuver::waypoint() const +{ + return maneuver_.waypoint(); +} + +/*! + \qmlproperty Object RouteManeuver::extendedAttributes + + This property holds the extended attributes of the maneuver and is a map. + These attributes are plugin specific, and can be empty. + + Consult the \l {Qt Location#Plugin References and Parameters}{plugin documentation} + for what attributes are supported and how they should be used. + + Note, due to limitations of the QQmlPropertyMap, it is not possible + to declaratively specify the attributes in QML, assignment of attributes keys + and values can only be accomplished by JavaScript. + + \since QtLocation 5.11 +*/ +QQmlPropertyMap *QDeclarativeGeoManeuver::extendedAttributes() const +{ + if (!m_extendedAttributes) { + QDeclarativeGeoManeuver *self = const_cast(this); + self->m_extendedAttributes = new QQmlPropertyMap(self); + // Fill it + const QStringList keys = maneuver_.extendedAttributes().keys(); + for (const QString &key: keys) { + self->m_extendedAttributes->insert(key, + maneuver_.extendedAttributes().value(key)); + } + } + return m_extendedAttributes; +} + +/*! + \qmlproperty bool RouteManeuver::waypointValid + + This read-only property holds whether this \l waypoint, associated with this + maneuver, is valid or not. +*/ + +bool QDeclarativeGeoManeuver::waypointValid() const +{ + return maneuver_.waypoint().isValid(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomaneuver_p.h b/src/location/declarativemaps/qdeclarativegeomaneuver_p.h new file mode 100644 index 0000000..4c79aa8 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaneuver_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMANEUVER_H +#define QDECLARATIVEGEOMANEUVER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include + + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoManeuver : public QObject +{ + Q_OBJECT + Q_ENUMS(Direction) + + Q_PROPERTY(bool valid READ valid CONSTANT) + Q_PROPERTY(QGeoCoordinate position READ position CONSTANT) + Q_PROPERTY(QString instructionText READ instructionText CONSTANT) + Q_PROPERTY(Direction direction READ direction CONSTANT) + Q_PROPERTY(int timeToNextInstruction READ timeToNextInstruction CONSTANT) + Q_PROPERTY(qreal distanceToNextInstruction READ distanceToNextInstruction CONSTANT) + Q_PROPERTY(QGeoCoordinate waypoint READ waypoint CONSTANT) + Q_PROPERTY(bool waypointValid READ waypointValid CONSTANT) + Q_PROPERTY(QObject *extendedAttributes READ extendedAttributes NOTIFY extendedAttributesChanged REVISION 11) + +public: + enum Direction { + NoDirection = QGeoManeuver::NoDirection, + DirectionForward = QGeoManeuver::DirectionForward, + DirectionBearRight = QGeoManeuver::DirectionBearRight, + DirectionLightRight = QGeoManeuver::DirectionLightRight, + DirectionRight = QGeoManeuver::DirectionRight, + DirectionHardRight = QGeoManeuver::DirectionHardRight, + DirectionUTurnRight = QGeoManeuver::DirectionUTurnRight, + DirectionUTurnLeft = QGeoManeuver::DirectionUTurnLeft, + DirectionHardLeft = QGeoManeuver::DirectionHardLeft, + DirectionLeft = QGeoManeuver::DirectionLeft, + DirectionLightLeft = QGeoManeuver::DirectionLightLeft, + DirectionBearLeft = QGeoManeuver::DirectionBearLeft + }; + + explicit QDeclarativeGeoManeuver(QObject *parent = 0); + QDeclarativeGeoManeuver(const QGeoManeuver &maneuver, QObject *parent = 0); + ~QDeclarativeGeoManeuver(); + + bool valid() const; + bool waypointValid() const; + + QGeoCoordinate position() const; + QString instructionText() const; + Direction direction() const; + int timeToNextInstruction() const; + qreal distanceToNextInstruction() const; + QGeoCoordinate waypoint() const; + QQmlPropertyMap *extendedAttributes() const; + +Q_SIGNALS: + void extendedAttributesChanged(); //in practice is never emitted since parameters cannot be re-assigned + //the declaration is needed to avoid warnings about non-notifyable properties + +private: + QGeoManeuver maneuver_; + QQmlPropertyMap *m_extendedAttributes = nullptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomap.cpp b/src/location/declarativemaps/qdeclarativegeomap.cpp new file mode 100644 index 0000000..09f9d01 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomap.cpp @@ -0,0 +1,2451 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomap_p.h" +#include "qdeclarativegeomapquickitem_p.h" +#include "qdeclarativegeomapcopyrightsnotice_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "qdeclarativegeomaptype_p.h" +#include "qgeomappingmanager_p.h" +#include "qgeocameracapabilities_p.h" +#include "qgeomap_p.h" +#include "qdeclarativegeomapparameter_p.h" +#include "qgeomapobject_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef M_PI +#define M_PI 3.141592653589793238463 +#endif + + +QT_BEGIN_NAMESPACE + +static qreal sanitizeBearing(qreal bearing) +{ + bearing = std::fmod(bearing, qreal(360.0)); + if (bearing < 0.0) + bearing += 360.0; + + return bearing; +} + +/*! + \qmltype Map + \instantiates QDeclarativeGeoMap + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.0 + + \brief The Map type displays a map. + + The Map type is used to display a map or image of the Earth, with + the capability to also display interactive objects tied to the map's + surface. + + There are a variety of different ways to visualize the Earth's surface + in a 2-dimensional manner, but all of them involve some kind of projection: + a mathematical relationship between the 3D coordinates (latitude, longitude + and altitude) and 2D coordinates (X and Y in pixels) on the screen. + + Different sources of map data can use different projections, and from the + point of view of the Map type, we treat these as one replaceable unit: + the Map plugin. A Map plugin consists of a data source, as well as all other + details needed to display its data on-screen. + + The current Map plugin in use is contained in the \l plugin property of + the Map item. In order to display any image in a Map item, you will need + to set this property. See the \l Plugin type for a description of how + to retrieve an appropriate plugin for use. + + The geographic region displayed in the Map item is referred to as its + viewport, and this is defined by the properties \l center, and + \l zoomLevel. The \l center property contains a \l {coordinate} + specifying the center of the viewport, while \l zoomLevel controls the scale of the + map. See each of these properties for further details about their values. + + When the map is displayed, each possible geographic coordinate that is + visible will map to some pixel X and Y coordinate on the screen. To perform + conversions between these two, Map provides the \l toCoordinate and + \l fromCoordinate functions, which are of general utility. + + \section2 Map Objects + + Map related objects can be declared within the body of a Map object in Qt Quick and will + automatically appear on the Map. To add an object programmatically, first be + sure it is created with the Map as its parent (for example in an argument to + Component::createObject). + Then call the \l addMapItem method on the Map, if the type of this object is one of + \l MapCircle, \l MapRectangle, \l MapPolyline, \l MapPolygon, \l MapRoute or \l MapQuickItem. + A corresponding \l removeMapItem method also exists to do the opposite and + remove any of the above types of map objects from the Map. + + Moving Map objects around, resizing them or changing their shape normally + does not involve any special interaction with Map itself -- changing these + properties in a map object will automatically update the display. + + \section2 Interaction + + The Map type includes support for pinch and flick gestures to control + zooming and panning. These are enabled by default, and available at any + time by using the \l gesture object. The actual GestureArea is constructed + specially at startup and cannot be replaced or destroyed. Its properties + can be altered, however, to control its behavior. + + \section2 Performance + + Maps are rendered using OpenGL (ES) and the Qt Scene Graph stack, and as + a result perform quite well where GL accelerated hardware is available. + + For "online" Map plugins, network bandwidth and latency can be major + contributors to the user's perception of performance. Extensive caching is + performed to mitigate this, but such mitigation is not always perfect. For + "offline" plugins, the time spent retrieving the stored geographic data + and rendering the basic map features can often play a dominant role. Some + offline plugins may use hardware acceleration themselves to (partially) + avert this. + + In general, large and complex Map items such as polygons and polylines with + large numbers of vertices can have an adverse effect on UI performance. + Further, more detailed notes on this are in the documentation for each + map item type. + + \section2 Example Usage + + The following snippet shows a simple Map and the necessary Plugin type + to use it. The map is centered over Oslo, Norway, with zoom level 14. + + \quotefromfile minimal_map/main.qml + \skipto import + \printuntil } + \printline } + \skipto Map + \printuntil } + \printline } + + \image minimal_map.png +*/ + +/*! + \qmlsignal QtLocation::Map::copyrightLinkActivated(string link) + + This signal is emitted when the user clicks on a \a link in the copyright notice. The + application should open the link in a browser or display its contents to the user. +*/ + +QDeclarativeGeoMap::QDeclarativeGeoMap(QQuickItem *parent) + : QQuickItem(parent), + m_plugin(0), + m_mappingManager(0), + m_activeMapType(0), + m_gestureArea(new QQuickGeoMapGestureArea(this)), + m_map(0), + m_error(QGeoServiceProvider::NoError), + m_color(QColor::fromRgbF(0.9, 0.9, 0.9)), + m_componentCompleted(false), + m_pendingFitViewport(false), + m_copyrightsVisible(true), + m_maximumViewportLatitude(0.0), + m_initialized(false), + m_userMinimumZoomLevel(qQNaN()), + m_userMaximumZoomLevel(qQNaN()), + m_userMinimumTilt(qQNaN()), + m_userMaximumTilt(qQNaN()), + m_userMinimumFieldOfView(qQNaN()), + m_userMaximumFieldOfView(qQNaN()) +{ + setAcceptHoverEvents(false); + setAcceptedMouseButtons(Qt::LeftButton); + setFlags(QQuickItem::ItemHasContents | QQuickItem::ItemClipsChildrenToShape); + setFiltersChildMouseEvents(true); + + m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap, + tr("No Map"), + tr("No Map"), + false, false, + 0, + QByteArrayLiteral(""), + QGeoCameraCapabilities()), this); + m_cameraData.setCenter(QGeoCoordinate(51.5073,-0.1277)); //London city center + m_cameraData.setZoomLevel(8.0); + + m_cameraCapabilities.setTileSize(256); + m_cameraCapabilities.setSupportsBearing(true); + m_cameraCapabilities.setSupportsTilting(true); + m_cameraCapabilities.setMinimumZoomLevel(0); + m_cameraCapabilities.setMaximumZoomLevel(30); + m_cameraCapabilities.setMinimumTilt(0); + m_cameraCapabilities.setMaximumTilt(89.5); + m_cameraCapabilities.setMinimumFieldOfView(1); + m_cameraCapabilities.setMaximumFieldOfView(179); + + m_minimumTilt = m_cameraCapabilities.minimumTilt(); + m_maximumTilt = m_cameraCapabilities.maximumTilt(); + m_minimumFieldOfView = m_cameraCapabilities.minimumFieldOfView(); + m_maximumFieldOfView = m_cameraCapabilities.maximumFieldOfView(); +} + +QDeclarativeGeoMap::~QDeclarativeGeoMap() +{ + // Removing map parameters and map items from m_map + if (m_map) { + m_map->clearParameters(); + m_map->clearMapItems(); + } + + // This forces the destruction of the associated items now, not when QObject destructor is called, at which point + // QDeclarativeGeoMap is long gone + if (!m_mapViews.isEmpty()) { + for (QDeclarativeGeoMapItemView *v : qAsConst(m_mapViews)) { + if (!v) + continue; + if (v->parent() == this) + delete v; + else + v->removeInstantiatedItems(); + } + } + // remove any map items associations + for (int i = 0; i < m_mapItems.count(); ++i) { + if (m_mapItems.at(i)) + m_mapItems.at(i).data()->setMap(0,0); + } + m_mapItems.clear(); + + for (auto g: qAsConst(m_mapItemGroups)) { + if (!g) + continue; + const QList quickKids = g->childItems(); + for (auto c: quickKids) { + QDeclarativeGeoMapItemBase *itemBase = qobject_cast(c); + if (itemBase) + itemBase->setMap(0,0); + } + } + m_mapItemGroups.clear(); + + if (m_copyrights.data()) + delete m_copyrights.data(); + m_copyrights.clear(); + + for (auto obj: qAsConst(m_pendingMapObjects)) + obj->setMap(nullptr); // worst case: going to be setMap(nullptr)'d twice + + delete m_map; // map objects get reset here +} + +static QDeclarativeGeoMapType *findMapType(const QList &types, const QGeoMapType &type) +{ + for (int i = 0; i < types.size(); ++i) + if (types[i]->mapType() == type) + return types[i]; + return nullptr; +} + +void QDeclarativeGeoMap::onSupportedMapTypesChanged() +{ + QList supportedMapTypes; + QList types = m_mappingManager->supportedMapTypes(); + for (int i = 0; i < types.size(); ++i) { + // types that are present and get removed will be deleted at QObject destruction + QDeclarativeGeoMapType *type = findMapType(m_supportedMapTypes, types[i]); + if (!type) + type = new QDeclarativeGeoMapType(types[i], this); + supportedMapTypes.append(type); + } + m_supportedMapTypes.swap(supportedMapTypes); + if (m_supportedMapTypes.isEmpty()) { + m_map->setActiveMapType(QGeoMapType()); // no supported map types: setting an invalid one + } else { + bool hasMapType = false; + foreach (QDeclarativeGeoMapType *declarativeType, m_supportedMapTypes) { + if (declarativeType->mapType() == m_map->activeMapType()) + hasMapType = true; + } + if (!hasMapType) { + QDeclarativeGeoMapType *type = m_supportedMapTypes.at(0); + m_activeMapType = type; + m_map->setActiveMapType(type->mapType()); + } + } + + emit supportedMapTypesChanged(); +} + +void QDeclarativeGeoMap::setError(QGeoServiceProvider::Error error, const QString &errorString) +{ + if (m_error == error && m_errorString == errorString) + return; + m_error = error; + m_errorString = errorString; + emit errorChanged(); +} + +/*! + \internal + Called when the mapping manager is initialized AND the declarative element has a valid size > 0 +*/ +void QDeclarativeGeoMap::initialize() +{ + // try to keep change signals in the end + bool centerHasChanged = false; + bool bearingHasChanged = false; + bool tiltHasChanged = false; + bool fovHasChanged = false; + + QGeoCoordinate center = m_cameraData.center(); + + if (!qIsFinite(m_userMinimumZoomLevel)) + setMinimumZoomLevel(m_map->minimumZoom(), false); + else + setMinimumZoomLevel(qMax(m_map->minimumZoom(), m_userMinimumZoomLevel), false); + + double bearing = m_cameraData.bearing(); + double tilt = m_cameraData.tilt(); + double fov = m_cameraData.fieldOfView(); // Must be 45.0 + + if (!m_cameraCapabilities.supportsBearing() && bearing != 0.0) { + m_cameraData.setBearing(0); + bearingHasChanged = true; + } + if (!m_cameraCapabilities.supportsTilting() && tilt != 0.0) { + m_cameraData.setTilt(0); + tiltHasChanged = true; + } + + m_cameraData.setFieldOfView(qBound(m_cameraCapabilities.minimumFieldOfView(), + fov, + m_cameraCapabilities.maximumFieldOfView())); + if (fov != m_cameraData.fieldOfView()) + fovHasChanged = true; + + // set latitude boundary check + m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(m_cameraData); + + center.setLatitude(qBound(-m_maximumViewportLatitude, center.latitude(), m_maximumViewportLatitude)); + + if (center != m_cameraData.center()) { + centerHasChanged = true; + m_cameraData.setCenter(center); + } + + m_map->setCameraData(m_cameraData); + + for (auto obj : qAsConst(m_pendingMapObjects)) + obj->setMap(m_map); + + m_initialized = true; + + if (centerHasChanged) + emit centerChanged(m_cameraData.center()); + + if (bearingHasChanged) + emit bearingChanged(m_cameraData.bearing()); + + if (tiltHasChanged) + emit tiltChanged(m_cameraData.tilt()); + + if (fovHasChanged) + emit fieldOfViewChanged(m_cameraData.fieldOfView()); + + emit mapReadyChanged(true); + + if (m_copyrights) // To not update during initialize() + update(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::pluginReady() +{ + QGeoServiceProvider *provider = m_plugin->sharedGeoServiceProvider(); + m_mappingManager = provider->mappingManager(); + + if (provider->error() != QGeoServiceProvider::NoError) { + setError(provider->error(), provider->errorString()); + return; + } + + if (!m_mappingManager) { + //TODO Should really be EngineNotSetError (see QML GeoCodeModel) + setError(QGeoServiceProvider::NotSupportedError, tr("Plugin does not support mapping.")); + return; + } + + if (!m_mappingManager->isInitialized()) + connect(m_mappingManager, SIGNAL(initialized()), this, SLOT(mappingManagerInitialized())); + else + mappingManagerInitialized(); + + // make sure this is only called once + disconnect(this, SLOT(pluginReady())); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::componentComplete() +{ + m_componentCompleted = true; + populateParameters(); + populateMap(); + QQuickItem::componentComplete(); +} + +/*! + \qmlproperty MapGestureArea QtLocation::Map::gesture + + Contains the MapGestureArea created with the Map. This covers pan, flick and pinch gestures. + Use \c{gesture.enabled: true} to enable basic gestures, or see \l{MapGestureArea} for + further details. +*/ + +QQuickGeoMapGestureArea *QDeclarativeGeoMap::gesture() +{ + return m_gestureArea; +} + +/*! + \internal + + This may happen before mappingManagerInitialized() +*/ +void QDeclarativeGeoMap::populateMap() +{ + QSet kids = children().toSet(); + const QList quickKids = childItems(); + for (QQuickItem *ite: quickKids) + kids.insert(ite); + + for (QObject *k : qAsConst(kids)) { + // dispatch items appropriately + QDeclarativeGeoMapItemView *mapView = qobject_cast(k); + if (mapView) { + m_mapViews.append(mapView); + setupMapView(mapView); + continue; + } + QDeclarativeGeoMapItemBase *mapItem = qobject_cast(k); + if (mapItem) { + addMapItem(mapItem); + continue; + } + // Allow to add to the map Map items contained inside a parent QQuickItem, but only those at one level of nesting. + QDeclarativeGeoMapItemGroup *itemGroup = qobject_cast(k); + if (itemGroup) { + addMapItemGroup(itemGroup); + continue; + } + + QGeoMapObject *mapObject = qobject_cast(k); + if (mapObject) + addMapObject(mapObject); + } +} + +void QDeclarativeGeoMap::populateParameters() +{ + QObjectList kids = children(); + QList quickKids = childItems(); + for (int i = 0; i < quickKids.count(); ++i) + kids.append(quickKids.at(i)); + for (int i = 0; i < kids.size(); ++i) { + QDeclarativeGeoMapParameter *mapParameter = qobject_cast(kids.at(i)); + if (mapParameter) + addMapParameter(mapParameter); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::setupMapView(QDeclarativeGeoMapItemView *view) +{ + view->setMap(this); +} + +/*! + * \internal + */ +QSGNode *QDeclarativeGeoMap::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + if (!m_map) { + delete oldNode; + return 0; + } + + QSGRectangleNode *root = static_cast(oldNode); + if (!root) + root = window()->createRectangleNode(); + + root->setRect(boundingRect()); + root->setColor(m_color); + + QSGNode *content = root->childCount() ? root->firstChild() : 0; + content = m_map->updateSceneGraph(content, window()); + if (content && root->childCount() == 0) + root->appendChildNode(content); + + return root; +} + +/*! + \qmlproperty Plugin QtLocation::Map::plugin + + This property holds the plugin which provides the mapping functionality. + + This is a write-once property. Once the map has a plugin associated with + it, any attempted modifications of the plugin will be ignored. +*/ + +void QDeclarativeGeoMap::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin) { + qmlWarning(this) << QStringLiteral("Plugin is a write-once property, and cannot be set again."); + return; + } + m_plugin = plugin; + emit pluginChanged(m_plugin); + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities) +{ + if (m_map->cameraCapabilities() == oldCameraCapabilities) + return; + m_cameraCapabilities = m_map->cameraCapabilities(); + + //The zoom level limits are only restricted by the plugins values, if the user has set a more + //strict zoom level limit before initialization nothing is done here. + //minimum zoom level might be changed to limit gray bundaries + //This code assumes that plugins' maximum zoom level will never exceed 30.0 + if (m_cameraCapabilities.maximumZoomLevelAt256() < m_gestureArea->maximumZoomLevel()) { + setMaximumZoomLevel(m_cameraCapabilities.maximumZoomLevelAt256(), false); + } else if (m_cameraCapabilities.maximumZoomLevelAt256() > m_gestureArea->maximumZoomLevel()) { + if (!qIsFinite(m_userMaximumZoomLevel)) { + // If the user didn't set anything + setMaximumZoomLevel(m_cameraCapabilities.maximumZoomLevelAt256(), false); + } else { // Try to set what the user requested + // Else if the user set something larger, but that got clamped by the previous camera caps + setMaximumZoomLevel(qMin(m_cameraCapabilities.maximumZoomLevelAt256(), + m_userMaximumZoomLevel), false); + } + } + + if (m_cameraCapabilities.minimumZoomLevelAt256() > m_gestureArea->minimumZoomLevel()) { + setMinimumZoomLevel(m_cameraCapabilities.minimumZoomLevelAt256(), false); + } else if (m_cameraCapabilities.minimumZoomLevelAt256() < m_gestureArea->minimumZoomLevel()) { + if (!qIsFinite(m_userMinimumZoomLevel)) { + // If the user didn't set anything, trying to set the new caps. + setMinimumZoomLevel(m_cameraCapabilities.minimumZoomLevelAt256(), false); + } else { // Try to set what the user requested + // Else if the user set a minimum, m_gestureArea->minimumZoomLevel() might be larger + // because of different reasons. Resetting it, as if it ends to be the same, + // no signal will be emitted. + setMinimumZoomLevel(qMax(m_cameraCapabilities.minimumZoomLevelAt256(), + m_userMinimumZoomLevel), false); + } + } + + // Tilt + if (m_cameraCapabilities.maximumTilt() < m_maximumTilt) { + setMaximumTilt(m_cameraCapabilities.maximumTilt(), false); + } else if (m_cameraCapabilities.maximumTilt() > m_maximumTilt) { + if (!qIsFinite(m_userMaximumTilt)) + setMaximumTilt(m_cameraCapabilities.maximumTilt(), false); + else // Try to set what the user requested + setMaximumTilt(qMin(m_cameraCapabilities.maximumTilt(), m_userMaximumTilt), false); + } + + if (m_cameraCapabilities.minimumTilt() > m_minimumTilt) { + setMinimumTilt(m_cameraCapabilities.minimumTilt(), false); + } else if (m_cameraCapabilities.minimumTilt() < m_minimumTilt) { + if (!qIsFinite(m_userMinimumTilt)) + setMinimumTilt(m_cameraCapabilities.minimumTilt(), false); + else // Try to set what the user requested + setMinimumTilt(qMax(m_cameraCapabilities.minimumTilt(), m_userMinimumTilt), false); + } + + // FoV + if (m_cameraCapabilities.maximumFieldOfView() < m_maximumFieldOfView) { + setMaximumFieldOfView(m_cameraCapabilities.maximumFieldOfView(), false); + } else if (m_cameraCapabilities.maximumFieldOfView() > m_maximumFieldOfView) { + if (!qIsFinite(m_userMaximumFieldOfView)) + setMaximumFieldOfView(m_cameraCapabilities.maximumFieldOfView(), false); + else // Try to set what the user requested + setMaximumFieldOfView(qMin(m_cameraCapabilities.maximumFieldOfView(), m_userMaximumFieldOfView), false); + } + + if (m_cameraCapabilities.minimumFieldOfView() > m_minimumFieldOfView) { + setMinimumFieldOfView(m_cameraCapabilities.minimumFieldOfView(), false); + } else if (m_cameraCapabilities.minimumFieldOfView() < m_minimumFieldOfView) { + if (!qIsFinite(m_userMinimumFieldOfView)) + setMinimumFieldOfView(m_cameraCapabilities.minimumFieldOfView(), false); + else // Try to set what the user requested + setMinimumFieldOfView(qMax(m_cameraCapabilities.minimumFieldOfView(), m_userMinimumFieldOfView), false); + } +} + +/*! + \internal + this function will only be ever called once +*/ +void QDeclarativeGeoMap::mappingManagerInitialized() +{ + m_map = m_mappingManager->createMap(this); + + if (!m_map) + return; + + /* COPY NOTICE SETUP */ + m_copyrights = new QDeclarativeGeoMapCopyrightNotice(this); + m_copyrights->setCopyrightsZ(m_maxChildZ + 1); + m_copyrights->setCopyrightsVisible(m_copyrightsVisible); + m_copyrights->setMapSource(this); + + m_gestureArea->setMap(m_map); + + QList types = m_mappingManager->supportedMapTypes(); + for (int i = 0; i < types.size(); ++i) { + QDeclarativeGeoMapType *type = new QDeclarativeGeoMapType(types[i], this); + m_supportedMapTypes.append(type); + } + + if (m_activeMapType && m_plugin->name().toLatin1() == m_activeMapType->mapType().pluginName()) { + m_map->setActiveMapType(m_activeMapType->mapType()); + } else { + if (m_activeMapType) + m_activeMapType->deleteLater(); + + if (!m_supportedMapTypes.isEmpty()) { + m_activeMapType = m_supportedMapTypes.at(0); + m_map->setActiveMapType(m_activeMapType->mapType()); + } else { + m_activeMapType = new QDeclarativeGeoMapType(QGeoMapType(QGeoMapType::NoMap, + tr("No Map"), + tr("No Map"), + false, + false, + 0, + QByteArrayLiteral(""), + QGeoCameraCapabilities()), this); + } + } + + // Update camera capabilities + onCameraCapabilitiesChanged(m_cameraCapabilities); + + // Map tiles are built in this call. m_map->minimumZoom() becomes operational + // after this has been called at least once, after creation. + // However, getting into the following block may fire a copyrightsChanged that would get lost, + // as the connections are set up after. + QString copyrightString; + QImage copyrightImage; + if (!m_initialized && width() > 0 && height() > 0) { + QMetaObject::Connection copyrightStringCatcherConnection = + connect(m_map, + QOverload::of(&QGeoMap::copyrightsChanged), + [©rightString](const QString ©){ copyrightString = copy; }); + QMetaObject::Connection copyrightImageCatcherConnection = + connect(m_map, + QOverload::of(&QGeoMap::copyrightsChanged), + [©rightImage](const QImage ©){ copyrightImage = copy; }); + m_map->setViewportSize(QSize(width(), height())); + initialize(); // This emits the caught signals above + QObject::disconnect(copyrightStringCatcherConnection); + QObject::disconnect(copyrightImageCatcherConnection); + } + + + /* COPYRIGHT SIGNALS REWIRING */ + connect(m_map, SIGNAL(copyrightsChanged(QImage)), + this, SIGNAL(copyrightsChanged(QImage))); + connect(m_map, SIGNAL(copyrightsChanged(QString)), + this, SIGNAL(copyrightsChanged(QString))); + if (!copyrightString.isEmpty()) + emit m_map->copyrightsChanged(copyrightString); + else if (!copyrightImage.isNull()) + emit m_map->copyrightsChanged(copyrightImage); + + + connect(m_map, &QGeoMap::sgNodeChanged, this, &QQuickItem::update); + connect(m_map, &QGeoMap::cameraCapabilitiesChanged, this, &QDeclarativeGeoMap::onCameraCapabilitiesChanged); + + // This prefetches a buffer around the map + m_map->prefetchData(); + + connect(m_mappingManager, SIGNAL(supportedMapTypesChanged()), this, SLOT(onSupportedMapTypesChanged())); + emit minimumZoomLevelChanged(); + emit maximumZoomLevelChanged(); + emit supportedMapTypesChanged(); + emit activeMapTypeChanged(); + + // Any map items that were added before the plugin was ready + // need to have setMap called again + for (const QPointer &item : qAsConst(m_mapItems)) { + if (item) { + item->setMap(this, m_map); + m_map->addMapItem(item.data()); // m_map filters out what is not supported. + } + } + + // Any map item groups that were added before the plugin was ready + // need to have setMap called again on their children map items + for (auto g: qAsConst(m_mapItemGroups)) { + const QList quickKids = g->childItems(); + for (auto c: quickKids) { + QDeclarativeGeoMapItemBase *itemBase = qobject_cast(c); + if (itemBase) + itemBase->setMap(this, m_map); + } + } + + // All map parameters that were added before the plugin was ready + // need to be added to m_map + for (QDeclarativeGeoMapParameter *p : qAsConst(m_mapParameters)) + m_map->addParameter(p); + + if (m_initialized) + update(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider *QDeclarativeGeoMap::plugin() const +{ + return m_plugin; +} + +/*! + \internal + Sets the gesture areas minimum zoom level. If the camera capabilities + has been set this method honors the boundaries set by it. + The minimum zoom level will also have a lower bound dependent on the size + of the canvas, effectively preventing to display out of bounds areas. +*/ +void QDeclarativeGeoMap::setMinimumZoomLevel(qreal minimumZoomLevel, bool userSet) +{ + if (minimumZoomLevel >= 0) { + qreal oldUserMinimumZoomLevel = m_userMinimumZoomLevel; + if (userSet) + m_userMinimumZoomLevel = minimumZoomLevel; + qreal oldMinimumZoomLevel = this->minimumZoomLevel(); + + minimumZoomLevel = qBound(qreal(m_cameraCapabilities.minimumZoomLevelAt256()), minimumZoomLevel, maximumZoomLevel()); + if (m_map) + minimumZoomLevel = qMax(minimumZoomLevel, m_map->minimumZoom()); + + // minimumZoomLevel is, at this point, the implicit minimum zoom level + m_gestureArea->setMinimumZoomLevel(minimumZoomLevel); + + if (zoomLevel() < minimumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled())) + setZoomLevel(minimumZoomLevel); + + if (qIsNaN(m_userMinimumZoomLevel) && oldMinimumZoomLevel != minimumZoomLevel) + emit minimumZoomLevelChanged(); + else if (userSet && oldUserMinimumZoomLevel != m_userMinimumZoomLevel) + emit minimumZoomLevelChanged(); + } +} + +/*! + \qmlproperty real QtLocation::Map::minimumZoomLevel + + This property holds the minimum valid zoom level for the map. + + The minimum zoom level defined by the \l plugin used is a lower bound for + this property. However, the returned value is also canvas-size-dependent, and + can be higher than the user-specified value, or than the minimum zoom level + defined by the plugin used, to prevent the map from being smaller than the + viewport in either dimension. + + If the \l plugin property is not set or the plugin does not support mapping, this property is \c 0. +*/ + +qreal QDeclarativeGeoMap::minimumZoomLevel() const +{ + if (!qIsNaN(m_userMinimumZoomLevel)) + return m_userMinimumZoomLevel; + else + return m_gestureArea->minimumZoomLevel(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMap::implicitMinimumZoomLevel() const +{ + return m_gestureArea->minimumZoomLevel(); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMap::effectiveMinimumZoomLevel() const +{ + return qMax(minimumZoomLevel(), implicitMinimumZoomLevel()); +} + +/*! + \internal + Sets the gesture areas maximum zoom level. If the camera capabilities + has been set this method honors the boundaries set by it. +*/ +void QDeclarativeGeoMap::setMaximumZoomLevel(qreal maximumZoomLevel, bool userSet) +{ + if (maximumZoomLevel >= 0) { + if (userSet) + m_userMaximumZoomLevel = maximumZoomLevel; + qreal oldMaximumZoomLevel = this->maximumZoomLevel(); + + maximumZoomLevel = qBound(minimumZoomLevel(), maximumZoomLevel, qreal(m_cameraCapabilities.maximumZoomLevelAt256())); + + m_gestureArea->setMaximumZoomLevel(maximumZoomLevel); + + if (zoomLevel() > maximumZoomLevel && (m_gestureArea->enabled() || !m_cameraCapabilities.overzoomEnabled())) + setZoomLevel(maximumZoomLevel); + + if (oldMaximumZoomLevel != maximumZoomLevel) + emit maximumZoomLevelChanged(); + } +} + +/*! + \qmlproperty real QtLocation::Map::maximumZoomLevel + + This property holds the maximum valid zoom level for the map. + + The maximum zoom level is defined by the \l plugin used. + If the \l plugin property is not set or the plugin does not support mapping, this property is \c 30. +*/ + +qreal QDeclarativeGeoMap::maximumZoomLevel() const +{ + return m_gestureArea->maximumZoomLevel(); +} + +/*! + \qmlproperty real QtLocation::Map::zoomLevel + + This property holds the zoom level for the map. + + Larger values for the zoom level provide more detail. Zoom levels + are always non-negative. The default value is 8.0. Depending on the plugin in use, + values outside the [minimumZoomLevel, maximumZoomLevel] range, which represent the range for which + tiles are available, may be accepted, or clamped. +*/ +void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel) +{ + return setZoomLevel(zoomLevel, m_cameraCapabilities.overzoomEnabled()); +} + +/*! + \internal + + Sets the zoom level. + Larger values for the zoom level provide more detail. Zoom levels + are always non-negative. The default value is 8.0. Values outside the + [minimumZoomLevel, maximumZoomLevel] range, which represent the range for which + tiles are available, can be accepted or clamped by setting the overzoom argument + to true or false respectively. +*/ +void QDeclarativeGeoMap::setZoomLevel(qreal zoomLevel, bool overzoom) +{ + const qreal oldZoom = m_cameraData.zoomLevel(); + if (oldZoom == zoomLevel || zoomLevel < 0) + return; + + //small optimization to avoid double setCameraData + bool centerHasChanged = false; + + if (m_initialized) { + m_cameraData.setZoomLevel(qBound(overzoom ? m_map->minimumZoom() : effectiveMinimumZoomLevel(), + zoomLevel, + overzoom ? 30 : maximumZoomLevel())); + m_maximumViewportLatitude = m_map->maximumCenterLatitudeAtZoom(m_cameraData); + QGeoCoordinate coord = m_cameraData.center(); + coord.setLatitude(qBound(-m_maximumViewportLatitude, coord.latitude(), m_maximumViewportLatitude)); + if (coord != m_cameraData.center()) { + centerHasChanged = true; + m_cameraData.setCenter(coord); + } + m_map->setCameraData(m_cameraData); + } else { + m_cameraData.setZoomLevel(zoomLevel); + } + + if (centerHasChanged) + emit centerChanged(m_cameraData.center()); + if (oldZoom != m_cameraData.zoomLevel()) + emit zoomLevelChanged(m_cameraData.zoomLevel()); +} + +qreal QDeclarativeGeoMap::zoomLevel() const +{ + return m_cameraData.zoomLevel(); +} + +/*! + \qmlproperty real QtLocation::Map::bearing + + This property holds the bearing for the map. + The default value is 0. + If the Plugin used for the Map supports bearing, the valid range for this value is between 0 and 360. + If the Plugin used for the Map does not support bearing, changing this property will have no effect. + + \since QtLocation 5.9 +*/ +void QDeclarativeGeoMap::setBearing(qreal bearing) +{ + bearing = sanitizeBearing(bearing); + if (m_cameraData.bearing() == bearing) + return; + + m_cameraData.setBearing(bearing); + if (m_map) + m_map->setCameraData(m_cameraData); + emit bearingChanged(bearing); +} + +/*! + \qmlmethod void QtLocation::Map::setBearing(real bearing, coordinate coordinate) + + Sets the bearing for the map to \a bearing, rotating it around \a coordinate. + If the Plugin used for the Map supports bearing, the valid range for \a bearing is between 0 and 360. + If the Plugin used for the Map does not support bearing, or if the map is tilted and \a coordinate happens + to be behind the camera, or if the map is not ready (see \l mapReady), calling this method will have no effect. + + The release of this API with Qt 5.10 is a Technology Preview. + + \since 5.10 +*/ +void QDeclarativeGeoMap::setBearing(qreal bearing, const QGeoCoordinate &coordinate) +{ + if (!m_map) + return; + + const QGeoCoordinate currentCenter = center(); + const qreal currentBearing = QDeclarativeGeoMap::bearing(); + bearing = sanitizeBearing(bearing); + + if (!coordinate.isValid() + || !qIsFinite(bearing) + || (coordinate == currentCenter && bearing == currentBearing)) + return; + + if (m_map->capabilities() & QGeoMap::SupportsSetBearing) { + if (!m_map->setBearing(bearing, coordinate)) + return; + + m_cameraData = m_map->cameraData(); + + if (m_cameraData.center() != currentCenter) + emit centerChanged(m_cameraData.center()); + if (m_cameraData.bearing() != currentBearing) + emit bearingChanged(bearing); + } +} + +qreal QDeclarativeGeoMap::bearing() const +{ + return m_cameraData.bearing(); +} + +/*! + \qmlproperty real QtLocation::Map::tilt + + This property holds the tilt for the map, in degrees. + The default value is 0. + The valid range for this value is [ minimumTilt, maximumTilt ]. + If the Plugin used for the Map does not support tilting, changing this property will have no effect. + + \sa minimumTilt, maximumTilt + + \since QtLocation 5.9 +*/ +void QDeclarativeGeoMap::setTilt(qreal tilt) +{ + tilt = qBound(minimumTilt(), tilt, maximumTilt()); + if (m_cameraData.tilt() == tilt) + return; + + m_cameraData.setTilt(tilt); + if (m_map) + m_map->setCameraData(m_cameraData); + emit tiltChanged(tilt); +} + +qreal QDeclarativeGeoMap::tilt() const +{ + return m_cameraData.tilt(); +} + +void QDeclarativeGeoMap::setMinimumTilt(qreal minimumTilt, bool userSet) +{ + if (minimumTilt >= 0) { + if (userSet) + m_userMinimumTilt = minimumTilt; + qreal oldMinimumTilt = this->minimumTilt(); + + m_minimumTilt = qBound(m_cameraCapabilities.minimumTilt(), + minimumTilt, + m_cameraCapabilities.maximumTilt()); + + if (tilt() < m_minimumTilt) + setTilt(m_minimumTilt); + + if (oldMinimumTilt != m_minimumTilt) + emit minimumTiltChanged(m_minimumTilt); + } +} + +/*! + \qmlproperty real QtLocation::Map::fieldOfView + + This property holds the field of view of the camera used to look at the map, in degrees. + If the plugin property of the map is not set, or the plugin does not support mapping, the value is 45 degrees. + + Note that changing this value implicitly changes also the distance between the camera and the map, + so that, at a tilting angle of 0 degrees, the resulting image is identical for any value used for this property. + + For more information about this parameter, consult the Wikipedia articles about \l {https://en.wikipedia.org/wiki/Field_of_view} {Field of view} and + \l {https://en.wikipedia.org/wiki/Angle_of_view} {Angle of view}. + + \sa minimumFieldOfView, maximumFieldOfView + + \since QtLocation 5.9 +*/ +void QDeclarativeGeoMap::setFieldOfView(qreal fieldOfView) +{ + fieldOfView = qBound(minimumFieldOfView(), fieldOfView, maximumFieldOfView()); + if (m_cameraData.fieldOfView() == fieldOfView) + return; + + m_cameraData.setFieldOfView(fieldOfView); + if (m_map) + m_map->setCameraData(m_cameraData); + emit fieldOfViewChanged(fieldOfView); +} + +qreal QDeclarativeGeoMap::fieldOfView() const +{ + return m_cameraData.fieldOfView(); +} + +void QDeclarativeGeoMap::setMinimumFieldOfView(qreal minimumFieldOfView, bool userSet) +{ + if (minimumFieldOfView > 0 && minimumFieldOfView < 180.0) { + if (userSet) + m_userMinimumFieldOfView = minimumFieldOfView; + qreal oldMinimumFoV = this->minimumFieldOfView(); + + m_minimumFieldOfView = qBound(m_cameraCapabilities.minimumFieldOfView(), + minimumFieldOfView, + m_cameraCapabilities.maximumFieldOfView()); + + if (fieldOfView() < m_minimumFieldOfView) + setFieldOfView(m_minimumFieldOfView); + + if (oldMinimumFoV != m_minimumFieldOfView) + emit minimumFieldOfViewChanged(m_minimumFieldOfView); + } +} + +/*! + \qmlproperty real QtLocation::Map::minimumFieldOfView + + This property holds the minimum valid field of view for the map, in degrees. + + The minimum tilt field of view by the \l plugin used is a lower bound for + this property. + If the \l plugin property is not set or the plugin does not support mapping, this property is \c 1. + + \sa fieldOfView, maximumFieldOfView + + \since QtLocation 5.9 +*/ +qreal QDeclarativeGeoMap::minimumFieldOfView() const +{ + return m_minimumFieldOfView; +} + +void QDeclarativeGeoMap::setMaximumFieldOfView(qreal maximumFieldOfView, bool userSet) +{ + if (maximumFieldOfView > 0 && maximumFieldOfView < 180.0) { + if (userSet) + m_userMaximumFieldOfView = maximumFieldOfView; + qreal oldMaximumFoV = this->maximumFieldOfView(); + + m_maximumFieldOfView = qBound(m_cameraCapabilities.minimumFieldOfView(), + maximumFieldOfView, + m_cameraCapabilities.maximumFieldOfView()); + + if (fieldOfView() > m_maximumFieldOfView) + setFieldOfView(m_maximumFieldOfView); + + if (oldMaximumFoV != m_maximumFieldOfView) + emit maximumFieldOfViewChanged(m_maximumFieldOfView); + } +} + +/*! + \qmlproperty real QtLocation::Map::maximumFieldOfView + + This property holds the maximum valid field of view for the map, in degrees. + + The minimum tilt field of view by the \l plugin used is an upper bound for + this property. + If the \l plugin property is not set or the plugin does not support mapping, this property is \c 179. + + \sa fieldOfView, minimumFieldOfView + + \since QtLocation 5.9 +*/ +qreal QDeclarativeGeoMap::maximumFieldOfView() const +{ + return m_maximumFieldOfView; +} + +/*! + \qmlproperty real QtLocation::Map::minimumTilt + + This property holds the minimum valid tilt for the map, in degrees. + + The minimum tilt defined by the \l plugin used is a lower bound for + this property. + If the \l plugin property is not set or the plugin does not support mapping, this property is \c 0. + + \sa tilt, maximumTilt + + \since QtLocation 5.9 +*/ +qreal QDeclarativeGeoMap::minimumTilt() const +{ + return m_minimumTilt; +} + +void QDeclarativeGeoMap::setMaximumTilt(qreal maximumTilt, bool userSet) +{ + if (maximumTilt >= 0) { + if (userSet) + m_userMaximumTilt = maximumTilt; + qreal oldMaximumTilt = this->maximumTilt(); + + m_maximumTilt = qBound(m_cameraCapabilities.minimumTilt(), + maximumTilt, + m_cameraCapabilities.maximumTilt()); + + if (tilt() > m_maximumTilt) + setTilt(m_maximumTilt); + + if (oldMaximumTilt != m_maximumTilt) + emit maximumTiltChanged(m_maximumTilt); + } +} + +/*! + \qmlproperty real QtLocation::Map::maximumTilt + + This property holds the maximum valid tilt for the map, in degrees. + + The maximum tilt defined by the \l plugin used is an upper bound for + this property. + If the \l plugin property is not set or the plugin does not support mapping, this property is \c 89.5. + + \sa tilt, minimumTilt + + \since QtLocation 5.9 +*/ +qreal QDeclarativeGeoMap::maximumTilt() const +{ + return m_maximumTilt; +} + +/*! + \qmlproperty coordinate QtLocation::Map::center + + This property holds the coordinate which occupies the center of the + mapping viewport. Invalid center coordinates are ignored. + + The default value is an arbitrary valid coordinate. +*/ +void QDeclarativeGeoMap::setCenter(const QGeoCoordinate ¢er) +{ + if (center == m_cameraData.center()) + return; + + if (!center.isValid()) + return; + + if (m_initialized) { + QGeoCoordinate coord(center); + coord.setLatitude(qBound(-m_maximumViewportLatitude, center.latitude(), m_maximumViewportLatitude)); + m_cameraData.setCenter(coord); + m_map->setCameraData(m_cameraData); + } else { + m_cameraData.setCenter(center); + } + + emit centerChanged(m_cameraData.center()); +} + +QGeoCoordinate QDeclarativeGeoMap::center() const +{ + return m_cameraData.center(); +} + + +/*! + \qmlproperty geoshape QtLocation::Map::visibleRegion + + This property holds the region which occupies the viewport of + the map. The camera is positioned in the center of the shape, and + at the largest integral zoom level possible which allows the + whole shape to be visible on the screen. This implies that + reading this property back shortly after having been set the + returned area is equal or larger than the set area. + + Setting this property implicitly changes the \l center and + \l zoomLevel of the map. Any previously set value to those + properties will be overridden. + + This property does not provide any change notifications. + + \since 5.6 +*/ +void QDeclarativeGeoMap::setVisibleRegion(const QGeoShape &shape) +{ + if (shape.boundingGeoRectangle() == visibleRegion()) + return; + + m_visibleRegion = shape.boundingGeoRectangle(); + if (!m_visibleRegion.isValid() + || (m_visibleRegion.bottomRight().latitude() >= 85.0) // rect entirely outside web mercator + || (m_visibleRegion.topLeft().latitude() <= -85.0)) { + // shape invalidated -> nothing to fit anymore + m_visibleRegion = QGeoRectangle(); + m_pendingFitViewport = false; + return; + } + + if (!m_map || !width() || !height()) { + m_pendingFitViewport = true; + return; + } + + fitViewportToGeoShape(); +} + +QGeoShape QDeclarativeGeoMap::visibleRegion() const +{ + if (!m_map || !width() || !height()) + return m_visibleRegion; + + if (m_map->capabilities() & QGeoMap::SupportsVisibleRegion) { + return m_map->visibleRegion(); + } else { + // ToDo: handle projections not supporting visible region in a better way. + // This approach will fail when horizon is in the view or the map is greatly zoomed out. + QList visiblePoly; + visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(0,0), false); + visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_map->viewportWidth() - 1, + 0), false); + visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(m_map->viewportWidth() - 1, + m_map->viewportHeight() - 1), false); + visiblePoly << m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(0, + m_map->viewportHeight() - 1), false); + QGeoPath path; + path.setPath(visiblePoly); + return path.boundingGeoRectangle(); + } +} + +/*! + \qmlproperty bool QtLocation::Map::copyrightsVisible + + This property holds the visibility of the copyrights notice. The notice is usually + displayed in the bottom left corner. By default, this property is set to \c true. + + \note Many map providers require the notice to be visible as part of the terms and conditions. + Please consult the relevant provider documentation before turning this notice off. + + \since 5.7 +*/ +void QDeclarativeGeoMap::setCopyrightsVisible(bool visible) +{ + if (m_copyrightsVisible == visible) + return; + + if (!m_copyrights.isNull()) + m_copyrights->setCopyrightsVisible(visible); + + m_copyrightsVisible = visible; + emit copyrightsVisibleChanged(visible); +} + +bool QDeclarativeGeoMap::copyrightsVisible() const +{ + return m_copyrightsVisible; +} + + + +/*! + \qmlproperty color QtLocation::Map::color + + This property holds the background color of the map element. + + \since 5.6 +*/ +void QDeclarativeGeoMap::setColor(const QColor &color) +{ + if (color != m_color) { + m_color = color; + update(); + emit colorChanged(m_color); + } +} + +QColor QDeclarativeGeoMap::color() const +{ + return m_color; +} + +/*! + \qmlproperty bool QtLocation::Map::mapReady + + This property holds whether the map has been successfully initialized and is ready to be used. + Some methods, such as \l fromCoordinate and \l toCoordinate, will not work before the map is ready. + Due to the architecture of the \l Map, it's advised to use the signal emitted for this property + in place of \l {QtQml::Component::completed()}{Component.onCompleted}, to make sure that everything behaves as expected. + + \since 5.9 +*/ +bool QDeclarativeGeoMap::mapReady() const +{ + return m_initialized; +} + +// TODO: offer the possibility to specify the margins. +void QDeclarativeGeoMap::fitViewportToGeoShape() +{ + if (m_map->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { + // This case remains handled here, and not inside QGeoMap*::fitViewportToGeoRectangle, + // in order to honor animations on center and zoomLevel + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + const int margins = 10; + if (!m_map || !m_visibleRegion.isValid() || width() <= margins || height() <= margins) + return; + + QDoubleVector2D topLeftPoint = p.geoToMapProjection(m_visibleRegion.topLeft()); + QDoubleVector2D bottomRightPoint = p.geoToMapProjection(m_visibleRegion.bottomRight()); + if (bottomRightPoint.x() < topLeftPoint.x()) // crossing the dateline + bottomRightPoint.setX(bottomRightPoint.x() + 1.0); + + // find center of the bounding box + QDoubleVector2D center = (topLeftPoint + bottomRightPoint) * 0.5; + center.setX(center.x() > 1.0 ? center.x() - 1.0 : center.x()); + QGeoCoordinate centerCoordinate = p.mapProjectionToGeo(center); + + // position camera to the center of bounding box + setProperty("center", QVariant::fromValue(centerCoordinate)); // not using setCenter(centerCoordinate) to honor a possible animation set on the center property + + // if the shape is empty we just change center position, not zoom + double bboxWidth = (bottomRightPoint.x() - topLeftPoint.x()) * m_map->mapWidth(); + double bboxHeight = (bottomRightPoint.y() - topLeftPoint.y()) * m_map->mapHeight(); + + if (bboxHeight == 0.0 && bboxWidth == 0.0) + return; + + double zoomRatio = qMax(bboxWidth / (width() - margins), + bboxHeight / (height() - margins)); + zoomRatio = std::log(zoomRatio) / std::log(2.0); + double newZoom = qMax(minimumZoomLevel(), zoomLevel() - zoomRatio); + setProperty("zoomLevel", QVariant::fromValue(newZoom)); // not using setZoomLevel(newZoom) to honor a possible animation set on the zoomLevel property + } else if (m_map->capabilities() & QGeoMap::SupportsFittingViewportToGeoRectangle) { + // Animations cannot be honored in this case, as m_map act as a black box + const QGeoCoordinate currentCenter = center(); + const qreal currentZoom = zoomLevel(); + + if (!m_map->fitViewportToGeoRectangle(m_visibleRegion)) + return; + + m_cameraData = m_map->cameraData(); + if (m_cameraData.center() != currentCenter) + emit centerChanged(m_cameraData.center()); + if (m_cameraData.zoomLevel() != currentZoom) + emit zoomLevelChanged(m_cameraData.zoomLevel()); + } +} + + +/*! + \qmlproperty list QtLocation::Map::supportedMapTypes + + This read-only property holds the set of \l{MapType}{map types} supported by this map. + + \sa activeMapType +*/ +QQmlListProperty QDeclarativeGeoMap::supportedMapTypes() +{ + return QQmlListProperty(this, m_supportedMapTypes); +} + +/*! + \qmlmethod void QtLocation::Map::alignCoordinateToPoint(coordinate coordinate, QPointF point) + + Aligns \a coordinate to \a point. + This method effectively extends the functionality offered by the \l center qml property, allowing + to align a coordinate to point of the Map element other than its center. + This is useful in those applications where the center of the scene (e.g., a cursor) is not to be + placed exactly in the center of the map. + + If the map is tilted, and \a coordinate happens to be behind the camera, or if the map is not ready + (see \l mapReady), calling this method will have no effect. + + The release of this API with Qt 5.10 is a Technology Preview. + + \sa center + + \since 5.10 +*/ +void QDeclarativeGeoMap::alignCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &point) +{ + if (!m_map || !(m_map->capabilities() & QGeoMap::SupportsAnchoringCoordinate)) + return; + + const QGeoCoordinate currentCenter = center(); + + if (!coordinate.isValid() + || !qIsFinite(point.x()) + || !qIsFinite(point.y())) + return; + + if (!m_map->anchorCoordinateToPoint(coordinate, point)) + return; + + m_cameraData = m_map->cameraData(); + + if (m_cameraData.center() != currentCenter) + emit centerChanged(m_cameraData.center()); +} + +/*! + \qmlmethod coordinate QtLocation::Map::toCoordinate(QPointF position, bool clipToViewPort) + + Returns the coordinate which corresponds to the \a position relative to the map item. + + If \a cliptoViewPort is \c true, or not supplied then returns an invalid coordinate if + \a position is not within the current viewport. +*/ +QGeoCoordinate QDeclarativeGeoMap::toCoordinate(const QPointF &position, bool clipToViewPort) const +{ + if (m_map) + return m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(position), clipToViewPort); + else + return QGeoCoordinate(); +} + +/*! + \qmlmethod point QtLocation::Map::fromCoordinate(coordinate coordinate, bool clipToViewPort) + + Returns the position relative to the map item which corresponds to the \a coordinate. + + If \a cliptoViewPort is \c true, or not supplied then returns an invalid QPointF if + \a coordinate is not within the current viewport. +*/ +QPointF QDeclarativeGeoMap::fromCoordinate(const QGeoCoordinate &coordinate, bool clipToViewPort) const +{ + if (m_map) + return m_map->geoProjection().coordinateToItemPosition(coordinate, clipToViewPort).toPointF(); + else + return QPointF(qQNaN(), qQNaN()); +} + +/*! + \qmlmethod void QtLocation::Map::pan(int dx, int dy) + + Starts panning the map by \a dx pixels along the x-axis and + by \a dy pixels along the y-axis. + + Positive values for \a dx move the map right, negative values left. + Positive values for \a dy move the map down, negative values up. + + During panning the \l center, and \l zoomLevel may change. +*/ +void QDeclarativeGeoMap::pan(int dx, int dy) +{ + if (!m_map) + return; + if (dx == 0 && dy == 0) + return; + + QGeoCoordinate coord = m_map->geoProjection().itemPositionToCoordinate( + QDoubleVector2D(m_map->viewportWidth() / 2 + dx, + m_map->viewportHeight() / 2 + dy)); + setCenter(coord); +} + + +/*! + \qmlmethod void QtLocation::Map::prefetchData() + + Optional hint that allows the map to prefetch during this idle period +*/ +void QDeclarativeGeoMap::prefetchData() +{ + if (!m_map) + return; + m_map->prefetchData(); +} + +/*! + \qmlmethod void QtLocation::Map::clearData() + + Clears map data collected by the currently selected plugin. + \note This method will delete cached files. + \sa plugin +*/ +void QDeclarativeGeoMap::clearData() +{ + if (m_map) + m_map->clearData(); +} + +/*! + \qmlproperty string QtLocation::Map::errorString + + This read-only property holds the textual presentation of the latest mapping provider error. + If no error has occurred, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. + + \sa QGeoServiceProvider::errorString() +*/ + +QString QDeclarativeGeoMap::errorString() const +{ + return m_errorString; +} + +/*! + \qmlproperty enumeration QtLocation::Map::error + + This read-only property holds the last occurred mapping service provider error. + + \list + \li Map.NoError - No error has occurred. + \li Map.NotSupportedError -The maps plugin property was not set or there is no mapping manager associated with the plugin. + \li Map.UnknownParameterError -The plugin did not recognize one of the parameters it was given. + \li Map.MissingRequiredParameterError - The plugin did not find one of the parameters it was expecting. + \li Map.ConnectionError - The plugin could not connect to its backend service or database. + \endlist + + \sa QGeoServiceProvider::Error +*/ + +QGeoServiceProvider::Error QDeclarativeGeoMap::error() const +{ + return m_error; +} + +QGeoMap *QDeclarativeGeoMap::map() const +{ + return m_map; +} + +void QDeclarativeGeoMap::itemChange(ItemChange change, const ItemChangeData &value) +{ + if (change == ItemChildAddedChange) { + QQuickItem *child = value.item; + QQuickItem *mapItem = qobject_cast(child); + if (!mapItem) + mapItem = qobject_cast(child); + + if (mapItem) { + qreal z = mapItem->z(); + if (z > m_maxChildZ) { // Ignore children removal + m_maxChildZ = z; + // put the copyrights notice object at the highest z order + if (m_copyrights) + m_copyrights->setCopyrightsZ(m_maxChildZ + 1); + } + } + } + QQuickItem::itemChange(change, value); +} + +bool QDeclarativeGeoMap::isInteractive() +{ + return (m_gestureArea->enabled() && m_gestureArea->acceptedGestures()) || m_gestureArea->isActive(); +} + +void QDeclarativeGeoMap::attachCopyrightNotice(bool initialVisibility) +{ + if (initialVisibility) { + ++m_copyNoticesVisible; + if (m_map) + m_map->setCopyrightVisible(m_copyNoticesVisible > 0); + } +} + +void QDeclarativeGeoMap::detachCopyrightNotice(bool currentVisibility) +{ + if (currentVisibility) { + --m_copyNoticesVisible; + if (m_map) + m_map->setCopyrightVisible(m_copyNoticesVisible > 0); + } +} + +void QDeclarativeGeoMap::onAttachedCopyrightNoticeVisibilityChanged() +{ + QDeclarativeGeoMapCopyrightNotice *copy = static_cast(sender()); + m_copyNoticesVisible += ( int(copy->copyrightsVisible()) * 2 - 1); + if (m_map) + m_map->setCopyrightVisible(m_copyNoticesVisible > 0); +} + +/*! + \qmlmethod void QtLocation::Map::addMapItem(MapItem item) + + Adds the given \a item to the Map (for example MapQuickItem, MapCircle). If the object + already is on the Map, it will not be added again. + + As an example, consider the case where you have a MapCircle representing your current position: + + \snippet declarative/maps.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/maps.qml Map addMapItem MapCircle at current position + + \note MapItemViews cannot be added with this method. + + \sa mapItems, removeMapItem, clearMapItems +*/ + +void QDeclarativeGeoMap::addMapItem(QDeclarativeGeoMapItemBase *item) +{ + if (!item || item->quickMap()) + return; + // If the item comes from a MapItemGroup, do not reparent it. + if (!qobject_cast(item->parentItem())) + item->setParentItem(this); + m_mapItems.append(item); + if (m_map) { + item->setMap(this, m_map); + m_map->addMapItem(item); + } + emit mapItemsChanged(); +} + +/*! + \qmlmethod void QtLocation::Map::addMapParameter(MapParameter parameter) + + Adds a MapParameter object to the map. The effect of this call is dependent + on the combination of the content of the MapParameter and the type of + underlying QGeoMap. If a MapParameter that is not supported by the underlying + QGeoMap gets added, the call has no effect. + + The release of this API with Qt 5.9 is a Technology Preview. + + \sa MapParameter, removeMapParameter, mapParameters, clearMapParameters + + \since 5.9 +*/ +void QDeclarativeGeoMap::addMapParameter(QDeclarativeGeoMapParameter *parameter) +{ + if (!parameter->isComponentComplete()) { + connect(parameter, &QDeclarativeGeoMapParameter::completed, this, &QDeclarativeGeoMap::addMapParameter); + return; + } + + disconnect(parameter); + if (m_mapParameters.contains(parameter)) + return; + parameter->setParent(this); + m_mapParameters.append(parameter); // parameter now owned by QDeclarativeGeoMap + if (m_map) + m_map->addParameter(parameter); +} + +/*! + \qmlmethod void QtLocation::Map::removeMapParameter(MapParameter parameter) + + Removes the given MapParameter object from the map. + + The release of this API with Qt 5.9 is a Technology Preview. + + \sa MapParameter, addMapParameter, mapParameters, clearMapParameters + + \since 5.9 +*/ +void QDeclarativeGeoMap::removeMapParameter(QDeclarativeGeoMapParameter *parameter) +{ + if (!m_mapParameters.contains(parameter)) + return; + if (m_map) + m_map->removeParameter(parameter); + m_mapParameters.removeOne(parameter); +} + +/*! + \qmlmethod void QtLocation::Map::clearMapParameters() + + Removes all map parameters from the map. + + The release of this API with Qt 5.9 is a Technology Preview. + + \sa MapParameter, mapParameters, addMapParameter, removeMapParameter, clearMapParameters + + \since 5.9 +*/ +void QDeclarativeGeoMap::clearMapParameters() +{ + if (m_map) + m_map->clearParameters(); + m_mapParameters.clear(); +} + +/*! + \qmlproperty list QtLocation::Map::mapParameters + + Returns the list of all map parameters in no particular order. + These items include map parameters that were declared statically as part of + the type declaration, as well as dynamical map parameters (\l addMapParameter). + + The release of this API with Qt 5.9 is a Technology Preview. + + \sa MapParameter, addMapParameter, removeMapParameter, clearMapParameters + + \since 5.9 +*/ +QList QDeclarativeGeoMap::mapParameters() +{ + QList ret; + for (QDeclarativeGeoMapParameter *p : qAsConst(m_mapParameters)) + ret << p; + return ret; +} + +/* + \internal +*/ +void QDeclarativeGeoMap::addMapObject(QGeoMapObject *object) +{ + if (!object || object->map()) + return; + + if (!m_initialized) { + m_pendingMapObjects.append(object); + return; + } + + int curObjects = m_map->mapObjects().size(); + // object adds itself to the map + object->setMap(m_map); + + if (curObjects != m_map->mapObjects().size()) + emit mapObjectsChanged(); +} + +/* + \internal +*/ +void QDeclarativeGeoMap::removeMapObject(QGeoMapObject *object) +{ + if (!object || object->map() != m_map) // if !initialized this is fine, since both object and m_map are supposed to be NULL + return; + + if (!m_initialized) { + m_pendingMapObjects.removeOne(object); + return; + } + + int curObjects = m_map->mapObjects().size(); + // object adds itself to the map + object->setMap(nullptr); + + if (curObjects != m_map->mapObjects().size()) + emit mapObjectsChanged(); +} + +/* + \internal +*/ +void QDeclarativeGeoMap::clearMapObjects() +{ + if (!m_initialized) { + m_pendingMapObjects.clear(); + } else { + const QList objs = m_map->mapObjects(); + for (QGeoMapObject *o: objs) + o->setMap(nullptr); + if (objs.size()) + emit mapObjectsChanged(); + } +} + +/* + \internal +*/ +QList QDeclarativeGeoMap::mapObjects() +{ + if (!m_initialized) + return m_pendingMapObjects; + else + return m_map->mapObjects(); +} + +/*! + \qmlproperty list QtLocation::Map::mapItems + + Returns the list of all map items in no particular order. + These items include items that were declared statically as part of + the type declaration, as well as dynamical items (\l addMapItem, + \l MapItemView). + + \sa addMapItem, removeMapItem, clearMapItems +*/ + +QList QDeclarativeGeoMap::mapItems() +{ + QList ret; + foreach (const QPointer &ptr, m_mapItems) { + if (ptr) + ret << ptr.data(); + } + return ret; +} + +/*! + \qmlmethod void QtLocation::Map::removeMapItem(MapItem item) + + Removes the given \a item from the Map (for example MapQuickItem, MapCircle). If + the MapItem does not exist or was not previously added to the map, the + method does nothing. + + \sa mapItems, addMapItem, clearMapItems +*/ +void QDeclarativeGeoMap::removeMapItem(QDeclarativeGeoMapItemBase *ptr) +{ + if (!ptr || !m_map) + return; + m_map->removeMapItem(ptr); + QPointer item(ptr); + if (!m_mapItems.contains(item)) + return; + if (item->parentItem() == this) + item->setParentItem(0); + item->setMap(0, 0); + // these can be optimized for perf, as we already check the 'contains' above + m_mapItems.removeOne(item); + emit mapItemsChanged(); +} + +/*! + \qmlmethod void QtLocation::Map::clearMapItems() + + Removes all items and item groups from the map. + + \sa mapItems, addMapItem, removeMapItem, addMapItemGroup, removeMapItemGroup +*/ +void QDeclarativeGeoMap::clearMapItems() +{ + if (m_mapItems.isEmpty()) + return; + if (m_map) + m_map->clearMapItems(); + for (auto i : qAsConst(m_mapItems)) { + if (i) { + i->setMap(0, 0); + if (i->parentItem() == this) + i->setParentItem(0); + } + } + m_mapItems.clear(); + m_mapItemGroups.clear(); + emit mapItemsChanged(); +} + +/*! + \qmlmethod void QtLocation::Map::addMapItemGroup(MapItemGroup itemGroup) + + Adds the map items contained in the given \a itemGroup to the Map + (for example MapQuickItem, MapCircle). + + \sa MapItemGroup, removeMapItemGroup + + \since 5.9 +*/ +void QDeclarativeGeoMap::addMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup) +{ + if (!itemGroup || itemGroup->quickMap()) // || Already added to some map + return; + + itemGroup->setQuickMap(this); + QPointer g(itemGroup); + m_mapItemGroups.append(g); + const QList quickKids = g->childItems(); + for (auto c: quickKids) { + QDeclarativeGeoMapItemBase *mapItem = qobject_cast(c); + if (mapItem) + addMapItem(mapItem); + } + itemGroup->setParentItem(this); +} + +/*! + \qmlmethod void QtLocation::Map::removeMapItemGroup(MapItemGroup itemGroup) + + Removes \a itemGroup and the items contained therein from the Map. + + \sa MapItemGroup, addMapItemGroup + + \since 5.9 +*/ +void QDeclarativeGeoMap::removeMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup) +{ + if (!itemGroup || itemGroup->quickMap() != this) // cant remove an itemGroup added to another map + return; + + QPointer g(itemGroup); + if (!m_mapItemGroups.removeOne(g)) + return; + + const QList quickKids = itemGroup->childItems(); + for (auto c: quickKids) { + QDeclarativeGeoMapItemBase *mapItem = qobject_cast(c); + if (mapItem) + removeMapItem(mapItem); + } + itemGroup->setQuickMap(nullptr); + itemGroup->setParentItem(0); +} + +/*! + \qmlmethod void QtLocation::Map::removeMapItemView(MapItemView itemView) + + Removes \a itemView and the items instantiated by it from the Map. + + \sa MapItemView, addMapItemView + + \since 5.10 +*/ +void QDeclarativeGeoMap::removeMapItemView(QDeclarativeGeoMapItemView *itemView) +{ + if (!itemView || itemView->m_map != this) // can't remove a view that is already added to another map + return; + + itemView->removeInstantiatedItems(); + itemView->m_map = 0; + // it can be removed from the list at this point, since no operations that require a Map have to be done + // anymore on destruction. + m_mapViews.removeOne(itemView); +} + +/*! + \qmlmethod void QtLocation::Map::addMapItemView(MapItemView itemView) + + Adds \a itemView to the Map. + + \sa MapItemView, removeMapItemView + + \since 5.10 +*/ +void QDeclarativeGeoMap::addMapItemView(QDeclarativeGeoMapItemView *itemView) +{ + if (!itemView || itemView->m_map) // can't add a view twice + return; + + // Not appending it to m_mapViews because it seems unnecessary even if the + // itemView is a child of this (in which case it would be destroyed + m_mapViews.append(itemView); + setupMapView(itemView); +} + +/*! + \qmlproperty MapType QtLocation::Map::activeMapType + + \brief Access to the currently active \l{MapType}{map type}. + + This property can be set to change the active \l{MapType}{map type}. + See the \l{Map::supportedMapTypes}{supportedMapTypes} property for possible values. + + \sa MapType +*/ +void QDeclarativeGeoMap::setActiveMapType(QDeclarativeGeoMapType *mapType) +{ + if (m_activeMapType->mapType() != mapType->mapType()) { + if (m_map) { + if (mapType->mapType().pluginName() == m_plugin->name().toLatin1()) { + m_map->setActiveMapType(mapType->mapType()); + m_activeMapType = mapType; + emit activeMapTypeChanged(); + } + } else { + m_activeMapType = mapType; + emit activeMapTypeChanged(); + } + } +} + +QDeclarativeGeoMapType * QDeclarativeGeoMap::activeMapType() const +{ + return m_activeMapType; +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + m_gestureArea->setSize(newGeometry.size()); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + if (!m_map || newGeometry.size().isEmpty()) + return; + + m_map->setViewportSize(newGeometry.size().toSize()); + + if (!m_initialized) { + initialize(); + } else { + setMinimumZoomLevel(m_map->minimumZoom(), false); + + // Update the center latitudinal threshold + double maximumCenterLatitudeAtZoom = m_map->maximumCenterLatitudeAtZoom(m_cameraData); + if (maximumCenterLatitudeAtZoom != m_maximumViewportLatitude) { + m_maximumViewportLatitude = maximumCenterLatitudeAtZoom; + QGeoCoordinate coord = m_cameraData.center(); + coord.setLatitude(qBound(-m_maximumViewportLatitude, coord.latitude(), m_maximumViewportLatitude)); + + if (coord != m_cameraData.center()) { + m_cameraData.setCenter(coord); + m_map->setCameraData(m_cameraData); + emit centerChanged(m_cameraData.center()); + } + } + } + + /* + The fitViewportTo*() functions depend on a valid map geometry. + If they were called prior to the first resize they cause + the zoomlevel to jump to 0 (showing the world). Therefore the + calls were queued up until now. + + Multiple fitViewportTo*() calls replace each other. + */ + if (m_pendingFitViewport && width() && height()) { + fitViewportToGeoShape(); + m_pendingFitViewport = false; + } + +} + +/*! + \qmlmethod void QtLocation::Map::fitViewportToMapItems() + + Fits the current viewport to the boundary of all map items. The camera is positioned + in the center of the map items, and at the largest integral zoom level possible which + allows all map items to be visible on screen. + + \sa fitViewportToVisibleMapItems +*/ +void QDeclarativeGeoMap::fitViewportToMapItems() +{ + fitViewportToMapItemsRefine(true, false); +} + +/*! + \qmlmethod void QtLocation::Map::fitViewportToVisibleMapItems() + + Fits the current viewport to the boundary of all \b visible map items. + The camera is positioned in the center of the map items, and at the largest integral + zoom level possible which allows all map items to be visible on screen. + + \sa fitViewportToMapItems +*/ +void QDeclarativeGeoMap::fitViewportToVisibleMapItems() +{ + fitViewportToMapItemsRefine(true, true); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::fitViewportToMapItemsRefine(bool refine, bool onlyVisible) +{ + if (!m_map) + return; + + if (m_mapItems.size() == 0) + return; + + double minX = qInf(); + double maxX = -qInf(); + double minY = qInf(); + double maxY = -qInf(); + double topLeftX = 0; + double topLeftY = 0; + double bottomRightX = 0; + double bottomRightY = 0; + bool haveQuickItem = false; + + // find bounds of all map items + int itemCount = 0; + for (int i = 0; i < m_mapItems.count(); ++i) { + if (!m_mapItems.at(i)) + continue; + QDeclarativeGeoMapItemBase *item = m_mapItems.at(i).data(); + if (!item || (onlyVisible && (!item->isVisible() || item->mapItemOpacity() <= 0.0))) + continue; + + // skip quick items in the first pass and refine the fit later + QDeclarativeGeoMapQuickItem *quickItem = + qobject_cast(item); + if (refine && quickItem) { + haveQuickItem = true; + continue; + } + // Force map items to update immediately. Needed to ensure correct item size and positions + // when recursively calling this function. + // TODO: See if we really need updatePolish on delegated items, in particular + // in relation to + // a) fitViewportToMapItems + // b) presence of MouseArea + // + // This is also legacy code. It must be updated to not operate on screen sizes. + if (item->isPolishScheduled()) + item->updatePolish(); + + if (quickItem && quickItem->matrix_ && !quickItem->matrix_->m_matrix.isIdentity()) { + // TODO: recalculate the center/zoom level so that the item becomes projectable again + if (quickItem->zoomLevel() == 0.0) // the item is unprojectable, should be skipped. + continue; + + QRectF brect = item->boundingRect(); + brect = quickItem->matrix_->m_matrix.mapRect(brect); + QPointF transformedPosition = quickItem->matrix_->m_matrix * item->position(); + topLeftX = transformedPosition.x(); + topLeftY = transformedPosition.y(); + bottomRightX = topLeftX + brect.width(); + bottomRightY = topLeftY + brect.height(); + } else { + topLeftX = item->position().x(); + topLeftY = item->position().y(); + bottomRightX = topLeftX + item->width(); + bottomRightY = topLeftY + item->height(); + } + + minX = qMin(minX, topLeftX); + maxX = qMax(maxX, bottomRightX); + minY = qMin(minY, topLeftY); + maxY = qMax(maxY, bottomRightY); + + ++itemCount; + } + + if (itemCount == 0) { + if (haveQuickItem) + fitViewportToMapItemsRefine(false, onlyVisible); + return; + } + double bboxWidth = maxX - minX; + double bboxHeight = maxY - minY; + double bboxCenterX = minX + (bboxWidth / 2.0); + double bboxCenterY = minY + (bboxHeight / 2.0); + + // position camera to the center of bounding box + QGeoCoordinate coordinate; + coordinate = m_map->geoProjection().itemPositionToCoordinate(QDoubleVector2D(bboxCenterX, bboxCenterY), false); + setProperty("center", QVariant::fromValue(coordinate)); + + // adjust zoom + double bboxWidthRatio = bboxWidth / (bboxWidth + bboxHeight); + double mapWidthRatio = width() / (width() + height()); + double zoomRatio; + + if (bboxWidthRatio > mapWidthRatio) + zoomRatio = bboxWidth / width(); + else + zoomRatio = bboxHeight / height(); + + qreal newZoom = std::log10(zoomRatio) / std::log10(0.5); + newZoom = std::floor(qMax(minimumZoomLevel(), (zoomLevel() + newZoom))); + setProperty("zoomLevel", QVariant::fromValue(newZoom)); + + // as map quick items retain the same screen size after the camera zooms in/out + // we refine the viewport again to achieve better results + if (refine) + fitViewportToMapItemsRefine(false, onlyVisible); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mousePressEvent(QMouseEvent *event) +{ + if (isInteractive()) + m_gestureArea->handleMousePressEvent(event); + else + QQuickItem::mousePressEvent(event); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mouseMoveEvent(QMouseEvent *event) +{ + if (isInteractive()) + m_gestureArea->handleMouseMoveEvent(event); + else + QQuickItem::mouseMoveEvent(event); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mouseReleaseEvent(QMouseEvent *event) +{ + if (isInteractive()) + m_gestureArea->handleMouseReleaseEvent(event); + else + QQuickItem::mouseReleaseEvent(event); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::mouseUngrabEvent() +{ + if (isInteractive()) + m_gestureArea->handleMouseUngrabEvent(); + else + QQuickItem::mouseUngrabEvent(); +} + +void QDeclarativeGeoMap::touchUngrabEvent() +{ + if (isInteractive()) + m_gestureArea->handleTouchUngrabEvent(); + else + QQuickItem::touchUngrabEvent(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMap::touchEvent(QTouchEvent *event) +{ + if (isInteractive()) { + m_gestureArea->handleTouchEvent(event); + } else { + //ignore event so sythesized event is generated; + QQuickItem::touchEvent(event); + } +} + +#if QT_CONFIG(wheelevent) +/*! + \internal +*/ +void QDeclarativeGeoMap::wheelEvent(QWheelEvent *event) +{ + if (isInteractive()) + m_gestureArea->handleWheelEvent(event); + else + QQuickItem::wheelEvent(event); + +} +#endif + +/*! + \internal +*/ +bool QDeclarativeGeoMap::childMouseEventFilter(QQuickItem *item, QEvent *event) +{ + Q_UNUSED(item) + if (!isVisible() || !isEnabled() || !isInteractive()) + return QQuickItem::childMouseEventFilter(item, event); + + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::MouseMove: + case QEvent::MouseButtonRelease: + return sendMouseEvent(static_cast(event)); + case QEvent::UngrabMouse: { + QQuickWindow *win = window(); + if (!win) break; + if (!win->mouseGrabberItem() || + (win->mouseGrabberItem() && + win->mouseGrabberItem() != this)) { + // child lost grab, we could even lost + // some events if grab already belongs for example + // in item in diffrent window , clear up states + mouseUngrabEvent(); + } + break; + } + case QEvent::TouchBegin: + case QEvent::TouchUpdate: + case QEvent::TouchEnd: + case QEvent::TouchCancel: + if (static_cast(event)->touchPoints().count() >= 2) { + // 1 touch point = handle with MouseEvent (event is always synthesized) + // let the synthesized mouse event grab the mouse, + // note there is no mouse grabber at this point since + // touch event comes first (see Qt::AA_SynthesizeMouseForUnhandledTouchEvents) + return sendTouchEvent(static_cast(event)); + } + default: + break; + } + return QQuickItem::childMouseEventFilter(item, event); +} + +bool QDeclarativeGeoMap::sendMouseEvent(QMouseEvent *event) +{ + QPointF localPos = mapFromScene(event->windowPos()); + QQuickWindow *win = window(); + QQuickItem *grabber = win ? win->mouseGrabberItem() : 0; + bool stealEvent = m_gestureArea->isActive(); + + if ((stealEvent || contains(localPos)) && (!grabber || (!grabber->keepMouseGrab() && !grabber->keepTouchGrab()))) { + QScopedPointer mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos)); + mouseEvent->setAccepted(false); + + switch (mouseEvent->type()) { + case QEvent::MouseMove: + m_gestureArea->handleMouseMoveEvent(mouseEvent.data()); + break; + case QEvent::MouseButtonPress: + m_gestureArea->handleMousePressEvent(mouseEvent.data()); + break; + case QEvent::MouseButtonRelease: + m_gestureArea->handleMouseReleaseEvent(mouseEvent.data()); + break; + default: + break; + } + + stealEvent = m_gestureArea->isActive(); + grabber = win ? win->mouseGrabberItem() : 0; + + if (grabber && stealEvent && !grabber->keepMouseGrab() && !grabber->keepTouchGrab() && grabber != this) + grabMouse(); + + if (stealEvent) { + //do not deliver + event->setAccepted(true); + return true; + } else { + return false; + } + } + + return false; +} + +bool QDeclarativeGeoMap::sendTouchEvent(QTouchEvent *event) +{ + QQuickPointerDevice *touchDevice = QQuickPointerDevice::touchDevice(event->device()); + const QTouchEvent::TouchPoint &point = event->touchPoints().first(); + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window()); + + auto touchPointGrabberItem = [touchDevice, windowPriv](const QTouchEvent::TouchPoint &point) -> QQuickItem* { + if (QQuickEventPoint *eventPointer = windowPriv->pointerEventInstance(touchDevice)->pointById(point.id())) + return eventPointer->grabberItem(); + return nullptr; + }; + + QQuickItem *grabber = touchPointGrabberItem(point); + + bool stealEvent = m_gestureArea->isActive(); + bool containsPoint = contains(mapFromScene(point.scenePos())); + + if ((stealEvent || containsPoint) && (!grabber || !grabber->keepTouchGrab())) { + QScopedPointer touchEvent(new QTouchEvent(event->type(), event->device(), event->modifiers(), event->touchPointStates(), event->touchPoints())); + touchEvent->setTimestamp(event->timestamp()); + touchEvent->setAccepted(false); + + m_gestureArea->handleTouchEvent(touchEvent.data()); + stealEvent = m_gestureArea->isActive(); + grabber = touchPointGrabberItem(point); + + if (grabber && stealEvent && !grabber->keepTouchGrab() && grabber != this) { + QVector ids; + foreach (const QTouchEvent::TouchPoint &tp, event->touchPoints()) { + if (!(tp.state() & Qt::TouchPointReleased)) { + ids.append(tp.id()); + } + } + grabTouchPoints(ids); + } + + if (stealEvent) { + //do not deliver + event->setAccepted(true); + return true; + } else { + return false; + } + } + + return false; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomap_p.h b/src/location/declarativemaps/qdeclarativegeomap_p.h new file mode 100644 index 0000000..7f1bf7a --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomap_p.h @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAP_H +#define QDECLARATIVEGEOMAP_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; +class QDeclarativeGeoMapType; +class QDeclarativeGeoMapCopyrightNotice; +class QDeclarativeGeoMapParameter; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMap : public QQuickItem +{ + Q_OBJECT + Q_ENUMS(QGeoServiceProvider::Error) + Q_PROPERTY(QQuickGeoMapGestureArea *gesture READ gesture CONSTANT) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel WRITE setMinimumZoomLevel NOTIFY minimumZoomLevelChanged) + Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel WRITE setMaximumZoomLevel NOTIFY maximumZoomLevelChanged) + Q_PROPERTY(qreal zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY zoomLevelChanged) + + Q_PROPERTY(qreal tilt READ tilt WRITE setTilt NOTIFY tiltChanged) + Q_PROPERTY(qreal minimumTilt READ minimumTilt WRITE setMinimumTilt NOTIFY minimumTiltChanged) + Q_PROPERTY(qreal maximumTilt READ maximumTilt WRITE setMaximumTilt NOTIFY maximumTiltChanged) + + Q_PROPERTY(qreal bearing READ bearing WRITE setBearing NOTIFY bearingChanged) + + Q_PROPERTY(qreal fieldOfView READ fieldOfView WRITE setFieldOfView NOTIFY fieldOfViewChanged) + Q_PROPERTY(qreal minimumFieldOfView READ minimumFieldOfView WRITE setMinimumFieldOfView NOTIFY minimumFieldOfViewChanged) + Q_PROPERTY(qreal maximumFieldOfView READ maximumFieldOfView WRITE setMaximumFieldOfView NOTIFY minimumFieldOfViewChanged) + + Q_PROPERTY(QDeclarativeGeoMapType *activeMapType READ activeMapType WRITE setActiveMapType NOTIFY activeMapTypeChanged) + Q_PROPERTY(QQmlListProperty supportedMapTypes READ supportedMapTypes NOTIFY supportedMapTypesChanged) + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) + Q_PROPERTY(QList mapItems READ mapItems NOTIFY mapItemsChanged) + Q_PROPERTY(QList mapParameters READ mapParameters) + Q_PROPERTY(QGeoServiceProvider::Error error READ error NOTIFY errorChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(QGeoShape visibleRegion READ visibleRegion WRITE setVisibleRegion) + Q_PROPERTY(bool copyrightsVisible READ copyrightsVisible WRITE setCopyrightsVisible NOTIFY copyrightsVisibleChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(bool mapReady READ mapReady NOTIFY mapReadyChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + + explicit QDeclarativeGeoMap(QQuickItem *parent = 0); + ~QDeclarativeGeoMap(); + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setActiveMapType(QDeclarativeGeoMapType *mapType); + QDeclarativeGeoMapType *activeMapType() const; + + void setMinimumZoomLevel(qreal minimumZoomLevel, bool userSet = true); + qreal minimumZoomLevel() const; + qreal implicitMinimumZoomLevel() const; + qreal effectiveMinimumZoomLevel() const; + + void setMaximumZoomLevel(qreal maximumZoomLevel, bool userSet = true); + qreal maximumZoomLevel() const; + + void setZoomLevel(qreal zoomLevel); + qreal zoomLevel() const; + + void setBearing(qreal bearing); + qreal bearing() const; + + void setTilt(qreal tilt); + qreal tilt() const; + void setMinimumTilt(qreal minimumTilt, bool userSet = true); + qreal minimumTilt() const; + void setMaximumTilt(qreal maximumTilt, bool userSet = true); + qreal maximumTilt() const; + + void setFieldOfView(qreal fieldOfView); + qreal fieldOfView() const; + void setMinimumFieldOfView(qreal minimumFieldOfView, bool userSet = true); + qreal minimumFieldOfView() const; + void setMaximumFieldOfView(qreal maximumFieldOfView, bool userSet = true); + qreal maximumFieldOfView() const; + + void setCenter(const QGeoCoordinate ¢er); + QGeoCoordinate center() const; + + void setVisibleRegion(const QGeoShape &shape); + QGeoShape visibleRegion() const; + + void setCopyrightsVisible(bool visible); + bool copyrightsVisible() const; + + void setColor(const QColor &color); + QColor color() const; + + bool mapReady() const; + + QQmlListProperty supportedMapTypes(); + + Q_INVOKABLE void setBearing(qreal bearing, const QGeoCoordinate &coordinate); + Q_INVOKABLE void alignCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &point); + + Q_INVOKABLE void removeMapItem(QDeclarativeGeoMapItemBase *item); + Q_INVOKABLE void addMapItem(QDeclarativeGeoMapItemBase *item); + + Q_INVOKABLE void addMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup); + Q_INVOKABLE void removeMapItemGroup(QDeclarativeGeoMapItemGroup *itemGroup); + + Q_INVOKABLE void removeMapItemView(QDeclarativeGeoMapItemView *itemView); + Q_INVOKABLE void addMapItemView(QDeclarativeGeoMapItemView *itemView); + + Q_INVOKABLE void clearMapItems(); + QList mapItems(); + + Q_INVOKABLE void addMapParameter(QDeclarativeGeoMapParameter *parameter); + Q_INVOKABLE void removeMapParameter(QDeclarativeGeoMapParameter *parameter); + Q_INVOKABLE void clearMapParameters(); + QList mapParameters(); + + void addMapObject(QGeoMapObject *object); + void removeMapObject(QGeoMapObject *object); + void clearMapObjects(); + QList mapObjects(); + + + Q_INVOKABLE QGeoCoordinate toCoordinate(const QPointF &position, bool clipToViewPort = true) const; + Q_INVOKABLE QPointF fromCoordinate(const QGeoCoordinate &coordinate, bool clipToViewPort = true) const; + + QQuickGeoMapGestureArea *gesture(); + + Q_INVOKABLE void fitViewportToMapItems(); + Q_INVOKABLE void fitViewportToVisibleMapItems(); + Q_INVOKABLE void pan(int dx, int dy); + Q_INVOKABLE void prefetchData(); // optional hint for prefetch + Q_INVOKABLE void clearData(); + + QString errorString() const; + QGeoServiceProvider::Error error() const; + QGeoMap* map() const; + + // From QQuickItem + void itemChange(ItemChange, const ItemChangeData &) override; + +Q_SIGNALS: + void pluginChanged(QDeclarativeGeoServiceProvider *plugin); + void zoomLevelChanged(qreal zoomLevel); + void centerChanged(const QGeoCoordinate &coordinate); + void activeMapTypeChanged(); + void supportedMapTypesChanged(); + void minimumZoomLevelChanged(); + void maximumZoomLevelChanged(); + void mapItemsChanged(); + void errorChanged(); + void copyrightLinkActivated(const QString &link); + void copyrightsVisibleChanged(bool visible); + void colorChanged(const QColor &color); + void bearingChanged(qreal bearing); + void tiltChanged(qreal tilt); + void fieldOfViewChanged(qreal fieldOfView); + void minimumTiltChanged(qreal minimumTilt); + void maximumTiltChanged(qreal maximumTilt); + void minimumFieldOfViewChanged(qreal minimumFieldOfView); + void maximumFieldOfViewChanged(qreal maximumFieldOfView); + void copyrightsChanged(const QImage ©rightsImage); + void copyrightsChanged(const QString ©rightsHtml); + void mapReadyChanged(bool ready); + Q_REVISION(11) void mapObjectsChanged(); + +protected: + void mousePressEvent(QMouseEvent *event) override ; + void mouseMoveEvent(QMouseEvent *event) override ; + void mouseReleaseEvent(QMouseEvent *event) override ; + void mouseUngrabEvent() override ; + void touchUngrabEvent() override; + void touchEvent(QTouchEvent *event) override ; +#if QT_CONFIG(wheelevent) + void wheelEvent(QWheelEvent *event) override ; +#endif + + bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; + bool sendMouseEvent(QMouseEvent *event); + bool sendTouchEvent(QTouchEvent *event); + + void componentComplete() override; + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) override; + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + + void setError(QGeoServiceProvider::Error error, const QString &errorString); + void initialize(); + void setZoomLevel(qreal zoomLevel, bool overzoom); + +private Q_SLOTS: + void mappingManagerInitialized(); + void pluginReady(); + void onSupportedMapTypesChanged(); + void onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities); + void onAttachedCopyrightNoticeVisibilityChanged(); + +private: + void setupMapView(QDeclarativeGeoMapItemView *view); + void populateMap(); + void populateParameters(); + void fitViewportToMapItemsRefine(bool refine, bool onlyVisible); + void fitViewportToGeoShape(); + bool isInteractive(); + void attachCopyrightNotice(bool initialVisibility); + void detachCopyrightNotice(bool currentVisibility); + +private: + QDeclarativeGeoServiceProvider *m_plugin; + QGeoMappingManager *m_mappingManager; + QDeclarativeGeoMapType *m_activeMapType; + QList m_supportedMapTypes; + QList m_mapViews; + QQuickGeoMapGestureArea *m_gestureArea; + QPointer m_map; + QPointer m_copyrights; + QList > m_mapItems; + QList > m_mapItemGroups; + QString m_errorString; + QGeoServiceProvider::Error m_error; + QGeoRectangle m_visibleRegion; + QColor m_color; + QGeoCameraData m_cameraData; + bool m_componentCompleted; + bool m_pendingFitViewport; + bool m_copyrightsVisible; + double m_maximumViewportLatitude; + bool m_initialized; + QList m_mapParameters; + QList m_pendingMapObjects; // Used only in the initialization phase + QGeoCameraCapabilities m_cameraCapabilities; + qreal m_userMinimumZoomLevel; + qreal m_userMaximumZoomLevel; + + qreal m_minimumTilt; + qreal m_maximumTilt; + qreal m_userMinimumTilt; + qreal m_userMaximumTilt; + + qreal m_minimumFieldOfView; + qreal m_maximumFieldOfView; + qreal m_userMinimumFieldOfView; + qreal m_userMaximumFieldOfView; + + int m_copyNoticesVisible = 0; + qreal m_maxChildZ = 0; + + + friend class QDeclarativeGeoMapItem; + friend class QDeclarativeGeoMapItemView; + friend class QQuickGeoMapGestureArea; + friend class QDeclarativeGeoMapCopyrightNotice; + Q_DISABLE_COPY(QDeclarativeGeoMap) +}; + + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMap) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp new file mode 100644 index 0000000..721d941 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice.cpp @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Aaron McCarthy +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomapcopyrightsnotice_p.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoMapCopyrightNoticePrivate: public QQuickPaintedItemPrivate +{ + Q_DECLARE_PUBLIC(QDeclarativeGeoMapCopyrightNotice) +public: + virtual void setVisible(bool visible); +}; + +/*! + \qmltype MapCopyrightNotice + \instantiates QDeclarativeGeoMapCopyrightNotice + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.9 + + \brief The MapCopyrightNotice item displays the current valid + copyright notice for a Map element. + + This object can be used to place an additional copyright notices + programmatically. + + Note that declaring a MapCopyrightNotice inside a QtLocation::Map element + is not possible, like for any other QQuickItem. + + The release of this API with Qt 5.9 is a Technology Preview. +*/ + +/*! + \qmlproperty Map QtLocation::MapCopyrightNotice::mapSource + + This property holds the current map source providing the copyright data shown + in this notice. + In order to let the MapCopyrightNotice display a copyright, this property must + be set, as it is the only data source for this element. +*/ + +/*! + \qmlproperty string QtLocation::MapCopyrightNotice::styleSheet + + This property holds the current css2.1 style sheet used to style the copyright notice, if in HTML form. + + Example: + \code + MapCopyrightNotice { + mapSource: myMap + styleSheet: "body { color : green; font-family: \"Lucida\"; font-size: 8px} a{ font-size: 8px; color:#A62900}" + } + \endcode +*/ + +QDeclarativeGeoMapCopyrightNotice::QDeclarativeGeoMapCopyrightNotice(QQuickItem *parent) +: QQuickPaintedItem(parent), m_copyrightsHtml(0), m_copyrightsVisible(true), m_mapSource(0), + m_userDefinedStyleSheet(false) +{ + // If this item is constructed inside the map, automatically anchor it where it always used to be. + if (qobject_cast(parent)) + anchorToBottomLeft(); +} + +QDeclarativeGeoMapCopyrightNotice::~QDeclarativeGeoMapCopyrightNotice() +{ + setMapSource(nullptr); +} + +void QDeclarativeGeoMapCopyrightNotice::anchorToBottomLeft() +{ + if (!parent()) + return; + QQuickAnchors *anchors = property("anchors").value(); + if (anchors) { + anchors->setLeft(QQuickAnchorLine(qobject_cast(parent()), QQuickAnchors::LeftAnchor)); + anchors->setBottom(QQuickAnchorLine(qobject_cast(parent()), QQuickAnchors::BottomAnchor)); + } +} + +void QDeclarativeGeoMapCopyrightNotice::setMapSource(QDeclarativeGeoMap *map) +{ + if (m_mapSource == map) + return; + + if (m_mapSource) { + // disconnect this object from current map source + m_mapSource->detachCopyrightNotice(copyrightsVisible()); + m_mapSource->disconnect(this); + m_mapSource->m_map->disconnect(this); + if (m_copyrightsHtml) + m_copyrightsHtml->clear(); + m_copyrightsImage = QImage(); + m_mapSource = nullptr; + } + + if (map) { + m_mapSource = map; + m_mapSource->attachCopyrightNotice(copyrightsVisible()); + connect(this, &QDeclarativeGeoMapCopyrightNotice::copyrightsVisibleChanged, + mapSource(), &QDeclarativeGeoMap::onAttachedCopyrightNoticeVisibilityChanged); + + // First update the copyright. Only Image will do here, no need to store HTML right away. + if (m_mapSource->m_copyrights && !m_mapSource->m_copyrights->m_copyrightsImage.isNull()) + m_copyrightsImage = m_mapSource->m_copyrights->m_copyrightsImage; + + connect(mapSource(), SIGNAL(copyrightsChanged(QImage)), + this, SLOT(copyrightsChanged(QImage))); + connect(mapSource(), SIGNAL(copyrightsChanged(QString)), + this, SLOT(copyrightsChanged(QString))); + + if (m_mapSource->m_map) + connectMap(); + else + connect(mapSource(), &QDeclarativeGeoMap::mapReadyChanged, this, &QDeclarativeGeoMapCopyrightNotice::connectMap); + } +} + +void QDeclarativeGeoMapCopyrightNotice::connectMap() +{ + connect(m_mapSource->m_map, SIGNAL(copyrightsStyleSheetChanged(QString)), + this, SLOT(onCopyrightsStyleSheetChanged(QString))); + connect(this, SIGNAL(linkActivated(QString)), + mapSource(), SIGNAL(copyrightLinkActivated(QString))); + + onCopyrightsStyleSheetChanged(m_mapSource->m_map->copyrightsStyleSheet()); + + update(); + emit mapSourceChanged(); +} + +QDeclarativeGeoMap *QDeclarativeGeoMapCopyrightNotice::mapSource() +{ + return m_mapSource.data(); +} + +QString QDeclarativeGeoMapCopyrightNotice::styleSheet() const +{ + return m_styleSheet; +} + +void QDeclarativeGeoMapCopyrightNotice::setStyleSheet(const QString &styleSheet) +{ + m_userDefinedStyleSheet = true; + + if (styleSheet == m_styleSheet) + return; + + m_styleSheet = styleSheet; + if (!m_html.isEmpty() && m_copyrightsHtml) { + delete m_copyrightsHtml; + createCopyright(); +#if QT_CONFIG(texthtmlparser) + m_copyrightsHtml->setHtml(m_html); +#else + m_copyrightsHtml->setPlainText(m_html); +#endif + } + rasterizeHtmlAndUpdate(); + emit styleSheetChanged(m_styleSheet); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::paint(QPainter *painter) +{ + painter->drawImage(0, 0, m_copyrightsImage); +} + +void QDeclarativeGeoMapCopyrightNotice::mousePressEvent(QMouseEvent *event) +{ + if (m_copyrightsHtml) { + m_activeAnchor = m_copyrightsHtml->documentLayout()->anchorAt(event->pos()); + if (!m_activeAnchor.isEmpty()) + return; + } + + QQuickPaintedItem::mousePressEvent(event); +} + +void QDeclarativeGeoMapCopyrightNotice::mouseReleaseEvent(QMouseEvent *event) +{ + if (m_copyrightsHtml) { + QString anchor = m_copyrightsHtml->documentLayout()->anchorAt(event->pos()); + if (anchor == m_activeAnchor && !anchor.isEmpty()) { + emit linkActivated(anchor); + m_activeAnchor.clear(); + } + } +} + +void QDeclarativeGeoMapCopyrightNotice::rasterizeHtmlAndUpdate() +{ + if (!m_copyrightsHtml || m_copyrightsHtml->isEmpty()) + return; + + m_copyrightsImage = QImage(m_copyrightsHtml->size().toSize(), + QImage::Format_ARGB32_Premultiplied); + + m_copyrightsImage.fill(qPremultiply(QColor(Qt::transparent).rgba())); + QPainter painter(&m_copyrightsImage); + QAbstractTextDocumentLayout::PaintContext ctx; + ctx.palette.setColor(QPalette::Text, QStringLiteral("black")); + m_copyrightsHtml->documentLayout()->draw(&painter, ctx); + + setImplicitSize(m_copyrightsImage.width(), m_copyrightsImage.height()); + setContentsSize(m_copyrightsImage.size()); + + setKeepMouseGrab(true); + setAcceptedMouseButtons(Qt::LeftButton); + + update(); +} + +void QDeclarativeGeoMapCopyrightNotice::createCopyright() +{ + m_copyrightsHtml = new QTextDocument(this); +#if QT_CONFIG(cssparser) + if (!m_styleSheet.isEmpty()) + m_copyrightsHtml->setDefaultStyleSheet(m_styleSheet); +#endif + + // The default 4 makes the copyright too wide and tall. + m_copyrightsHtml->setDocumentMargin(0); +} + +void QDeclarativeGeoMapCopyrightNoticePrivate::setVisible(bool visible) +{ + Q_Q(QDeclarativeGeoMapCopyrightNotice); + q->m_copyrightsVisible = visible; + QQuickItemPrivate::setVisible(visible); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::setCopyrightsVisible(bool visible) +{ + Q_D(QDeclarativeGeoMapCopyrightNotice); + if (visible == m_copyrightsVisible) + return; + + m_copyrightsVisible = visible; + d->QQuickItemPrivate::setVisible(!m_copyrightsImage.isNull() && visible); + emit copyrightsVisibleChanged(); +} + +bool QDeclarativeGeoMapCopyrightNotice::copyrightsVisible() const +{ + return m_copyrightsVisible; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::setCopyrightsZ(qreal copyrightsZ) +{ + setZ(copyrightsZ); + update(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QImage ©rightsImage) +{ + Q_D(QDeclarativeGeoMapCopyrightNotice); + delete m_copyrightsHtml; + m_copyrightsHtml = 0; + + m_copyrightsImage = copyrightsImage; + + setImplicitSize(m_copyrightsImage.width(), m_copyrightsImage.height()); + + setKeepMouseGrab(false); + setAcceptedMouseButtons(Qt::NoButton); + d->QQuickItemPrivate::setVisible(m_copyrightsVisible && !m_copyrightsImage.isNull()); + + update(); +} + +void QDeclarativeGeoMapCopyrightNotice::copyrightsChanged(const QString ©rightsHtml) +{ + Q_D(QDeclarativeGeoMapCopyrightNotice); + if (copyrightsHtml.isEmpty()) { + d->QQuickItemPrivate::setVisible(false); + return; + } else { + d->QQuickItemPrivate::setVisible(m_copyrightsVisible); + } + + // Divfy, so we can style the background. The extra is a + // workaround to QTBUG-58838 and should be removed when it gets fixed. +#if QT_CONFIG(texthtmlparser) + m_html = QStringLiteral(""); +#else + m_html = copyrightsHtml; +#endif + + if (!m_copyrightsHtml) + createCopyright(); + +#if QT_CONFIG(texthtmlparser) + m_copyrightsHtml->setHtml(m_html); +#else + m_copyrightsHtml->setPlainText(m_html); +#endif + rasterizeHtmlAndUpdate(); +} + +void QDeclarativeGeoMapCopyrightNotice::onCopyrightsStyleSheetChanged(const QString &styleSheet) +{ + if (m_userDefinedStyleSheet || styleSheet == m_styleSheet) + return; + + m_styleSheet = styleSheet; + if (!m_html.isEmpty() && m_copyrightsHtml) { + delete m_copyrightsHtml; + createCopyright(); +#if QT_CONFIG(texthtmlparser) + m_copyrightsHtml->setHtml(m_html); +#else + m_copyrightsHtml->setPlainText(m_html); +#endif + } + rasterizeHtmlAndUpdate(); + emit styleSheetChanged(m_styleSheet); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h new file mode 100644 index 0000000..0cf06d1 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapcopyrightsnotice_p.h @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Aaron McCarthy +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPCOPYRIGHTSNOTICE_H +#define QDECLARATIVEGEOMAPCOPYRIGHTSNOTICE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QTextDocument; +class QDeclarativeGeoMap; +class QDeclarativeGeoMapCopyrightNoticePrivate; +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapCopyrightNotice : public QQuickPaintedItem +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeGeoMap *mapSource READ mapSource WRITE setMapSource NOTIFY mapSourceChanged) + Q_PROPERTY(QString styleSheet READ styleSheet WRITE setStyleSheet NOTIFY styleSheetChanged) + +public: + QDeclarativeGeoMapCopyrightNotice(QQuickItem *parent = nullptr); + ~QDeclarativeGeoMapCopyrightNotice(); + + void setCopyrightsZ(qreal copyrightsZ); + + void setCopyrightsVisible(bool visible); + bool copyrightsVisible() const; + void anchorToBottomLeft(); + + void setMapSource(QDeclarativeGeoMap *mapSource); + QDeclarativeGeoMap *mapSource(); + + QString styleSheet() const; + void setStyleSheet(const QString &styleSheet); + +public Q_SLOTS: + void copyrightsChanged(const QImage ©rightsImage); + void copyrightsChanged(const QString ©rightsHtml); + void onCopyrightsStyleSheetChanged(const QString &styleSheet); + +signals: + void linkActivated(const QString &link); + void mapSourceChanged(); + void backgroundColorChanged(const QColor &color); + void styleSheetChanged(const QString &styleSheet); + void copyrightsVisibleChanged(); + +protected: + void paint(QPainter *painter) override; + void mousePressEvent(QMouseEvent *event) override; + void mouseReleaseEvent(QMouseEvent *event) override; + void rasterizeHtmlAndUpdate(); + void connectMap(); + +private: + void createCopyright(); + + QTextDocument *m_copyrightsHtml; + QString m_html; + QImage m_copyrightsImage; + QString m_activeAnchor; + bool m_copyrightsVisible; + QPointer m_mapSource; + QColor m_backgroundColor; + QString m_styleSheet; + bool m_userDefinedStyleSheet; + + Q_DISABLE_COPY(QDeclarativeGeoMapCopyrightNotice) + Q_DECLARE_PRIVATE(QDeclarativeGeoMapCopyrightNotice) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase.cpp b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp new file mode 100644 index 0000000..faa06fe --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitembase.cpp @@ -0,0 +1,374 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomapitembase_p.h" +#include "qgeocameradata_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent() + : zoomLevelChanged(false), + centerChanged(false), + mapSizeChanged(false), + tiltChanged(false), + bearingChanged(false), + rollChanged(false) +{ +} + +QGeoMapViewportChangeEvent::QGeoMapViewportChangeEvent(const QGeoMapViewportChangeEvent &other) +{ + this->operator=(other); +} + +QGeoMapViewportChangeEvent &QGeoMapViewportChangeEvent::operator=(const QGeoMapViewportChangeEvent &other) +{ + if (this == &other) + return (*this); + + cameraData = other.cameraData; + mapSize = other.mapSize; + zoomLevelChanged = other.zoomLevelChanged; + centerChanged = other.centerChanged; + mapSizeChanged = other.mapSizeChanged; + tiltChanged = other.tiltChanged; + bearingChanged = other.bearingChanged; + rollChanged = other.rollChanged; + + return (*this); +} + +QDeclarativeGeoMapItemBase::QDeclarativeGeoMapItemBase(QQuickItem *parent) +: QQuickItem(parent), map_(0), quickMap_(0), parentGroup_(0) +{ + setFiltersChildMouseEvents(true); + connect(this, SIGNAL(childrenChanged()), + this, SLOT(afterChildrenChanged())); + // Changing opacity on a mapItemGroup should affect also the opacity on the children. + // This must be notified to plugins, if they are to render the item. + connect(this, &QQuickItem::opacityChanged, this, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged); + parentGroup_ = qobject_cast(parent); + if (parentGroup_) + connect(qobject_cast(parent), &QQuickItem::opacityChanged, + this, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged); +} + +QDeclarativeGeoMapItemBase::~QDeclarativeGeoMapItemBase() +{ + disconnect(this, SLOT(afterChildrenChanged())); + if (quickMap_) + quickMap_->removeMapItem(this); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::afterChildrenChanged() +{ + QList kids = childItems(); + if (kids.size() > 0) { + bool printedWarning = false; + foreach (QQuickItem *i, kids) { + if (i->flags() & QQuickItem::ItemHasContents + && !qobject_cast(i)) { + if (!printedWarning) { + qmlWarning(this) << "Geographic map items do not support child items"; + printedWarning = true; + } + + qmlWarning(i) << "deleting this child"; + i->deleteLater(); + } + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + if (quickMap == quickMap_) + return; + if (quickMap && quickMap_) + return; // don't allow association to more than one map + if (quickMap_) + quickMap_->disconnect(this); + if (map_) + map_->disconnect(this); + + quickMap_ = quickMap; + map_ = map; + + if (map_ && quickMap_) { + connect(map_, SIGNAL(cameraDataChanged(QGeoCameraData)), + this, SLOT(baseCameraDataChanged(QGeoCameraData))); + connect(quickMap, SIGNAL(heightChanged()), this, SLOT(polishAndUpdate())); + connect(quickMap, SIGNAL(widthChanged()), this, SLOT(polishAndUpdate())); + lastSize_ = QSizeF(quickMap_->width(), quickMap_->height()); + lastCameraData_ = map_->cameraData(); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::baseCameraDataChanged(const QGeoCameraData &cameraData) +{ + QGeoMapViewportChangeEvent evt; + evt.cameraData = cameraData; + evt.mapSize = QSizeF(quickMap_->width(), quickMap_->height()); + + if (evt.mapSize != lastSize_) + evt.mapSizeChanged = true; + + if (cameraData.bearing() != lastCameraData_.bearing()) + evt.bearingChanged = true; + if (cameraData.center() != lastCameraData_.center()) + evt.centerChanged = true; + if (cameraData.roll() != lastCameraData_.roll()) + evt.rollChanged = true; + if (cameraData.tilt() != lastCameraData_.tilt()) + evt.tiltChanged = true; + if (cameraData.zoomLevel() != lastCameraData_.zoomLevel()) + evt.zoomLevelChanged = true; + + lastSize_ = evt.mapSize; + lastCameraData_ = cameraData; + + afterViewportChanged(evt); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemBase::setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset) +{ + if (!map_ || !quickMap_) + return; + + QDoubleVector2D pos; + if (map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + QDoubleVector2D wrappedProjection = p.geoToWrappedMapProjection(coordinate); + if (!p.isProjectable(wrappedProjection)) + return; + pos = p.wrappedMapProjectionToItemPosition(wrappedProjection); + } else { + pos = map()->geoProjection().coordinateToItemPosition(coordinate, false); + if (qIsNaN(pos.x())) + return; + } + + QPointF topLeft = pos.toPointF() - offset; + + setPosition(topLeft); +} + +static const double opacityRampMin = 1.5; +static const double opacityRampMax = 2.5; +/*! + \internal +*/ +float QDeclarativeGeoMapItemBase::zoomLevelOpacity() const +{ + if (quickMap_->zoomLevel() > opacityRampMax) + return 1.0; + else if (quickMap_->zoomLevel() > opacityRampMin) + return quickMap_->zoomLevel() - opacityRampMin; + else + return 0.0; +} + +bool QDeclarativeGeoMapItemBase::childMouseEventFilter(QQuickItem *item, QEvent *event) +{ + Q_UNUSED(item) + if (event->type() == QEvent::MouseButtonPress && !contains(static_cast(event)->pos())) { + // In case of items that are not rectangles, this filter is used to test if the event has landed + // inside the actual item shape. + // If so, the method returns true, meaning that it prevents the event delivery to child "*item" (for example, + // a mouse area that is on top of this map item). + // However, this method sets "accepted" to false, so that the event can still be passed further up, + // specifically to the parent Map, that is a sort of flickable. + // Otherwise, if the event is not contained within the map item, the method returns false, meaning the event + // is delivered to the child *item (like the mouse area associated). + event->setAccepted(false); + return true; + } + return false; +} + +/*! + \internal +*/ +QSGNode *QDeclarativeGeoMapItemBase::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *pd) +{ + if (!map_ || !quickMap_ || map_->supportedMapItemTypes() & itemType()) { + if (oldNode) + delete oldNode; + oldNode = 0; + return 0; + } + + QSGOpacityNode *opn = static_cast(oldNode); + if (!opn) + opn = new QSGOpacityNode(); + + opn->setOpacity(zoomLevelOpacity()); + + QSGNode *oldN = opn->childCount() ? opn->firstChild() : 0; + opn->removeAllChildNodes(); + if (opn->opacity() > 0.0) { + QSGNode *n = this->updateMapItemPaintNode(oldN, pd); + if (n) + opn->appendChildNode(n); + } else { + delete oldN; + } + + return opn; +} + +/*! + \internal +*/ +QSGNode *QDeclarativeGeoMapItemBase::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + delete oldNode; + return 0; +} + +qreal QDeclarativeGeoMapItemBase::mapItemOpacity() const +{ + if (parentGroup_) + return parentGroup_->opacity() * opacity(); + return opacity(); +} + +bool QDeclarativeGeoMapItemBase::prepareEnterTransition() +{ + if (m_transitionManager->m_transitionState == QDeclarativeGeoMapItemTransitionManager::EnterTransition + && m_transitionManager->isRunning()) + return false; + + if (m_transitionManager->m_transitionState != QDeclarativeGeoMapItemTransitionManager::EnterTransition) { + setVisible(true); + m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::EnterTransition; + } + return true; +} + +bool QDeclarativeGeoMapItemBase::prepareExitTransition() +{ + if (m_transitionManager->m_transitionState == QDeclarativeGeoMapItemTransitionManager::ExitTransition + && m_transitionManager->isRunning()) + return false; + + if (m_transitionManager->m_transitionState != QDeclarativeGeoMapItemTransitionManager::ExitTransition) { + m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::ExitTransition; + } + return true; +} + +void QDeclarativeGeoMapItemBase::finalizeEnterTransition() +{ + m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::NoTransition; + emit enterTransitionFinished(); +} + +void QDeclarativeGeoMapItemBase::finalizeExitTransition() +{ + setVisible(false); + m_transitionManager->m_transitionState = QDeclarativeGeoMapItemTransitionManager::NoTransition; + emit exitTransitionFinished(); +} + +bool QDeclarativeGeoMapItemBase::isPolishScheduled() const +{ + return QQuickItemPrivate::get(this)->polishScheduled; +} + +void QDeclarativeGeoMapItemBase::polishAndUpdate() +{ + polish(); + update(); +} + +QDeclarativeGeoMapItemTransitionManager::QDeclarativeGeoMapItemTransitionManager(QDeclarativeGeoMapItemBase *mapItem) + : QQuickTransitionManager(), m_mapItem(mapItem) +{ +} + +void QDeclarativeGeoMapItemTransitionManager::transitionEnter() +{ + if (m_transitionState == ExitTransition) + cancel(); + + if (!m_mapItem->prepareEnterTransition()) + return; + + if (m_view && m_view->m_enter) + transition(enterActions, m_view->m_enter, m_mapItem); + else + finished(); +} + +void QDeclarativeGeoMapItemTransitionManager::transitionExit() +{ + if (!m_mapItem->prepareExitTransition()) + return; + + if (m_view && m_view->m_exit) + transition(exitActions, m_view->m_exit, m_mapItem); + else + finished(); +} + +void QDeclarativeGeoMapItemTransitionManager::finished() +{ + if (m_transitionState == EnterTransition) + m_mapItem->finalizeEnterTransition(); + else if (m_transitionState == ExitTransition) + m_mapItem->finalizeExitTransition(); +} + + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapitembase_p.h b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h new file mode 100644 index 0000000..7a284e8 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitembase_p.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPITEMBASE_H +#define QDECLARATIVEGEOMAPITEMBASE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QDeclarativeGeoMapItemTransitionManager; +class QDeclarativeGeoMapItemBase; +class Q_LOCATION_PRIVATE_EXPORT QGeoMapViewportChangeEvent +{ +public: + explicit QGeoMapViewportChangeEvent(); + QGeoMapViewportChangeEvent(const QGeoMapViewportChangeEvent &other); + QGeoMapViewportChangeEvent &operator=(const QGeoMapViewportChangeEvent &other); + + QGeoCameraData cameraData; + QSizeF mapSize; + + bool zoomLevelChanged; + bool centerChanged; + bool mapSizeChanged; + bool tiltChanged; + bool bearingChanged; + bool rollChanged; +}; + +class QDeclarativeGeoMapItemTransitionManager : public QQuickTransitionManager +{ +public: + enum TransitionState { + NoTransition, EnterTransition, ExitTransition + }; + + QDeclarativeGeoMapItemTransitionManager(QDeclarativeGeoMapItemBase *mapItem); + + void transitionEnter(); + void transitionExit(); + +protected: + void finished() override; + +public: + QDeclarativeGeoMapItemBase *m_mapItem; + QDeclarativeGeoMapItemView *m_view = nullptr; + QList enterActions; + QList exitActions; + TransitionState m_transitionState = NoTransition; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemBase : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(QGeoShape geoShape READ geoShape STORED false ) +public: + explicit QDeclarativeGeoMapItemBase(QQuickItem *parent = 0); + virtual ~QDeclarativeGeoMapItemBase(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map); + virtual void setPositionOnMap(const QGeoCoordinate &coordinate, const QPointF &offset); + + QDeclarativeGeoMap *quickMap() { return quickMap_; } + QGeoMap *map() { return map_; } + virtual const QGeoShape &geoShape() const = 0; + + QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *); + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *); + + virtual QGeoMap::ItemType itemType() const = 0; + qreal mapItemOpacity() const; + + virtual bool prepareEnterTransition(); + virtual bool prepareExitTransition(); + virtual void finalizeEnterTransition(); + virtual void finalizeExitTransition(); + +Q_SIGNALS: + void mapItemOpacityChanged(); + Q_REVISION(11) void enterTransitionFinished(); + Q_REVISION(11) void exitTransitionFinished(); + +protected Q_SLOTS: + virtual void afterChildrenChanged(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) = 0; + void polishAndUpdate(); + +protected: + float zoomLevelOpacity() const; + bool childMouseEventFilter(QQuickItem *item, QEvent *event); + bool isPolishScheduled() const; + +private Q_SLOTS: + void baseCameraDataChanged(const QGeoCameraData &camera); + +private: + QPointer map_; + QDeclarativeGeoMap *quickMap_; + + QSizeF lastSize_; + QGeoCameraData lastCameraData_; + + QDeclarativeGeoMapItemGroup *parentGroup_; + + QScopedPointer m_transitionManager; + + friend class QDeclarativeGeoMap; + friend class QDeclarativeGeoMapItemView; + friend class QDeclarativeGeoMapItemTransitionManager; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapitemgroup.cpp b/src/location/declarativemaps/qdeclarativegeomapitemgroup.cpp new file mode 100644 index 0000000..b4d214a --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemgroup.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomapitemgroup_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapItemGroup + \instantiates QDeclarativeGeoMapItemGroup + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.9 + + \brief The MapItemGroup type is a container for map items. + + Its purpose is to enable code modularization by allowing the usage + of qml files containing map elements related to each other, and + the associated bindings. + + \note The release of this API with Qt 5.9 is a Technology Preview. + + \section2 Example Usage + + The following snippet shows how to use a MapItemGroup to create a MapCircle, centered at + the coordinate (63, -18) with a radius of 100km, filled in red, surrounded by an ondulated green border, + both contained in a semitransparent blue circle with a MouseArea that moves the whole group. + This group is defined in a separate file named PolygonGroup.qml: + + \code + import QtQuick 2.4 + import QtPositioning 5.6 + import QtLocation 5.9 + + MapItemGroup { + id: itemGroup + property alias position: mainCircle.center + property var radius: 100 * 1000 + property var borderHeightPct : 0.3 + + MapCircle { + id: mainCircle + center : QtPositioning.coordinate(40, 0) + radius: itemGroup.radius * (1.0 + borderHeightPct) + opacity: 0.05 + visible: true + color: 'blue' + + MouseArea{ + anchors.fill: parent + drag.target: parent + id: maItemGroup + } + } + + MapCircle { + id: groupCircle + center: itemGroup.position + radius: itemGroup.radius + color: 'crimson' + + onCenterChanged: { + groupPolyline.populateBorder(); + } + } + + MapPolyline { + id: groupPolyline + line.color: 'green' + line.width: 3 + + function populateBorder() { + groupPolyline.path = [] // clearing the path + var waveLength = 8.0; + var waveAmplitude = groupCircle.radius * borderHeightPct; + for (var i=0; i <= 360; i++) { + var wavePhase = (i/360.0 * 2.0 * Math.PI )* waveLength + var waveHeight = (Math.cos(wavePhase) + 1.0) / 2.0 + groupPolyline.addCoordinate(groupCircle.center.atDistanceAndAzimuth(groupCircle.radius + waveAmplitude * waveHeight , i)) + } + } + + Component.onCompleted: { + populateBorder() + } + } + } + \endcode + + PolygonGroup.qml is now a reusable component that can then be used in a Map as: + + \code + Map { + id: map + PolygonGroup { + id: polygonGroup + position: QtPositioning.coordinate(63,-18) + } + } + \endcode + + \image api-mapitemgroup.png +*/ + +QDeclarativeGeoMapItemGroup::QDeclarativeGeoMapItemGroup(QQuickItem *parent): QQuickItem(parent), m_quickMap(nullptr) +{ + +} + +QDeclarativeGeoMapItemGroup::~QDeclarativeGeoMapItemGroup() +{ + +} + +void QDeclarativeGeoMapItemGroup::setQuickMap(QDeclarativeGeoMap *quickMap) +{ + m_quickMap = quickMap; +} + +QDeclarativeGeoMap *QDeclarativeGeoMapItemGroup::quickMap() const +{ + return m_quickMap; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapitemgroup_p.h b/src/location/declarativemaps/qdeclarativegeomapitemgroup_p.h new file mode 100644 index 0000000..f91d291 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemgroup_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPITEMGROUP_P_H +#define QDECLARATIVEGEOMAPITEMGROUP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoMap; +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemGroup : public QQuickItem +{ + Q_OBJECT +public: + explicit QDeclarativeGeoMapItemGroup(QQuickItem *parent = 0); + virtual ~QDeclarativeGeoMapItemGroup(); + + void setQuickMap(QDeclarativeGeoMap *quickMap); + QDeclarativeGeoMap *quickMap() const; +private: + QDeclarativeGeoMap *m_quickMap; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapItemGroup) + +#endif // QDECLARATIVEGEOMAPITEMGROUP_P_H diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview.cpp b/src/location/declarativemaps/qdeclarativegeomapitemview.cpp new file mode 100644 index 0000000..2140213 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemview.cpp @@ -0,0 +1,386 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomapitemview_p.h" +#include "qdeclarativegeomap_p.h" +#include "qdeclarativegeomapitembase_p.h" +#include "mapitemviewdelegateincubator_p.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapItemView + \instantiates QDeclarativeGeoMapItemView + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.5 + \inherits QObject + + \brief The MapItemView is used to populate Map from a model. + + The MapItemView is used to populate Map with MapItems from a model. + The MapItemView type only makes sense when contained in a Map, + meaning that it has no standalone presentation. + + \section2 Example Usage + + This example demonstrates how to use the MapViewItem object to display + a \l{Route}{route} on a \l{Map}{map}: + + \snippet declarative/maps.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/maps.qml MapRoute +*/ + +QDeclarativeGeoMapItemView::QDeclarativeGeoMapItemView(QQuickItem *parent) + : QObject(parent), m_componentCompleted(false), m_delegate(0), + m_map(0), m_fitViewport(false), m_delegateModel(0) +{ + m_exit = new QQuickTransition(this); + QQmlListProperty anims = m_exit->animations(); + QQuickNumberAnimation *ani = new QQuickNumberAnimation(m_exit); + ani->setProperty(QStringLiteral("opacity")); + ani->setTo(0.0); + ani->setDuration(300.0); + anims.append(&anims, ani); +} + +QDeclarativeGeoMapItemView::~QDeclarativeGeoMapItemView() +{ + removeInstantiatedItems(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::componentComplete() +{ + m_componentCompleted = true; + if (!m_itemModel.isNull()) + m_delegateModel->setModel(m_itemModel); + + if (m_delegate) + m_delegateModel->setDelegate(m_delegate); + + m_delegateModel->componentComplete(); +} + +void QDeclarativeGeoMapItemView::classBegin() +{ + QQmlContext *ctx = qmlContext(this); + m_delegateModel = new QQmlDelegateModel(ctx, this); + m_delegateModel->classBegin(); + + connect(m_delegateModel, &QQmlInstanceModel::modelUpdated, this, &QDeclarativeGeoMapItemView::modelUpdated); + connect(m_delegateModel, &QQmlInstanceModel::createdItem, this, &QDeclarativeGeoMapItemView::createdItem); + connect(m_delegateModel, &QQmlInstanceModel::destroyingItem, this, &QDeclarativeGeoMapItemView::destroyingItem); + connect(m_delegateModel, &QQmlInstanceModel::initItem, this, &QDeclarativeGeoMapItemView::initItem); +} + +void QDeclarativeGeoMapItemView::destroyingItem(QObject */*object*/) +{ + +} + +void QDeclarativeGeoMapItemView::initItem(int /*index*/, QObject */*object*/) +{ + +} + +void QDeclarativeGeoMapItemView::createdItem(int index, QObject */*object*/) +{ + if (!m_map) + return; + // createdItem is emitted on asynchronous creation. In which case, object has to be invoked again. + // See QQmlDelegateModel::object for further info. + QDeclarativeGeoMapItemBase *item = + qobject_cast(m_delegateModel->object(index, QQmlIncubator::Asynchronous)); + if (item) + addItemToMap(item, index); + else + qWarning() << "createdItem for " << index << " produced a null item"; +} + +void QDeclarativeGeoMapItemView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + // move changes are expressed as one remove + one insert, with the same moveId. + // For simplicity, they will be treated as remove + insert. + // Changes will be also ignored, as they represent only data changes, not layout changes + if (reset) { // Assuming this means "remove everything already instantiated" + removeInstantiatedItems(); + } else { + // Remove items from the back to the front to retain the mapping to what is received from the changesets + const QVector &removes = changeSet.removes(); + std::map mapRemoves; + for (int i = 0; i < removes.size(); i++) + mapRemoves.insert(std::pair(removes.at(i).start(), i)); + + for (auto rit = mapRemoves.rbegin(); rit != mapRemoves.rend(); ++rit) { + const QQmlChangeSet::Change &c = removes.at(rit->second); + for (int idx = c.end() - 1; idx >= c.start(); --idx) + removeItemFromMap(idx); + } + } + + for (const QQmlChangeSet::Change &c: changeSet.inserts()) { + for (int idx = c.start(); idx < c.end(); idx++) { + QDeclarativeGeoMapItemBase *item = + qobject_cast(m_delegateModel->object(idx, QQmlIncubator::Asynchronous)); + addItemToMap(item, idx); // if not item, a createdItem signal will be emitted. + } + } + + fitViewport(); +} + +/*! + \qmlproperty model QtLocation::MapItemView::model + + This property holds the model that provides data used for creating the map items defined by the + delegate. Only QAbstractItemModel based models are supported. +*/ +QVariant QDeclarativeGeoMapItemView::model() const +{ + return m_itemModel; +} + +void QDeclarativeGeoMapItemView::setModel(const QVariant &model) +{ + if (model == m_itemModel) + return; + + m_itemModel = model; + if (m_componentCompleted) + m_delegateModel->setModel(m_itemModel); + + emit modelChanged(); +} + +/*! + \qmlproperty Component QtLocation::MapItemView::delegate + + This property holds the delegate which defines how each item in the + model should be displayed. The Component must contain exactly one + MapItem -derived object as the root object. +*/ +QQmlComponent *QDeclarativeGeoMapItemView::delegate() const +{ + return m_delegate; +} + +void QDeclarativeGeoMapItemView::setDelegate(QQmlComponent *delegate) +{ + if (m_delegate == delegate) + return; + + m_delegate = delegate; + if (m_componentCompleted) + m_delegateModel->setDelegate(m_delegate); + + emit delegateChanged(); +} + +/*! + \qmlproperty Component QtLocation::MapItemView::autoFitViewport + + This property controls whether to automatically pan and zoom the viewport + to display all map items when items are added or removed. + + Defaults to false. +*/ +bool QDeclarativeGeoMapItemView::autoFitViewport() const +{ + return m_fitViewport; +} + +void QDeclarativeGeoMapItemView::setAutoFitViewport(const bool &fit) +{ + if (fit == m_fitViewport) + return; + m_fitViewport = fit; + fitViewport(); + emit autoFitViewportChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::fitViewport() +{ + + if (!m_map || !m_map->mapReady() || !m_fitViewport) + return; + + if (m_map->mapItems().size() > 0) + m_map->fitViewportToMapItems(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::setMap(QDeclarativeGeoMap *map) +{ + if (!map || m_map) // changing map on the fly not supported + return; + m_map = map; + instantiateAllItems(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapItemView::removeInstantiatedItems() +{ + if (!m_map) + return; + + // Backward as removeItemFromMap modifies m_instantiatedItems + for (int i = m_instantiatedItems.size() -1; i >= 0 ; i--) + removeItemFromMap(i); +} + +/*! + \internal + + Instantiates all items. +*/ +void QDeclarativeGeoMapItemView::instantiateAllItems() +{ + // The assumption is that if m_instantiatedItems isn't empty, instantiated items have been already added + if (!m_componentCompleted || !m_map || !m_delegate || m_itemModel.isNull() || !m_instantiatedItems.isEmpty()) + return; + + // If here, m_delegateModel may contain data, but QQmlInstanceModel::object for each row hasn't been called yet. + for (int i = 0; i < m_delegateModel->count(); i++) { + QDeclarativeGeoMapItemBase *item = + qobject_cast(m_delegateModel->object(i, QQmlIncubator::Asynchronous)); + addItemToMap(item, i); // if not item, createdItem will be emitted. + } + + fitViewport(); +} + +void QDeclarativeGeoMapItemView::removeItemFromMap(int index) +{ + if (index >= 0 && index < m_instantiatedItems.size()) { + QDeclarativeGeoMapItemBase *item = m_instantiatedItems.takeAt(index); + if (!item) { + if (m_incubatingItems.contains(index)) { + // cancel request + m_delegateModel->cancel(index); + m_incubatingItems.remove(index); + } + return; + } + if (m_exit && m_map) { + if (!item->m_transitionManager) { + QScopedPointermanager(new QDeclarativeGeoMapItemTransitionManager(item)); + item->m_transitionManager.swap(manager); + item->m_transitionManager->m_view = this; + } + connect(item, &QDeclarativeGeoMapItemBase::exitTransitionFinished, + this, &QDeclarativeGeoMapItemView::exitTransitionFinished); + item->m_transitionManager->transitionExit(); + } else { + disconnect(item, 0, this, 0); + if (m_map) + m_map->removeMapItem(item); + QQmlInstanceModel::ReleaseFlags releaseStatus = m_delegateModel->release(item); + if (releaseStatus == QQmlInstanceModel::Referenced) + qWarning() << "item "<< index << " still referenced"; + } + } +} + +void QDeclarativeGeoMapItemView::exitTransitionFinished() +{ + QDeclarativeGeoMapItemBase *item = static_cast(sender()); + disconnect(item, 0, this, 0); + if (m_map) + m_map->removeMapItem(item); + QQmlInstanceModel::ReleaseFlags releaseStatus = m_delegateModel->release(item); + if (releaseStatus == QQmlInstanceModel::Referenced) + qWarning() << "item "<quickMap() == m_map) // belonging to another map?? + return; + + if (m_map) { + if (!item) { + m_incubatingItems.insert(index); + m_instantiatedItems.insert(index, nullptr); + return; + } + + insertInstantiatedItem(index, item); + m_map->addMapItem(item); + if (m_enter) { + if (!item->m_transitionManager) { + QScopedPointermanager(new QDeclarativeGeoMapItemTransitionManager(item)); + item->m_transitionManager.swap(manager); + } + item->m_transitionManager->m_view = this; + item->m_transitionManager->transitionEnter(); + } + } +} + +void QDeclarativeGeoMapItemView::insertInstantiatedItem(int index, QDeclarativeGeoMapItemBase *o) +{ + if (m_incubatingItems.contains(index)) { + m_incubatingItems.remove(index); + m_instantiatedItems.replace(index, o); + } else { + m_instantiatedItems.insert(index, o); + } +} + +QT_END_NAMESPACE + + diff --git a/src/location/declarativemaps/qdeclarativegeomapitemview_p.h b/src/location/declarativemaps/qdeclarativegeomapitemview_p.h new file mode 100644 index 0000000..1f107fb --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapitemview_p.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPITEMVIEW_H +#define QDECLARATIVEGEOMAPITEMVIEW_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QAbstractItemModel; +class QQmlComponent; +class QQuickItem; +class QDeclarativeGeoMap; +class QDeclarativeGeoMapItemBase; +class QQmlOpenMetaObject; +class QQmlOpenMetaObjectType; +class MapItemViewDelegateIncubator; +class QDeclarativeGeoMapItemViewItemData; +class QDeclarativeGeoMapItemView; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapItemView : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_INTERFACES(QQmlParserStatus) + + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_PROPERTY(bool autoFitViewport READ autoFitViewport WRITE setAutoFitViewport NOTIFY autoFitViewportChanged) +// Q_PROPERTY(QQuickTransition *enter MEMBER m_enter) +// Q_PROPERTY(QQuickTransition *exit MEMBER m_exit) + +public: + explicit QDeclarativeGeoMapItemView(QQuickItem *parent = 0); + ~QDeclarativeGeoMapItemView(); + + QVariant model() const; + void setModel(const QVariant &); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *); + + bool autoFitViewport() const; + void setAutoFitViewport(const bool &fit); + + void setMap(QDeclarativeGeoMap *); + void removeInstantiatedItems(); + void instantiateAllItems(); + + // From QQmlParserStatus + void componentComplete() override; + void classBegin() override; + +Q_SIGNALS: + void modelChanged(); + void delegateChanged(); + void autoFitViewportChanged(); + +private Q_SLOTS: + void destroyingItem(QObject *object); + void initItem(int index, QObject *object); + void createdItem(int index, QObject *object); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + void exitTransitionFinished(); + +private: + void fitViewport(); + void removeItemFromMap(int index); + void addItemToMap(QDeclarativeGeoMapItemBase *item, int index); + void insertInstantiatedItem(int index, QDeclarativeGeoMapItemBase *o); + + bool m_componentCompleted; + QQmlComponent *m_delegate; + QVariant m_itemModel; + QDeclarativeGeoMap *m_map; + QList m_instantiatedItems; + QSet m_incubatingItems; + bool m_fitViewport; + QQmlDelegateModel *m_delegateModel; + QQuickTransition *m_enter = nullptr; + QQuickTransition *m_exit = nullptr; + + friend class QDeclarativeGeoMap; + friend class QDeclarativeGeoMapItemBase; + friend class QDeclarativeGeoMapItemTransitionManager; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapItemView) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomapparameter.cpp b/src/location/declarativemaps/qdeclarativegeomapparameter.cpp new file mode 100644 index 0000000..c1361d5 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapparameter.cpp @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomapparameter_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapParameter + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.9 + \deprecated + + Use \l DynamicParameter instead. +*/ + +/*! + \qmltype DynamicParameter + \instantiates QDeclarativeGeoMapParameter + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since Qt Location 5.11 + + \brief The DynamicParameter type represents a parameter for a Map element. + This type provides a mean to specify plugin-dependent optional parameters + for a map. + + DynamicParameters by default contain only the \l type property, and + are highly plugin-dependent. + For this reason, additional properties have to be defined inside a + DynamicParameter at declaration time, using the QML syntax "property var foo". + + What properties have to be put inside a particular DynamicParameter type for + a particular plugin can be found in the documentation of the plugin. + Note that DynamicParameters are \b optional. + By not specifying any of them, the Map will have the default behavior. +*/ + +/*! + \qmlproperty string QtLocation::DynamicParameter::type + + Set-once property which holds a string defining the type of the DynamicParameter +*/ + +QDeclarativeGeoMapParameter::QDeclarativeGeoMapParameter(QObject *parent) +: QGeoMapParameter(parent), m_initialPropertyCount(metaObject()->propertyCount()), m_complete(false) +{ + +} + +QDeclarativeGeoMapParameter::~QDeclarativeGeoMapParameter() +{ +} + +bool QDeclarativeGeoMapParameter::isComponentComplete() const +{ + return m_complete; +} + +int QDeclarativeGeoMapParameter::initialPropertyCount() const +{ + return m_initialPropertyCount; +} + +void QDeclarativeGeoMapParameter::classBegin() +{ +} + +void QDeclarativeGeoMapParameter::componentComplete() +{ + for (int i = m_initialPropertyCount; i < metaObject()->propertyCount(); ++i) { + QMetaProperty property = metaObject()->property(i); + + if (!property.hasNotifySignal()) { + return; + } + + QSignalMapper *mapper = new QSignalMapper(this); + mapper->setMapping(this, i); + + const QByteArray signalName = '2' + property.notifySignal().methodSignature(); // TODO: explain why '2' + QObject::connect(this, signalName, mapper, SLOT(map())); + QObject::connect(mapper, SIGNAL(mapped(int)), this, SLOT(onPropertyUpdated(int))); + } + m_complete = true; + emit completed(this); +} + +void QDeclarativeGeoMapParameter::onPropertyUpdated(int index) +{ + emit propertyUpdated(this, metaObject()->property(index).name()); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapparameter_p.h b/src/location/declarativemaps/qdeclarativegeomapparameter_p.h new file mode 100644 index 0000000..0f54e1b --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapparameter_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPPARAMETER_P_H +#define QDECLARATIVEGEOMAPPARAMETER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapParameter : public QGeoMapParameter, public QQmlParserStatus +{ + Q_OBJECT + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeGeoMapParameter(QObject *parent = 0); + virtual ~QDeclarativeGeoMapParameter(); + + bool isComponentComplete() const; + +Q_SIGNALS: + void completed(QDeclarativeGeoMapParameter *); + +protected: + int initialPropertyCount() const; + // QQmlParserStatus implementation + void classBegin() override; + void componentComplete() override; + +private slots: + void onPropertyUpdated(int index); + +private: + int m_initialPropertyCount; + bool m_complete; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapParameter) + +#endif // QDECLARATIVEGEOMAPPARAMETER_P_H diff --git a/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp b/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp new file mode 100644 index 0000000..6261312 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapquickitem.cpp @@ -0,0 +1,458 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomapquickitem_p.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapQuickItem + \instantiates QDeclarativeGeoMapQuickItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.5 + + \brief The MapQuickItem type displays an arbitrary Qt Quick object + on a Map. + + The MapQuickItem type is used to place an arbitrary Qt Quick object + on a Map at a specified location and size. Compared to floating an item + above the Map, a MapQuickItem will follow the panning (and optionally, the + zooming) of the Map as if it is on the Map surface. + + The \l{sourceItem} property contains the Qt Quick item to be drawn, which + can be any kind of visible type. + + \section2 Positioning and Sizing + + The positioning of the MapQuickItem on the Map is controlled by two + properties: \l coordinate and \l anchorPoint. If only \l coordinate is set, + it specifies a longitude/latitude coordinate for the item to be placed at. + The set coordinate will line up with the top-left corner of the contained + item when shown on the screen. + + The \l anchorPoint property provides a way to line up the coordinate with + other parts of the item than just the top-left corner, by setting a number + of pixels the item will be offset by. A simple way to think about it is + to note that the point given by \l anchorPoint on the item itself is the + point that will line up with the given \l coordinate when displayed. + + In addition to being anchored to the map, the MapQuickItem can optionally + follow the scale of the map, and change size when the Map is zoomed in or + zoomed out. This behaviour is controlled by the \l zoomLevel property. The + default behaviour if \l zoomLevel is not set is for the item to be drawn + "on the screen" rather than "on the map", so that its size remains the same + regardless of the zoom level of the Map. + + \section2 Performance + + Performance of a MapQuickItem is normally in the same ballpark as the + contained Qt Quick item alone. Overheads added amount to a translation + and (possibly) scaling of the original item, as well as a transformation + from longitude and latitude to screen position. + + \section2 Limitations + + \note Due to an implementation detail, items placed inside a + MapQuickItem will have a \c{parent} item which is not the MapQuickItem. + Refer to the MapQuickItem by its \c{id}, and avoid the use of \c{anchor} + in the \c{sourceItem}. + + \section2 Example Usage + + The following snippet shows a MapQuickItem containing an Image object, + to display a Marker on the Map. This strategy is used to show the map + markers in the MapViewer example. + + \snippet mapviewer/map/Marker.qml mqi-top + \snippet mapviewer/map/Marker.qml mqi-anchor + \snippet mapviewer/map/Marker.qml mqi-closeimage + \snippet mapviewer/map/Marker.qml mqi-close + + \image api-mapquickitem.png +*/ + +QMapQuickItemMatrix4x4::QMapQuickItemMatrix4x4(QObject *parent) : QQuickTransform(parent) { } + +void QMapQuickItemMatrix4x4::setMatrix(const QMatrix4x4 &matrix) +{ + if (m_matrix == matrix) + return; + m_matrix = matrix; + update(); +} + +void QMapQuickItemMatrix4x4::applyTo(QMatrix4x4 *matrix) const +{ + *matrix *= m_matrix; +} + + +QDeclarativeGeoMapQuickItem::QDeclarativeGeoMapQuickItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), zoomLevel_(0.0), + mapAndSourceItemSet_(false), updatingGeometry_(false), matrix_(nullptr) +{ + setFlag(ItemHasContents, true); + opacityContainer_ = new QQuickItem(this); + opacityContainer_->setParentItem(this); + opacityContainer_->setFlag(ItemHasContents, true); + setFiltersChildMouseEvents(true); +} + +QDeclarativeGeoMapQuickItem::~QDeclarativeGeoMapQuickItem() {} + +/*! + \qmlproperty coordinate MapQuickItem::coordinate + + This property holds the anchor coordinate of the MapQuickItem. The point + on the sourceItem that is specified by anchorPoint is kept aligned with + this coordinate when drawn on the map. + + In the image below, there are 3 MapQuickItems that are identical except + for the value of their anchorPoint properties. The values of anchorPoint + for each are written on top of the item. + + \image api-mapquickitem-anchor.png +*/ +void QDeclarativeGeoMapQuickItem::setCoordinate(const QGeoCoordinate &coordinate) +{ + if (coordinate_ == coordinate) + return; + + coordinate_ = coordinate; + geoshape_.setTopLeft(coordinate_); + geoshape_.setBottomRight(coordinate_); + // TODO: Handle zoomLevel != 0.0 + polishAndUpdate(); + emit coordinateChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map && quickMap) { + connect(map, SIGNAL(cameraDataChanged(QGeoCameraData)), + this, SLOT(polishAndUpdate())); + polishAndUpdate(); + } +} +// See QQuickMultiPointTouchArea::childMouseEventFilter for reference +bool QDeclarativeGeoMapQuickItem::childMouseEventFilter(QQuickItem *receiver, QEvent *event) +{ + if (isEnabled() && isVisible()) { + switch (event->type()) { + case QEvent::MouseButtonPress: + case QEvent::TouchBegin: + dragStartCoordinate_ = coordinate_; + default: + break; + } + } + return QQuickItem::childMouseEventFilter(receiver, event); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (!mapAndSourceItemSet_ || updatingGeometry_ || + newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + + QGeoCoordinate newCoordinate; + // with zoomLevel set the anchorPoint has to be factored into the transformation to properly transform around it. + if (zoomLevel_ != 0.0 + && map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + + // When dragStartCoordinate_ can't be projected to screen, dragging must be disabled. + if (!p.isProjectable(p.geoToWrappedMapProjection(dragStartCoordinate_))) + return; + + QDoubleVector2D pos = map()->geoProjection().coordinateToItemPosition(dragStartCoordinate_, false); + // oldGeometry.topLeft() is always intended to be (0,0), even when for some reason it's not. + pos.setX(pos.x() + newGeometry.topLeft().x()); + pos.setY(pos.y() + newGeometry.topLeft().y()); + newCoordinate = map()->geoProjection().itemPositionToCoordinate(pos, false); + } else { + newCoordinate = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(x(), y()) + QDoubleVector2D(anchorPoint_), false); + } + + if (newCoordinate.isValid()) + setCoordinate(newCoordinate); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +/*! + \internal +*/ +QGeoCoordinate QDeclarativeGeoMapQuickItem::coordinate() +{ + return coordinate_; +} + +/*! + \qmlproperty object MapQuickItem::sourceItem + + This property holds the source item that will be drawn on the map. +*/ +void QDeclarativeGeoMapQuickItem::setSourceItem(QQuickItem *sourceItem) +{ + if (sourceItem_.data() == sourceItem) + return; + sourceItem_ = sourceItem; + polishAndUpdate(); + emit sourceItemChanged(); +} + +QQuickItem *QDeclarativeGeoMapQuickItem::sourceItem() +{ + return sourceItem_.data(); +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::afterChildrenChanged() +{ + QList kids = childItems(); + if (kids.size() > 0) { + bool printedWarning = false; + foreach (QQuickItem *i, kids) { + if (i->flags() & QQuickItem::ItemHasContents + && !qobject_cast(i) + && sourceItem_.data() != i + && opacityContainer_ != i) { + if (!printedWarning) { + qmlWarning(this) << "Use the sourceItem property for the contained item, direct children are not supported"; + printedWarning = true; + } + + qmlWarning(i) << "deleting this child"; + i->deleteLater(); + } + } + } +} + +/*! + \qmlproperty QPointF MapQuickItem::anchorPoint + + This property determines which point on the sourceItem that will be lined + up with the coordinate on the map. +*/ +void QDeclarativeGeoMapQuickItem::setAnchorPoint(const QPointF &anchorPoint) +{ + if (anchorPoint == anchorPoint_) + return; + anchorPoint_ = anchorPoint; + polishAndUpdate(); + emit anchorPointChanged(); +} + +QPointF QDeclarativeGeoMapQuickItem::anchorPoint() const +{ + return anchorPoint_; +} + +/*! + \qmlproperty real MapQuickItem::zoomLevel + + This property controls the scaling behaviour of the contents of the + MapQuickItem. In particular, by setting this property it is possible + to choose between objects that are drawn on the screen (and sized in + screen pixels), and those drawn on the map surface (which change size + with the zoom level of the map). + + The default value for this property is 0.0, which corresponds to drawing + the object on the screen surface. If set to another value, the object will + be drawn on the map surface instead. The value (if not zero) specifies the + zoomLevel at which the object will be visible at a scale of 1:1 (ie, where + object pixels and screen pixels are the same). At zoom levels lower than + this, the object will appear smaller, and at higher zoom levels, appear + larger. This is in contrast to when this property is set to zero, where + the object remains the same size on the screen at all zoom levels. +*/ +void QDeclarativeGeoMapQuickItem::setZoomLevel(qreal zoomLevel) +{ + if (zoomLevel == zoomLevel_) + return; + zoomLevel_ = zoomLevel; + // TODO: update geoshape_! + polishAndUpdate(); + emit zoomLevelChanged(); +} + +qreal QDeclarativeGeoMapQuickItem::zoomLevel() const +{ + return zoomLevel_; +} + +const QGeoShape &QDeclarativeGeoMapQuickItem::geoShape() const +{ + // TODO: return a QGeoRectangle representing the bounding geo rectangle of the quick item + // when zoomLevel_ is != 0.0 + return geoshape_; +} + +QGeoMap::ItemType QDeclarativeGeoMapQuickItem::itemType() const +{ + return QGeoMap::MapQuickItem; +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::updatePolish() +{ + if (!quickMap() && sourceItem_) { + mapAndSourceItemSet_ = false; + sourceItem_.data()->setParentItem(0); + return; + } + + if (!quickMap() || !map() || !sourceItem_) { + mapAndSourceItemSet_ = false; + return; + } + + if (!mapAndSourceItemSet_ && quickMap() && map() && sourceItem_) { + mapAndSourceItemSet_ = true; + sourceItem_.data()->setParentItem(opacityContainer_); + sourceItem_.data()->setTransformOrigin(QQuickItem::TopLeft); + connect(sourceItem_.data(), SIGNAL(xChanged()), + this, SLOT(polishAndUpdate())); + connect(sourceItem_.data(), SIGNAL(yChanged()), + this, SLOT(polishAndUpdate())); + connect(sourceItem_.data(), SIGNAL(widthChanged()), + this, SLOT(polishAndUpdate())); + connect(sourceItem_.data(), SIGNAL(heightChanged()), + this, SLOT(polishAndUpdate())); + } + + if (!coordinate_.isValid()) { + opacityContainer_->setVisible(false); + return; + } else { + opacityContainer_->setVisible(true); + } + + QScopedValueRollback rollback(updatingGeometry_); + updatingGeometry_ = true; + + opacityContainer_->setOpacity(zoomLevelOpacity()); + + setWidth(sourceItem_.data()->width()); + setHeight(sourceItem_.data()->height()); + if (zoomLevel_ != 0.0 // zoom level initialized to 0.0. If it's different, it has been set explicitly. + && map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { // Currently unsupported on any other projection + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + + if (!matrix_) { + matrix_ = new QMapQuickItemMatrix4x4(this); + matrix_->appendToItem(opacityContainer_); + } + matrix_->setMatrix(p.quickItemTransformation(coordinate(), anchorPoint_, zoomLevel_)); + setPosition(QPointF(0,0)); + } else { + if (map()->geoProjection().projectionType() == QGeoProjection::ProjectionWebMercator) { + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + if (map()->cameraData().tilt() > 0.0 + && !p.isProjectable(p.geoToWrappedMapProjection(coordinate()))) { + // if the coordinate is behind the camera, we use the transformation to get the item out of the way + if (!matrix_) { + matrix_ = new QMapQuickItemMatrix4x4(this); + matrix_->appendToItem(opacityContainer_); + } + matrix_->setMatrix(p.quickItemTransformation(coordinate(), anchorPoint_, map()->cameraData().zoomLevel())); + setPosition(QPointF(0,0)); + } else { // All good, rendering screen-aligned + if (matrix_) + matrix_->setMatrix(QMatrix4x4()); + setPositionOnMap(coordinate(), anchorPoint_); + } + } else { // On other projections we can only currently test if coordinateToItemPosition returns a valid position + if (map()->cameraData().tilt() > 0.0 + && qIsNaN(map()->geoProjection().coordinateToItemPosition(coordinate(), false).x())) { + opacityContainer_->setVisible(false); + } else { + if (matrix_) + matrix_->setMatrix(QMatrix4x4()); + setPositionOnMap(coordinate(), anchorPoint_); + } + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoMapQuickItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + Q_UNUSED(event); +} + +/*! + \internal +*/ +qreal QDeclarativeGeoMapQuickItem::scaleFactor() +{ + qreal scale = 1.0; + // use 1+x to avoid fuzzy compare against zero + if (!qFuzzyCompare(1.0 + zoomLevel_, 1.0)) + scale = std::pow(0.5, zoomLevel_ - map()->cameraData().zoomLevel()); + return scale; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h b/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h new file mode 100644 index 0000000..8e2c278 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomapquickitem_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPQUICKITEM_H +#define QDECLARATIVEGEOMAPQUICKITEM_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QMapQuickItemMatrix4x4 : public QQuickTransform +{ +public: + QMapQuickItemMatrix4x4(QObject *parent = nullptr); + + void setMatrix(const QMatrix4x4& matrix); + void applyTo(QMatrix4x4 *matrix) const override; + + QMatrix4x4 m_matrix; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapQuickItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) + Q_PROPERTY(QPointF anchorPoint READ anchorPoint WRITE setAnchorPoint NOTIFY anchorPointChanged) + Q_PROPERTY(qreal zoomLevel READ zoomLevel WRITE setZoomLevel NOTIFY zoomLevelChanged) + Q_PROPERTY(QQuickItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged) + +public: + explicit QDeclarativeGeoMapQuickItem(QQuickItem *parent = 0); + ~QDeclarativeGeoMapQuickItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; + + void setCoordinate(const QGeoCoordinate &coordinate); + QGeoCoordinate coordinate(); + + void setSourceItem(QQuickItem *sourceItem); + QQuickItem *sourceItem(); + + void setAnchorPoint(const QPointF &anchorPoint); + QPointF anchorPoint() const; + + void setZoomLevel(qreal zoomLevel); + qreal zoomLevel() const; + + const QGeoShape &geoShape() const override; + QGeoMap::ItemType itemType() const override; + +Q_SIGNALS: + void coordinateChanged(); + void sourceItemChanged(); + void anchorPointChanged(); + void zoomLevelChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void updatePolish() override; + bool childMouseEventFilter(QQuickItem *item, QEvent *event) override; + +protected Q_SLOTS: + virtual void afterChildrenChanged() override; + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; + +private: + qreal scaleFactor(); + QGeoCoordinate dragStartCoordinate_; + QGeoCoordinate coordinate_; + QGeoRectangle geoshape_; + QPointer sourceItem_; + QQuickItem *opacityContainer_; + QPointF anchorPoint_; + qreal zoomLevel_; + bool mapAndSourceItemSet_; + bool updatingGeometry_; + QMapQuickItemMatrix4x4 *matrix_; + + friend class QDeclarativeGeoMap; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapQuickItem) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeomaptype.cpp b/src/location/declarativemaps/qdeclarativegeomaptype.cpp new file mode 100644 index 0000000..66ceaac --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaptype.cpp @@ -0,0 +1,247 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomaptype_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapType + \instantiates QDeclarativeGeoMapType + \inherits QObject + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.5 + + \brief The MapType type holds information about a map type. + + This includes the map type's \l name and \l description, the \l style and + a flag to indicate if the map type is optimized for mobile devices (\l mobile). +*/ + +QDeclarativeGeoMapType::QDeclarativeGeoMapType(const QGeoMapType mapType, QObject *parent) + : QObject(parent), + mapType_(mapType), + cameraCapabilities_(new QDeclarativeGeoCameraCapabilities(mapType.cameraCapabilities(), this)) {} + +QDeclarativeGeoMapType::~QDeclarativeGeoMapType() {} + +/*! + \qmlproperty enumeration MapType::style + + This read-only property gives access to the style of the map type. + + \list + \li MapType.NoMap - No map. + \li MapType.StreetMap - A street map. + \li MapType.SatelliteMapDay - A map with day-time satellite imagery. + \li MapType.SatelliteMapNight - A map with night-time satellite imagery. + \li MapType.TerrainMap - A terrain map. + \li MapType.HybridMap - A map with satellite imagery and street information. + \li MapType.GrayStreetMap - A gray-shaded street map. + \li MapType.PedestrianMap - A street map suitable for pedestriants. + \li MapType.CarNavigationMap - A street map suitable for car navigation. + \li MapType.CycleMap - A street map suitable for cyclists. + \li MapType.CustomMap - A custom map type. + \endlist +*/ +QDeclarativeGeoMapType::MapStyle QDeclarativeGeoMapType::style() const +{ + return QDeclarativeGeoMapType::MapStyle(mapType_.style()); +} + +/*! + \qmlproperty string MapType::name + + This read-only property holds the name of the map type as a single formatted string. +*/ +QString QDeclarativeGeoMapType::name() const +{ + return mapType_.name(); +} + +/*! + \qmlproperty string MapType::description + + This read-only property holds the description of the map type as a single formatted string. +*/ +QString QDeclarativeGeoMapType::description() const +{ + return mapType_.description(); +} + +/*! + \qmlproperty bool MapType::mobile + + \brief Whether the map type is optimized for the use on a mobile device. + + Map types for mobile devices usually have higher constrast to counteract the + effects of sunlight and a reduced color for improved readability. +*/ +bool QDeclarativeGeoMapType::mobile() const +{ + return mapType_.mobile(); +} + +/*! + \qmlproperty bool MapType::night + \since QtLocation 5.4 + + \brief Whether the map type is optimized for use at night. + + Map types suitable for use at night usually have a dark background. +*/ +bool QDeclarativeGeoMapType::night() const +{ + return mapType_.night(); +} + +/*! + \qmlproperty CameraCapabilities MapType::cameraCapabilities + \since QtLocation 5.10 + + This property holds the camera capabilities for this map type. +*/ +QDeclarativeGeoCameraCapabilities *QDeclarativeGeoMapType::cameraCapabilities() const +{ + return cameraCapabilities_; +} + +/*! + \qmlproperty VariantMap MapType::metadata + \since QtLocation 5.10 + + This property holds optional, extra metadata related to a specific map type. + The content of this property is entirely plugin-specific. +*/ +QVariantMap QDeclarativeGeoMapType::metadata() const +{ + return mapType_.metadata(); +} + +/* + * QDeclarativeGeoCameraCapabilities implementation + */ + +/*! + \qmltype CameraCapabilities + \instantiates QDeclarativeGeoCameraCapabilities + \inherits QObject + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.10 + + \brief The CameraCapabilities type holds information about the camera capabilities for a specific map type. + + This includes the map minimum and maximum zoom level, minimum and maximum tilt angle and + minimum and maximum field of view. +*/ + +QDeclarativeGeoCameraCapabilities::QDeclarativeGeoCameraCapabilities(const QGeoCameraCapabilities &cameraCaps, QObject *parent) + : QObject(parent), cameraCaps_(cameraCaps) +{ + +} + +QDeclarativeGeoCameraCapabilities::~QDeclarativeGeoCameraCapabilities() +{ + +} + +/*! + \qmlproperty qreal CameraCapabilities::minimumZoomLevel + + This read-only property holds the minimum available zoom level with this map type. +*/ +qreal QDeclarativeGeoCameraCapabilities::minimumZoomLevel() const +{ + return cameraCaps_.minimumZoomLevelAt256(); +} + +/*! + \qmlproperty qreal CameraCapabilities::maximumZoomLevel + + This read-only property holds the maximum available zoom level with this map type. +*/ +qreal QDeclarativeGeoCameraCapabilities::maximumZoomLevel() const +{ + return cameraCaps_.maximumZoomLevelAt256(); +} + +/*! + \qmlproperty qreal CameraCapabilities::minimumTilt + + This read-only property holds the minimum available tilt with this map type. +*/ +qreal QDeclarativeGeoCameraCapabilities::minimumTilt() const +{ + return cameraCaps_.minimumTilt(); +} + +/*! + \qmlproperty qreal CameraCapabilities::maximumTilt + + This read-only property holds the maximum available tilt with this map type. +*/ +qreal QDeclarativeGeoCameraCapabilities::maximumTilt() const +{ + return cameraCaps_.maximumTilt(); +} + +/*! + \qmlproperty qreal CameraCapabilities::minimumFieldOfView + + This read-only property holds the minimum available field of view with this map type. +*/ +qreal QDeclarativeGeoCameraCapabilities::minimumFieldOfView() const +{ + return cameraCaps_.minimumFieldOfView(); +} + +/*! + \qmlproperty qreal CameraCapabilities::maximumFieldOfView + + This read-only property holds the maximum available field of view with this map type. +*/ +qreal QDeclarativeGeoCameraCapabilities::maximumFieldOfView() const +{ + return cameraCaps_.maximumFieldOfView(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeomaptype_p.h b/src/location/declarativemaps/qdeclarativegeomaptype_p.h new file mode 100644 index 0000000..5d99083 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeomaptype_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOMAPTYPE_H +#define QDECLARATIVEGEOMAPTYPE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoCameraCapabilities: public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal minimumZoomLevel READ minimumZoomLevel CONSTANT) + Q_PROPERTY(qreal maximumZoomLevel READ maximumZoomLevel CONSTANT) + Q_PROPERTY(qreal minimumTilt READ minimumTilt CONSTANT) + Q_PROPERTY(qreal maximumTilt READ maximumTilt CONSTANT) + Q_PROPERTY(qreal minimumFieldOfView READ minimumFieldOfView CONSTANT) + Q_PROPERTY(qreal maximumFieldOfView READ maximumFieldOfView CONSTANT) + +public: + QDeclarativeGeoCameraCapabilities(const QGeoCameraCapabilities &cameraCaps, QObject *parent = 0); + ~QDeclarativeGeoCameraCapabilities(); + + qreal minimumZoomLevel() const; + qreal maximumZoomLevel() const; + qreal minimumTilt() const; + qreal maximumTilt() const; + qreal minimumFieldOfView() const; + qreal maximumFieldOfView() const; + +private: + QGeoCameraCapabilities cameraCaps_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoMapType : public QObject +{ + Q_OBJECT + Q_ENUMS(MapStyle) + + Q_PROPERTY(MapStyle style READ style CONSTANT) + Q_PROPERTY(QString name READ name CONSTANT) + Q_PROPERTY(QString description READ description CONSTANT) + Q_PROPERTY(bool mobile READ mobile CONSTANT) + Q_PROPERTY(bool night READ night CONSTANT REVISION 1) + Q_PROPERTY(QDeclarativeGeoCameraCapabilities *cameraCapabilities READ cameraCapabilities CONSTANT) + Q_PROPERTY(QVariantMap metadata READ metadata CONSTANT) + +public: + enum MapStyle { + NoMap = QGeoMapType::NoMap, + StreetMap = QGeoMapType::StreetMap, + SatelliteMapDay = QGeoMapType::SatelliteMapDay, + SatelliteMapNight = QGeoMapType::SatelliteMapNight, + TerrainMap = QGeoMapType::TerrainMap, + HybridMap = QGeoMapType::HybridMap, + TransitMap = QGeoMapType::TransitMap, + GrayStreetMap = QGeoMapType::GrayStreetMap, + PedestrianMap = QGeoMapType::PedestrianMap, + CarNavigationMap = QGeoMapType::CarNavigationMap, + CycleMap = QGeoMapType::CycleMap, + CustomMap = 100 + }; + + QDeclarativeGeoMapType(const QGeoMapType mapType, QObject *parent = 0); + ~QDeclarativeGeoMapType(); + + MapStyle style() const; + QString name() const; + QString description() const; + bool mobile() const; + bool night() const; + QDeclarativeGeoCameraCapabilities *cameraCapabilities() const; + QVariantMap metadata() const; + + const QGeoMapType mapType() { return mapType_; } + +private: + QGeoMapType mapType_; + QDeclarativeGeoCameraCapabilities *cameraCapabilities_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoMapType) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoroute.cpp b/src/location/declarativemaps/qdeclarativegeoroute.cpp new file mode 100644 index 0000000..95a26a3 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroute.cpp @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeoroute_p.h" +#include "locationvaluetypehelper_p.h" +#include +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Route + \instantiates QDeclarativeGeoRoute + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since QtLocation 5.5 + + \brief The Route type represents one geographical route. + + A Route type contains high level information about a route, such + as the length the route, the estimated travel time for the route, + and enough information to render a basic image of the route on a map. + + The QGeoRoute object also contains a list of \l RouteSegment objects which + describe subsections of the route in greater detail. + + The primary means of acquiring Route objects is \l RouteModel. + + \section1 Example + + This example shows how to display a route's maneuvers in a ListView: + + \snippet declarative/routing.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/routing.qml Route Maneuver List1 + \snippet declarative/routing.qml Route Maneuver List2 + \snippet declarative/routing.qml Route Maneuver List3 + +*/ + +QDeclarativeGeoRoute::QDeclarativeGeoRoute(QObject *parent) + : QObject(parent), segmentsDirty_(true) +{ +} + +QDeclarativeGeoRoute::QDeclarativeGeoRoute(const QGeoRoute &route, QObject *parent) + : QObject(parent), route_(route), segmentsDirty_(true) +{ +} + +QDeclarativeGeoRoute::~QDeclarativeGeoRoute() {} + +void QDeclarativeGeoRoute::initSegments(unsigned int lastIndex) // -1 turns it into unsigned int max +{ + if (!segmentsDirty_) + return; + + QGeoRouteSegment segment = route_.firstRouteSegment(); + unsigned int idx = 0; + unsigned int initialListSize = static_cast(segments_.size()); + while (segment.isValid()) { + if (idx >= initialListSize) { + QDeclarativeGeoRouteSegment *routeSegment = new QDeclarativeGeoRouteSegment(segment, this); + QQmlEngine::setContextForObject(routeSegment, QQmlEngine::contextForObject(this)); + segments_.append(routeSegment); + } + ++idx; + segment = segment.nextRouteSegment(); + if (idx > lastIndex && segment.isValid()) // Do not clean segmentsDirty_ if there are still segments to initialize + return; + } + segmentsDirty_ = false; +} + +/*! + \internal +*/ +QList QDeclarativeGeoRoute::routePath() +{ + return route_.path(); +} + +/*! + \qmlproperty georectangle QtLocation::Route::bounds + + Read-only property which holds a bounding box which encompasses the entire route. + +*/ + +QGeoRectangle QDeclarativeGeoRoute::bounds() const +{ + return route_.bounds(); +} + +/*! + \qmlproperty int QtLocation::Route::travelTime + + Read-only property which holds the estimated amount of time it will take to + traverse this route, in seconds. + +*/ + +int QDeclarativeGeoRoute::travelTime() const +{ + return route_.travelTime(); +} + +/*! + \qmlproperty real QtLocation::Route::distance + + Read-only property which holds distance covered by this route, in meters. +*/ + +qreal QDeclarativeGeoRoute::distance() const +{ + return route_.distance(); +} + +/*! + \qmlproperty list QtLocation::Route::path + + Read-only property which holds the geographical coordinates of this route. + Coordinates are listed in the order in which they would be traversed by someone + traveling along this segment of the route. + + To access individual segments you can use standard list accessors: 'path.length' + indicates the number of objects and 'path[index starting from zero]' gives + the actual object. + + \sa QtPositioning::coordinate +*/ + +QJSValue QDeclarativeGeoRoute::path() const +{ + QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped pathArray(scope, v4->newArrayObject(route_.path().length())); + for (int i = 0; i < route_.path().length(); ++i) { + const QGeoCoordinate &c = route_.path().at(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); + pathArray->putIndexed(i, cv); + } + + return QJSValue(v4, pathArray.asReturnedValue()); +} + +void QDeclarativeGeoRoute::setPath(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList pathList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoCoordinate c = parseCoordinate(value.property(i), &ok); + + if (!ok || !c.isValid()) { + qmlWarning(this) << "Unsupported path type"; + return; + } + + pathList.append(c); + } + + if (route_.path() == pathList) + return; + + route_.setPath(pathList); + + emit pathChanged(); +} + +/*! + \qmlproperty list QtLocation::Route::segments + + Read-only property which holds the list of \l RouteSegment objects of this route. + + To access individual segments you can use standard list accessors: 'segments.length' + indicates the number of objects and 'segments[index starting from zero]' gives + the actual objects. + + \sa RouteSegment +*/ + +QQmlListProperty QDeclarativeGeoRoute::segments() +{ + return QQmlListProperty(this, 0, segments_append, segments_count, + segments_at, segments_clear); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::segments_append(QQmlListProperty *prop, + QDeclarativeGeoRouteSegment *segment) +{ + QDeclarativeGeoRoute *declRoute = static_cast(prop->object); + declRoute->initSegments(); + declRoute->appendSegment(segment); +} + +/*! + \internal +*/ +int QDeclarativeGeoRoute::segments_count(QQmlListProperty *prop) +{ + QDeclarativeGeoRoute *declRoute = static_cast(prop->object); + return declRoute->segmentsCount(); +} + +/*! + \internal +*/ +QDeclarativeGeoRouteSegment *QDeclarativeGeoRoute::segments_at(QQmlListProperty *prop, int index) +{ + QDeclarativeGeoRoute *declRoute = static_cast(prop->object); + declRoute->initSegments(index); // init only what's needed. + return declRoute->segments_.at(index); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::segments_clear(QQmlListProperty *prop) +{ + static_cast(prop->object)->clearSegments(); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::appendSegment(QDeclarativeGeoRouteSegment *segment) +{ + segments_.append(segment); +} + +/*! + \internal +*/ +void QDeclarativeGeoRoute::clearSegments() +{ + segments_.clear(); +} + +/*! + \qmlmethod int QtLocation::Route::segmentsCount() + + Returns the number of segments in the route + + \sa RouteSegment + + \since 5.11 +*/ + +int QDeclarativeGeoRoute::segmentsCount() const +{ + return qMax(route_.d_ptr->segmentsCount(), segments_.count()); +} + +const QGeoRoute &QDeclarativeGeoRoute::route() const +{ + return route_; +} + +/*! + \qmlproperty RouteQuery QtLocation::Route::routeQuery + + Returns the route query associated with this route. + + \since 5.11 +*/ +QDeclarativeGeoRouteQuery *QDeclarativeGeoRoute::routeQuery() +{ + if (!routeQuery_) + routeQuery_ = new QDeclarativeGeoRouteQuery(route_.request(), this); + return routeQuery_; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeoroute_p.h b/src/location/declarativemaps/qdeclarativegeoroute_p.h new file mode 100644 index 0000000..c19c267 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroute_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOROUTE_H +#define QDECLARATIVEGEOROUTE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QDeclarativeGeoRouteQuery; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRoute : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QGeoRectangle bounds READ bounds CONSTANT) + Q_PROPERTY(int travelTime READ travelTime CONSTANT) + Q_PROPERTY(qreal distance READ distance CONSTANT) + Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QQmlListProperty segments READ segments CONSTANT) + Q_PROPERTY(QDeclarativeGeoRouteQuery *routeQuery READ routeQuery REVISION 11) + +public: + explicit QDeclarativeGeoRoute(QObject *parent = 0); + QDeclarativeGeoRoute(const QGeoRoute &route, QObject *parent = 0); + ~QDeclarativeGeoRoute(); + + QGeoRectangle bounds() const; + int travelTime() const; + qreal distance() const; + + QJSValue path() const; + void setPath(const QJSValue &value); + + QQmlListProperty segments(); + + void appendSegment(QDeclarativeGeoRouteSegment *segment); + void clearSegments(); + + int segmentsCount() const; + const QGeoRoute &route() const; + QDeclarativeGeoRouteQuery *routeQuery(); + +Q_SIGNALS: + void pathChanged(); + +private: + static void segments_append(QQmlListProperty *prop, QDeclarativeGeoRouteSegment *segment); + static int segments_count(QQmlListProperty *prop); + static QDeclarativeGeoRouteSegment *segments_at(QQmlListProperty *prop, int index); + static void segments_clear(QQmlListProperty *prop); + + void initSegments(unsigned int lastIndex = ~0U); + QList routePath(); + + QGeoRoute route_; + QDeclarativeGeoRouteQuery *routeQuery_ = nullptr; + QList segments_; + bool segmentsDirty_; + friend class QDeclarativeRouteMapItem; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp b/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp new file mode 100644 index 0000000..775fb11 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutemodel.cpp @@ -0,0 +1,1913 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeoroutemodel_p.h" +#include "qdeclarativegeoroute_p.h" +#include "error_messages_p.h" +#include "locationvaluetypehelper_p.h" + +#include +#include +#include +#include +#include +#include +#include "qdeclarativegeomapparameter_p.h" + +QT_BEGIN_NAMESPACE + +static bool compareFloats(qreal a, qreal b) +{ + return (qIsNaN(a) && qIsNaN(b)) + || a == b; +} + +static bool compareParameterList(const QList &a, const QList &b) +{ + if (a.size() != b.size()) + return false; + if (a != b) { + for (int i = 0; i < a.size(); ++i) { + if (! (*a.at(i) == *b.at(i))) + return false; + } + } + return true; +} + +static int findWaypoint(const QList &waypoints, const QDeclarativeGeoWaypoint *w) +{ + for (int i = waypoints.size() - 1; i >= 0; --i) { + if (waypoints.at(i) == w || *waypoints.at(i) == *w) + return i; + } + return -1; +} + +static int findWaypoint(const QList &waypoints, const QGeoCoordinate &c) +{ + for (int i = waypoints.size() - 1; i >= 0; --i) { + if (waypoints.at(i)->coordinate() == c) + return i; + } + return -1; +} + +static QList waypointCoordinates(const QList &waypoints) +{ + QList res; + for (const QDeclarativeGeoWaypoint *w: waypoints) + res << w->coordinate(); + return res; +} + +static QList waypointMetadata(const QList &waypoints) +{ + QList res; + for (QDeclarativeGeoWaypoint *w: waypoints) + res << w->metadata(); + return res; +} + +/*! + \qmltype RouteModel + \instantiates QDeclarativeGeoRouteModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since QtLocation 5.5 + + \brief The RouteModel type provides access to routes. + + The RouteModel type is used as part of a model/view grouping to retrieve + geographic routes from a backend provider. Routes include data about driving + directions between two points, walking directions with multiple waypoints, + and various other similar concepts. It functions much like other Model + types in QML (see for example \l {Models and Views in Qt Quick#Models}{ListModel} and + \l XmlListModel), and interacts with views such as \l MapItemView, and \l{ListView}. + + Like \l Map and \l GeocodeModel, all the data for a RouteModel to work comes + from a services plugin. This is contained in the \l{plugin} property, and + this must be set before the RouteModel can do any useful work. + + Once the plugin is set, create a \l RouteQuery with the appropriate + waypoints and other settings, and set the RouteModel's \l{query} + property. If \l autoUpdate is enabled, the update will being automatically. + Otherwise, the \l{update} method may be used. By default, autoUpdate is + disabled. + + The data stored and returned in the RouteModel consists of \l Route objects, + as a list with the role name "routeData". See the documentation for \l Route + for further details on its structure and contents. + + \section2 Example Usage + + The following snippet is two-part, showing firstly the declaration of + objects, and secondly a short piece of procedural code using it. We set + the routeModel's \l{autoUpdate} property to false, and call \l{update} once + the query is set up, to avoid useless extra requests halfway through the + set up of the query. + + \code + Plugin { + id: aPlugin + name: "osm" + } + + RouteQuery { + id: aQuery + } + + RouteModel { + id: routeModel + plugin: aPlugin + query: aQuery + autoUpdate: false + } + \endcode + + \code + { + aQuery.addWaypoint(...) + aQuery.addWaypoint(...) + aQuery.travelModes = ... + routeModel.update() + } + \endcode + +*/ + +QDeclarativeGeoRouteModel::QDeclarativeGeoRouteModel(QObject *parent) + : QAbstractListModel(parent), + complete_(false), + plugin_(0), + routeQuery_(0), + autoUpdate_(false), + status_(QDeclarativeGeoRouteModel::Null), + error_(QDeclarativeGeoRouteModel::NoError) +{ +} + +QDeclarativeGeoRouteModel::~QDeclarativeGeoRouteModel() +{ + if (!routes_.empty()) { + qDeleteAll(routes_); + routes_.clear(); + } +} + +/*! + \qmlproperty int QtLocation::RouteModel::count + + This property holds how many routes the model currently has. + Amongst other uses, you can use this value when accessing routes + via the QtLocation::RouteModel::get -method. +*/ + +int QDeclarativeGeoRouteModel::count() const +{ + return routes_.count(); +} + +/*! + \qmlmethod void QtLocation::RouteModel::reset() + + Resets the model. All route data is cleared, any outstanding requests + are aborted and possible errors are cleared. Model status will be set + to RouteModel.Null +*/ + +void QDeclarativeGeoRouteModel::reset() +{ + if (!routes_.isEmpty()) { + beginResetModel(); + qDeleteAll(routes_); + routes_.clear(); + emit countChanged(); + emit routesChanged(); + endResetModel(); + } + + emit abortRequested(); + setError(NoError, QString()); + setStatus(QDeclarativeGeoRouteModel::Null); +} + +/*! + \qmlmethod void QtLocation::RouteModel::cancel() + + Cancels any outstanding requests and clears errors. Model status will be set to either + RouteModel.Null or RouteModel.Ready. +*/ +void QDeclarativeGeoRouteModel::cancel() +{ + emit abortRequested(); + setError(NoError, QString()); + setStatus(routes_.isEmpty() ? Null : Ready); +} + +/*! + \qmlmethod Route QtLocation::RouteModel::get(int) + + Returns the Route at given index. Use \l count property to check the + amount of routes available. The routes are indexed from zero, so the accessible range + is 0...(count - 1). + + If you access out of bounds, a zero (null object) is returned and a warning is issued. +*/ + +QDeclarativeGeoRoute *QDeclarativeGeoRouteModel::get(int index) +{ + if (index < 0 || index >= routes_.count()) { + qmlWarning(this) << QStringLiteral("Index '%1' out of range").arg(index); + return 0; + } + return routes_.at(index); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::componentComplete() +{ + complete_ = true; + if (autoUpdate_) { + update(); + } +} + +/*! + \internal +*/ +int QDeclarativeGeoRouteModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + return routes_.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativeGeoRouteModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) { + qmlWarning(this) << QStringLiteral("Error in indexing route model's data (invalid index)."); + return QVariant(); + } + + if (index.row() >= routes_.count()) { + qmlWarning(this) << QStringLiteral("Fatal error in indexing route model's data (index overflow)."); + return QVariant(); + } + + if (role == RouteRole) { + QObject *route = routes_.at(index.row()); + return QVariant::fromValue(route); + } + return QVariant(); +} + +QHash QDeclarativeGeoRouteModel::roleNames() const +{ + QHash roleNames = QAbstractListModel::roleNames(); + roleNames.insert(RouteRole, "routeData"); + return roleNames; +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (plugin_ == plugin) + return; + + reset(); // reset the model + + if (plugin_) + disconnect(plugin_, SIGNAL(localesChanged()), this, SIGNAL(measurementSystemChanged())); + if (plugin) + connect(plugin, SIGNAL(localesChanged()), this, SIGNAL(measurementSystemChanged())); + + plugin_ = plugin; + + if (complete_) + emit pluginChanged(); + + if (!plugin) + return; + + if (plugin_->isAttached()) { + pluginReady(); + } else { + connect(plugin_, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::pluginReady() +{ + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + + if (serviceProvider->error() != QGeoServiceProvider::NoError) { + QDeclarativeGeoRouteModel::RouteError newError = UnknownError; + switch (serviceProvider->error()) { + case QGeoServiceProvider::NotSupportedError: + newError = EngineNotSetError; break; + case QGeoServiceProvider::UnknownParameterError: + newError = UnknownParameterError; break; + case QGeoServiceProvider::MissingRequiredParameterError: + newError = MissingRequiredParameterError; break; + case QGeoServiceProvider::ConnectionError: + newError = CommunicationError; break; + default: + break; + } + + setError(newError, serviceProvider->errorString()); + return; + } + + if (!routingManager) { + setError(EngineNotSetError, tr("Plugin does not support routing.")); + return; + } + + connect(routingManager, SIGNAL(finished(QGeoRouteReply*)), + this, SLOT(routingFinished(QGeoRouteReply*))); + connect(routingManager, SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)), + this, SLOT(routingError(QGeoRouteReply*,QGeoRouteReply::Error,QString))); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::queryDetailsChanged() +{ + if (autoUpdate_ && complete_) + update(); +} + +/*! + \qmlproperty Plugin QtLocation::RouteModel::plugin + + This property holds the plugin that providers the actual + routing service. Note that all plugins do not necessarily + provide routing (could for example provide only geocoding or maps). + + A valid plugin must be set before the RouteModel can perform any useful + operations. + + \sa Plugin +*/ + +QDeclarativeGeoServiceProvider *QDeclarativeGeoRouteModel::plugin() const +{ + return plugin_; +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setQuery(QDeclarativeGeoRouteQuery *query) +{ + if (!query || query == routeQuery_) + return; + if (routeQuery_) + routeQuery_->disconnect(this); + routeQuery_ = query; + connect(query, SIGNAL(queryDetailsChanged()), this, SLOT(queryDetailsChanged())); + if (complete_) { + emit queryChanged(); + if (autoUpdate_) + update(); + } +} + +/*! + \qmlproperty RouteQuery QtLocation::RouteModel::query + + This property holds the data of the route request. + The primary data are the waypoint coordinates and possible further + preferences (means of traveling, things to avoid on route etc). +*/ + +QDeclarativeGeoRouteQuery *QDeclarativeGeoRouteModel::query() const +{ + return routeQuery_; +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setAutoUpdate(bool autoUpdate) +{ + if (autoUpdate_ == autoUpdate) + return; + autoUpdate_ = autoUpdate; + if (complete_) + emit autoUpdateChanged(); +} + +/*! + \qmlproperty bool QtLocation::RouteModel::autoUpdate + + This property controls whether the Model automatically updates in response + to changes in its attached RouteQuery. The default value of this property + is false. + + If setting this value to 'true', note that any change at all in + the RouteQuery object set in the \l{query} property will trigger a new + request to be sent. If you are adjusting many properties of the RouteQuery + with autoUpdate enabled, this can generate large numbers of useless (and + later discarded) requests. +*/ + +bool QDeclarativeGeoRouteModel::autoUpdate() const +{ + return autoUpdate_; +} + +/*! + \qmlproperty Locale::MeasurementSystem QtLocation::RouteModel::measurementSystem + + This property holds the measurement system which will be used when calculating the route. This + property is changed when the \l {QtLocation::Plugin::locales}{Plugin::locales} property of + \l {QtLocation::RouteModel::plugin}{plugin} changes. + + If setting this property it must be set after the \l {QtLocation::RouteModel::plugin}{plugin} + property is set. +*/ +void QDeclarativeGeoRouteModel::setMeasurementSystem(QLocale::MeasurementSystem ms) +{ + if (!plugin_) + return; + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + if (!routingManager) + return; + + if (routingManager->measurementSystem() == ms) + return; + + routingManager->setMeasurementSystem(ms); + emit measurementSystemChanged(); +} + +QLocale::MeasurementSystem QDeclarativeGeoRouteModel::measurementSystem() const +{ + if (!plugin_) + return QLocale().measurementSystem(); + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) { + if (plugin_->locales().isEmpty()) + return QLocale().measurementSystem(); + + return QLocale(plugin_->locales().first()).measurementSystem(); + } + + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + if (!routingManager) { + if (plugin_->locales().isEmpty()) + return QLocale().measurementSystem(); + + return QLocale(plugin_->locales().first()).measurementSystem(); + } + + return routingManager->measurementSystem(); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::setStatus(QDeclarativeGeoRouteModel::Status status) +{ + if (status_ == status) + return; + + status_ = status; + + if (complete_) + emit statusChanged(); +} + +/*! + \qmlproperty enumeration QtLocation::RouteModel::status + + This read-only property holds the current status of the model. + + \list + \li RouteModel.Null - No route requests have been issued or \l reset has been called. + \li RouteModel.Ready - Route request(s) have finished successfully. + \li RouteModel.Loading - Route request has been issued but not yet finished + \li RouteModel.Error - Routing error has occurred, details are in \l error and \l errorString + \endlist +*/ + +QDeclarativeGeoRouteModel::Status QDeclarativeGeoRouteModel::status() const +{ + return status_; +} + +/*! + \qmlproperty string QtLocation::RouteModel::errorString + + This read-only property holds the textual presentation of the latest routing error. + If no error has occurred or the model has been reset, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. +*/ + +QString QDeclarativeGeoRouteModel::errorString() const +{ + return errorString_; +} + +/*! + \qmlproperty enumeration QtLocation::RouteModel::error + + This read-only property holds the latest error value of the routing request. + + \list + \li RouteModel.NoError - No error has occurred. + \li RouteModel.CommunicationError - An error occurred while communicating with the service provider. + \li RouteModel.EngineNotSetError - The model's plugin property was not set or there is no routing manager associated with the plugin. + \li RouteModel.MissingRequiredParameterError - A required parameter was not specified. + \li RouteModel.ParseError - The response from the service provider was in an unrecognizable format. + \li RouteModel.UnknownError - An error occurred which does not fit into any of the other categories. + \li RouteModel.UnknownParameterError - The plugin did not recognize one of the parameters it was given. + \li RouteModel.UnsupportedOptionError - The requested operation is not supported by the routing provider. + This may happen when the loaded engine does not support a particular + type of routing request. + \endlist +*/ + +QDeclarativeGeoRouteModel::RouteError QDeclarativeGeoRouteModel::error() const +{ + return error_; +} + +void QDeclarativeGeoRouteModel::setError(RouteError error, const QString& errorString) +{ + if (error_ == error && errorString_ == errorString) + return; + error_ = error; + errorString_ = errorString; + emit errorChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteModel::update() + + Instructs the RouteModel to update its data. This is most useful + when \l autoUpdate is disabled, to force a refresh when the query + has been changed. +*/ +void QDeclarativeGeoRouteModel::update() +{ + if (!complete_) + return; + + if (!plugin_) { + setError(EngineNotSetError, tr("Cannot route, plugin not set.")); + return; + } + + QGeoServiceProvider *serviceProvider = plugin_->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QGeoRoutingManager *routingManager = serviceProvider->routingManager(); + if (!routingManager) { + setError(EngineNotSetError, tr("Cannot route, route manager not set.")); + return; + } + if (!routeQuery_) { + setError(ParseError, tr("Cannot route, valid query not set.")); + return; + } + emit abortRequested(); // Clear previous requests + QGeoRouteRequest request = routeQuery_->routeRequest(); + if (request.waypoints().count() < 2) { + setError(ParseError,tr("Not enough waypoints for routing.")); + return; + } + + setError(NoError, QString()); + + QGeoRouteReply *reply = routingManager->calculateRoute(request); + setStatus(QDeclarativeGeoRouteModel::Loading); + if (!reply->isFinished()) { + connect(this, &QDeclarativeGeoRouteModel::abortRequested, reply, &QGeoRouteReply::abort); + } else { + if (reply->error() == QGeoRouteReply::NoError) { + routingFinished(reply); + } else { + routingError(reply, reply->error(), reply->errorString()); + } + } +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::routingFinished(QGeoRouteReply *reply) +{ + if (!reply) + return; + reply->deleteLater(); + if (reply->error() != QGeoRouteReply::NoError) + return; + + beginResetModel(); + int oldCount = routes_.count(); + qDeleteAll(routes_); + // Convert routes to declarative + routes_.clear(); + for (int i = 0; i < reply->routes().size(); ++i) { + QDeclarativeGeoRoute *route = new QDeclarativeGeoRoute(reply->routes().at(i), this); + QQmlEngine::setContextForObject(route, QQmlEngine::contextForObject(this)); + routes_.append(route); + } + endResetModel(); + + setError(NoError, QString()); + setStatus(QDeclarativeGeoRouteModel::Ready); + + if (oldCount != 0 || routes_.count() != 0) + emit routesChanged(); + if (oldCount != routes_.count()) + emit countChanged(); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteModel::routingError(QGeoRouteReply *reply, + QGeoRouteReply::Error error, + const QString &errorString) +{ + if (!reply) + return; + reply->deleteLater(); + setError(static_cast(error), errorString); + setStatus(QDeclarativeGeoRouteModel::Error); +} + + +/*! + \qmltype RouteQuery + \instantiates QDeclarativeGeoRouteQuery + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since QtLocation 5.5 + + \brief The RouteQuery type is used to provide query parameters to a + RouteModel. + + A RouteQuery is used to pack all the parameters necessary to make a request + to a routing service, which can then populate the contents of a RouteModel. + + These parameters describe key details of the route, such as \l waypoints to + pass through, \l excludedAreas to avoid, the \l travelModes in use, as well + as detailed preferences on how to optimize the route and what features + to prefer or avoid along the path (such as toll roads, highways, etc). + + RouteQuery objects are used exclusively to fill out the value of a + RouteModel's \l{RouteModel::query}{query} property, which can then begin + the retrieval process to populate the model. + + Some plugins might allow or require specific parameters to operate. + In order to specify these plugin-specific parameters, MapParameter elements + can be nested inside a RouteQuery. + + \section2 Example Usage + + The following snipped shows an incomplete example of creating a RouteQuery + object and setting it as the value of a RouteModel's \l{RouteModel::query}{query} + property. + + \code + RouteQuery { + id: aQuery + } + + RouteModel { + query: aQuery + autoUpdate: false + } + \endcode + + For a more complete example, see the documentation for the \l{RouteModel} + type, and the Mapviewer example. + + \sa RouteModel + +*/ + +QDeclarativeGeoRouteQuery::QDeclarativeGeoRouteQuery(QObject *parent) +: QObject(parent), complete_(false), m_excludedAreaCoordinateChanged(false) +{ +} + +QDeclarativeGeoRouteQuery::QDeclarativeGeoRouteQuery(const QGeoRouteRequest &request, QObject *parent) +: QObject(parent), request_(request), complete_(false), m_excludedAreaCoordinateChanged(false) +{ + // Extra params assumed to be already set in the request. + // Init waypoints + const QList wpts = request_.waypoints(); + const QList meta = request_.waypointsMetadata(); + for (int i = 0; i < wpts.size(); ++i) { + QDeclarativeGeoWaypoint *w = new QDeclarativeGeoWaypoint(this); + w->setCoordinate(wpts.at(i)); + w->setMetadata(meta.at(i)); + m_waypoints << w; + } +} + +QDeclarativeGeoRouteQuery::~QDeclarativeGeoRouteQuery() +{ +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteQuery::componentComplete() +{ + complete_ = true; +} + +/*! + \qmlproperty QList RouteQuery::featureTypes + + List of features that will be considered when planning the + route. Features with a weight of NeutralFeatureWeight will not be returned. + + \list + \li RouteQuery.NoFeature - No features will be taken into account when planning the route + \li RouteQuery.TollFeature - Consider tollways when planning the route + \li RouteQuery.HighwayFeature - Consider highways when planning the route + \li RouteQuery.PublicTransitFeature - Consider public transit when planning the route + \li RouteQuery.FerryFeature - Consider ferries when planning the route + \li RouteQuery.TunnelFeature - Consider tunnels when planning the route + \li RouteQuery.DirtRoadFeature - Consider dirt roads when planning the route + \li RouteQuery.ParksFeature - Consider parks when planning the route + \li RouteQuery.MotorPoolLaneFeature - Consider motor pool lanes when planning the route + \li RouteQuery.TrafficFeature - Consider traffic when planning the route + \endlist + + \sa setFeatureWeight, featureWeight +*/ + +QList QDeclarativeGeoRouteQuery::featureTypes() +{ + QList list; + + for (int i = 0; i < request_.featureTypes().count(); ++i) { + list.append(static_cast(request_.featureTypes().at(i))); + } + return list; +} + +/*! + \qmlproperty int RouteQuery::numberAlternativeRoutes + + The number of alternative routes requested when requesting routes. + The default value is 0. +*/ + + +int QDeclarativeGeoRouteQuery::numberAlternativeRoutes() const +{ + return request_.numberAlternativeRoutes(); +} + +void QDeclarativeGeoRouteQuery::setNumberAlternativeRoutes(int numberAlternativeRoutes) +{ + if (numberAlternativeRoutes == request_.numberAlternativeRoutes()) + return; + + request_.setNumberAlternativeRoutes(numberAlternativeRoutes); + + if (complete_) { + emit numberAlternativeRoutesChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlproperty list RouteQuery::waypoints + + + The coordinates of the waypoints for the desired route. + The waypoints should be given in order from origin to destination. + Two or more coordinates are needed. + + Waypoints can be set as part of the RouteQuery type declaration or + dynamically with the functions provided. + + When setting this property to a list of waypoints, each waypoint + can be either a \l coordinate or a \l Waypoint, interchangeably. + If a \l coordinate is passed, it will be internally converted to a + \l Waypoint. + + This property, however, always contains a list of coordinates. + + \sa waypointObjects, addWaypoint, removeWaypoint, clearWaypoints +*/ + +QVariantList QDeclarativeGeoRouteQuery::waypoints() +{ + QVariantList res; + + for (const auto &w : m_waypoints) + res << QVariant::fromValue(w->coordinate()); + + return res; +} + +/*! + \qmlmethod list QtLocation::RouteQuery::waypointObjects() + + This method can be used to retrieve the list of Waypoint objects + relative to RouteQuery::waypoints. + + \sa waypointObjects, addWaypoint, removeWaypoint, clearWaypoints +*/ +QVariantList QDeclarativeGeoRouteQuery::waypointObjects() +{ + QVariantList res; + + for (const auto &w : m_waypoints) + res << QVariant::fromValue(w); + + return res; +} + +void QDeclarativeGeoRouteQuery::setWaypoints(const QVariantList &value) +{ + QList waypointList; + bool allWaypoints = true; + + for (const auto &w: value) { + // First, test if this is already a QDeclarativeGeoWaypoint + // From QVariant to QObject * + QDeclarativeGeoWaypoint *waypoint = nullptr; + QObject *obj = qvariant_cast(w); + waypoint = qobject_cast(obj); + + if (waypoint) { + waypointList << waypoint; + continue; + } + + // if here, w is not a Waypoint, so either a QGeoCoordinate or a variant map, so a waypoint has to be instantiated. + allWaypoints = false; + + QGeoCoordinate c = parseCoordinate(w); + if (!c.isValid()) { + qmlWarning(this) << QStringLiteral("Invalid waypoint"); + flushWaypoints(waypointList); + return; + } + + waypoint = new QDeclarativeGeoWaypoint(this); + waypoint->setCoordinate(c); + waypointList << waypoint; + + } + + if (allWaypoints && m_waypoints == waypointList) + return; + + flushWaypoints(m_waypoints); + m_waypoints = waypointList; + for (const QDeclarativeGeoWaypoint *w: qAsConst(m_waypoints)) + connect(w, &QDeclarativeGeoWaypoint::waypointDetailsChanged, this, &QDeclarativeGeoRouteQuery::waypointChanged); + + waypointChanged(); +} + +/*! + \qmlproperty list RouteQuery::excludedAreas + + Areas that the route must not cross. + + Excluded areas can be set as part of the \l RouteQuery type declaration or + dynamically with the functions provided. + + \sa addExcludedArea, removeExcludedArea, clearExcludedAreas +*/ +QJSValue QDeclarativeGeoRouteQuery::excludedAreas() const +{ + QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped excludedAreasArray(scope, v4->newArrayObject(request_.excludeAreas().length())); + for (int i = 0; i < request_.excludeAreas().length(); ++i) { + const QGeoRectangle &r = request_.excludeAreas().at(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(r))); + excludedAreasArray->putIndexed(i, cv); + } + + return QJSValue(v4, excludedAreasArray.asReturnedValue()); +} + +void QDeclarativeGeoRouteQuery::setExcludedAreas(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList excludedAreasList; + quint32 length = value.property(QStringLiteral("length")).toUInt(); + for (quint32 i = 0; i < length; ++i) { + bool ok; + QGeoRectangle r = parseRectangle(value.property(i), &ok); + + if (!ok || !r.isValid()) { + qmlWarning(this) << QStringLiteral("Unsupported area type"); + return; + } + + excludedAreasList.append(r); + } + + if (request_.excludeAreas() == excludedAreasList) + return; + + request_.setExcludeAreas(excludedAreasList); + + if (complete_) { + emit excludedAreasChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::addExcludedArea(georectangle) + + Adds the given area to excluded areas (areas that the route must not cross). + Same area can only be added once. + + \sa removeExcludedArea, clearExcludedAreas +*/ + + +void QDeclarativeGeoRouteQuery::addExcludedArea(const QGeoRectangle &area) +{ + if (!area.isValid()) + return; + + QList excludedAreas = request_.excludeAreas(); + + if (excludedAreas.contains(area)) + return; + + excludedAreas.append(area); + + request_.setExcludeAreas(excludedAreas); + + if (complete_) { + emit excludedAreasChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::removeExcludedArea(georectangle) + + Removes the given area to excluded areas (areas that the route must not cross). + + \sa addExcludedArea, clearExcludedAreas +*/ + +void QDeclarativeGeoRouteQuery::removeExcludedArea(const QGeoRectangle &area) +{ + if (!area.isValid()) + return; + + QList excludedAreas = request_.excludeAreas(); + + int index = excludedAreas.lastIndexOf(area); + if (index == -1) { + qmlWarning(this) << QStringLiteral("Cannot remove nonexistent area."); + return; + } + excludedAreas.removeAt(index); + request_.setExcludeAreas(excludedAreas); + + if (complete_) { + emit excludedAreasChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::clearExcludedAreas() + + Clears all excluded areas (areas that the route must not cross). + + \sa addExcludedArea, removeExcludedArea +*/ + +void QDeclarativeGeoRouteQuery::clearExcludedAreas() +{ + if (request_.excludeAreas().isEmpty()) + return; + + request_.setExcludeAreas(QList()); + + if (complete_) { + emit excludedAreasChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::addWaypoint(coordinate) + + Appends a coordinate to the list of waypoints. Same coordinate + can be set multiple times. + The \a coordinate argument can be a \l coordinate or a \l Waypoint. + If a \l coordinate is used, it will be internally converted to a + \l Waypoint. + + \sa removeWaypoint, clearWaypoints +*/ +void QDeclarativeGeoRouteQuery::addWaypoint(const QVariant &waypoint) +{ + QDeclarativeGeoWaypoint *w = nullptr; + QObject *obj = qvariant_cast(waypoint); + w = qobject_cast(obj); + + if (w) { + if (! w->isValid()) { + qmlWarning(this) << QStringLiteral("Invalid waypoint"); + return; + } + + m_waypoints << w; + connect(w, &QDeclarativeGeoWaypoint::waypointDetailsChanged, this, &QDeclarativeGeoRouteQuery::waypointChanged); + waypointChanged(); + return; + } + + // if here, waypoint is not a Waypoint, so either a QGeoCoordinate or a variant map, so a waypoint has to be instantiated. + + QGeoCoordinate c = parseCoordinate(waypoint); + if (!c.isValid()) { + qmlWarning(this) << QStringLiteral("Invalid coordinate as waypoint"); + return; + } + + w = new QDeclarativeGeoWaypoint(this); + w->setCoordinate(c); + m_waypoints << w; + connect(w, &QDeclarativeGeoWaypoint::waypointDetailsChanged, this, &QDeclarativeGeoRouteQuery::waypointChanged); + waypointChanged(); +} + +/*! + \qmlmethod void QtLocation::RouteQuery::removeWaypoint(coordinate) + + Removes the given from the list of waypoints. In case same coordinate + appears multiple times, the most recently added coordinate instance is + removed. + + \sa addWaypoint, clearWaypoints +*/ +void QDeclarativeGeoRouteQuery::removeWaypoint(const QVariant &waypoint) +{ + QDeclarativeGeoWaypoint *w = nullptr; + QObject *obj = qvariant_cast(waypoint); + w = qobject_cast(obj); + + if (w) { + if (!w->isValid()) { + qmlWarning(this) << QStringLiteral("Invalid waypoint"); + return; + } + + int idx = findWaypoint(m_waypoints, w); + if (idx >= 0) { + QDeclarativeGeoWaypoint *toRemove = m_waypoints.takeAt(idx); + toRemove->disconnect(this); + if (toRemove->parent() == this) + delete toRemove; + + waypointChanged(); + } else { + qmlWarning(this) << QStringLiteral("Cannot remove nonexistent waypoint."); + } + return; + } + + QGeoCoordinate c = parseCoordinate(waypoint); + if (!c.isValid()) { + qmlWarning(this) << QStringLiteral("Invalid coordinate as waypoint"); + return; + } + + int idx = findWaypoint(m_waypoints, c); + if (idx >= 0) { + QDeclarativeGeoWaypoint *toRemove = m_waypoints.takeAt(idx); + toRemove->disconnect(this); + if (toRemove->parent() == this) + delete toRemove; + + waypointChanged(); + } else { + qmlWarning(this) << QStringLiteral("Cannot remove nonexistent waypoint."); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::clearWaypoints() + + Clears all waypoints. + + \sa removeWaypoint, addWaypoint +*/ +void QDeclarativeGeoRouteQuery::clearWaypoints() +{ + if (m_waypoints.isEmpty()) + return; + + flushWaypoints(m_waypoints); + waypointChanged(); +} + +void QDeclarativeGeoRouteQuery::flushWaypoints(QList &waypoints) +{ + for (const QDeclarativeGeoWaypoint *w : qAsConst(waypoints)) { + w->disconnect(this); + if (w->parent() == this) // w has been created internally as a result of adding a QGeoCoordinate + delete w; + } + waypoints.clear(); +} + +/*! + \qmlmethod void QtLocation::RouteQuery::setFeatureWeight(FeatureType, FeatureWeight) + + Defines the weight to associate with a feature during the planning of a + route. + + Following lists the possible feature weights: + + \value RouteQuery.NeutralFeatureWeight + The presence or absence of the feature will not affect the planning of the route + + \value RouteQuery.PreferFeatureWeight + Routes which contain the feature will be preferred over those that do not + + \value RouteQuery.RequireFeatureWeight + Only routes which contain the feature will be considered, otherwise no route will be returned + + \value RouteQuery.AvoidFeatureWeight + Routes which do not contain the feature will be preferred over those that do + + \value RouteQuery.DisallowFeatureWeight + Only routes which do not contain the feature will be considered, otherwise no route will be returned + + \sa featureTypes, resetFeatureWeights, featureWeight + +*/ + +void QDeclarativeGeoRouteQuery::setFeatureWeight(FeatureType featureType, FeatureWeight featureWeight) +{ + if (featureType == NoFeature && !request_.featureTypes().isEmpty()) { + resetFeatureWeights(); + return; + } + + // Check if the weight changes, as we need to signal it + FeatureWeight originalWeight = static_cast(request_.featureWeight(static_cast(featureType))); + if (featureWeight == originalWeight) + return; + + request_.setFeatureWeight(static_cast(featureType), + static_cast(featureWeight)); + if (complete_ && ((originalWeight == NeutralFeatureWeight) || (featureWeight == NeutralFeatureWeight))) { + // featureTypes should now give a different list, because the original and new weight + // were not same, and other one was neutral weight + emit featureTypesChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod void QtLocation::RouteQuery::resetFeatureWeights() + + Resets all feature weights to their default state (NeutralFeatureWeight). + + \sa featureTypes, setFeatureWeight, featureWeight +*/ +void QDeclarativeGeoRouteQuery::resetFeatureWeights() +{ + // reset all feature types. + QList featureTypes = request_.featureTypes(); + for (int i = 0; i < featureTypes.count(); ++i) { + request_.setFeatureWeight(featureTypes.at(i), QGeoRouteRequest::NeutralFeatureWeight); + } + if (complete_) { + emit featureTypesChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \qmlmethod FeatureWeight QtLocation::RouteQuery::featureWeight(FeatureType featureType) + + Gets the weight for the \a featureType. + + \sa featureTypes, setFeatureWeight, resetFeatureWeights +*/ + +int QDeclarativeGeoRouteQuery::featureWeight(FeatureType featureType) +{ + return request_.featureWeight(static_cast(featureType)); +} + +/*! + \internal +*/ +void QDeclarativeGeoRouteQuery::setTravelModes(QDeclarativeGeoRouteQuery::TravelModes travelModes) +{ + QGeoRouteRequest::TravelModes reqTravelModes; + + if (travelModes & QDeclarativeGeoRouteQuery::CarTravel) + reqTravelModes |= QGeoRouteRequest::CarTravel; + if (travelModes & QDeclarativeGeoRouteQuery::PedestrianTravel) + reqTravelModes |= QGeoRouteRequest::PedestrianTravel; + if (travelModes & QDeclarativeGeoRouteQuery::BicycleTravel) + reqTravelModes |= QGeoRouteRequest::BicycleTravel; + if (travelModes & QDeclarativeGeoRouteQuery::PublicTransitTravel) + reqTravelModes |= QGeoRouteRequest::PublicTransitTravel; + if (travelModes & QDeclarativeGeoRouteQuery::TruckTravel) + reqTravelModes |= QGeoRouteRequest::TruckTravel; + + if (reqTravelModes == request_.travelModes()) + return; + + request_.setTravelModes(reqTravelModes); + + if (complete_) { + emit travelModesChanged(); + emit queryDetailsChanged(); + } +} + + +/*! + \qmlproperty enumeration RouteQuery::segmentDetail + + The level of detail which will be used in the representation of routing segments. + + \value RouteQuery.NoSegmentData + No segment data should be included with the route + + \value RouteQuery.BasicSegmentData + Basic segment data will be included with the route + + The default value is \c {RouteQuery.BasicSegmentData}. +*/ + +void QDeclarativeGeoRouteQuery::setSegmentDetail(SegmentDetail segmentDetail) +{ + if (static_cast(segmentDetail) == request_.segmentDetail()) + return; + request_.setSegmentDetail(static_cast(segmentDetail)); + if (complete_) { + emit segmentDetailChanged(); + emit queryDetailsChanged(); + } +} + +QDeclarativeGeoRouteQuery::SegmentDetail QDeclarativeGeoRouteQuery::segmentDetail() const +{ + return static_cast(request_.segmentDetail()); +} + +/*! + \qmlproperty enumeration RouteQuery::maneuverDetail + + The level of detail which will be used in the representation of routing maneuvers. + + \value RouteQuery.NoManeuvers + No maneuvers should be included with the route + + \value RouteQuery.BasicManeuvers + Basic maneuvers will be included with the route + + The default value is \c {RouteQuery.BasicManeuvers}. +*/ + +void QDeclarativeGeoRouteQuery::setManeuverDetail(ManeuverDetail maneuverDetail) +{ + if (static_cast(maneuverDetail) == request_.maneuverDetail()) + return; + request_.setManeuverDetail(static_cast(maneuverDetail)); + if (complete_) { + emit maneuverDetailChanged(); + emit queryDetailsChanged(); + } +} + +QDeclarativeGeoRouteQuery::ManeuverDetail QDeclarativeGeoRouteQuery::maneuverDetail() const +{ + return static_cast(request_.maneuverDetail()); +} + +/*! + \qmlproperty enumeration RouteQuery::travelModes + + The travel modes which should be considered during the planning of the route. + Values can be combined with OR ('|') -operator. + + \value RouteQuery.CarTravel + The route will be optimized for someone who is driving a car + + \value RouteQuery.PedestrianTravel + The route will be optimized for someone who is walking + + \value RouteQuery.BicycleTravel + The route will be optimized for someone who is riding a bicycle + + \value RouteQuery.PublicTransit + Travel The route will be optimized for someone who is making use of public transit + + \value RouteQuery.TruckTravel + The route will be optimized for someone who is driving a truck + + The default value is \c {RouteQuery.CarTravel}. +*/ + +QDeclarativeGeoRouteQuery::TravelModes QDeclarativeGeoRouteQuery::travelModes() const +{ + QGeoRouteRequest::TravelModes reqTravelModes = request_.travelModes(); + QDeclarativeGeoRouteQuery::TravelModes travelModes; + + if (reqTravelModes & QGeoRouteRequest::CarTravel) + travelModes |= QDeclarativeGeoRouteQuery::CarTravel; + if (reqTravelModes & QGeoRouteRequest::PedestrianTravel) + travelModes |= QDeclarativeGeoRouteQuery::PedestrianTravel; + if (reqTravelModes & QGeoRouteRequest::BicycleTravel) + travelModes |= QDeclarativeGeoRouteQuery::BicycleTravel; + if (reqTravelModes & QGeoRouteRequest::PublicTransitTravel) + travelModes |= QDeclarativeGeoRouteQuery::PublicTransitTravel; + if (reqTravelModes & QGeoRouteRequest::TruckTravel) + travelModes |= QDeclarativeGeoRouteQuery::TruckTravel; + + return travelModes; +} + +/*! + \qmlproperty enumeration RouteQuery::routeOptimizations + + The route optimizations which should be considered during the planning of the route. + Values can be combined with OR ('|') -operator. + + + \value RouteQuery.ShortestRoute + Minimize the length of the journey + + \value RouteQuery.FastestRoute + Minimize the traveling time for the journey + + \value RouteQuery.MostEconomicRoute + Minimize the cost of the journey + + \value RouteQuery.MostScenicRoute + Maximize the scenic potential of the journey + + The default value is \c {RouteQuery.FastestRoute}. +*/ + +QDeclarativeGeoRouteQuery::RouteOptimizations QDeclarativeGeoRouteQuery::routeOptimizations() const +{ + QGeoRouteRequest::RouteOptimizations reqOptimizations = request_.routeOptimization(); + QDeclarativeGeoRouteQuery::RouteOptimizations optimization; + + if (reqOptimizations & QGeoRouteRequest::ShortestRoute) + optimization |= QDeclarativeGeoRouteQuery::ShortestRoute; + if (reqOptimizations & QGeoRouteRequest::FastestRoute) + optimization |= QDeclarativeGeoRouteQuery::FastestRoute; + if (reqOptimizations & QGeoRouteRequest::MostEconomicRoute) + optimization |= QDeclarativeGeoRouteQuery::MostEconomicRoute; + if (reqOptimizations & QGeoRouteRequest::MostScenicRoute) + optimization |= QDeclarativeGeoRouteQuery::MostScenicRoute; + + return optimization; +} + +void QDeclarativeGeoRouteQuery::setRouteOptimizations(QDeclarativeGeoRouteQuery::RouteOptimizations optimization) +{ + QGeoRouteRequest::RouteOptimizations reqOptimizations; + + if (optimization & QDeclarativeGeoRouteQuery::ShortestRoute) + reqOptimizations |= QGeoRouteRequest::ShortestRoute; + if (optimization & QDeclarativeGeoRouteQuery::FastestRoute) + reqOptimizations |= QGeoRouteRequest::FastestRoute; + if (optimization & QDeclarativeGeoRouteQuery::MostEconomicRoute) + reqOptimizations |= QGeoRouteRequest::MostEconomicRoute; + if (optimization & QDeclarativeGeoRouteQuery::MostScenicRoute) + reqOptimizations |= QGeoRouteRequest::MostScenicRoute; + + if (reqOptimizations == request_.routeOptimization()) + return; + + request_.setRouteOptimization(reqOptimizations); + + if (complete_) { + emit routeOptimizationsChanged(); + emit queryDetailsChanged(); + } +} + +/*! + \internal +*/ +QGeoRouteRequest QDeclarativeGeoRouteQuery::routeRequest() +{ + if (m_extraParametersChanged) { + m_extraParametersChanged = false; + // Update extra params into request + const QList params = quickChildren(); + QVariantMap extraParameters; + for (const QDeclarativeGeoMapParameter *p: params) + extraParameters[p->type()] = p->toVariantMap(); + request_.setExtraParameters(extraParameters); + } + if (m_waypointsChanged) { + m_waypointsChanged = false; + // Update waypoints and metadata into request + request_.setWaypoints(waypointCoordinates(m_waypoints)); + request_.setWaypointsMetadata(waypointMetadata(m_waypoints)); + } + return request_; +} + + +/*! + \qmlproperty VariantMap RouteQuery::extraParameters + \readonly + + The route query extra parameters. This property is read only. If the query is + defined by the user, these can be set by using MapParameters. + If the route query comes from the engine via signals, the query is intended to be read-only. + + \since 5.11 +*/ +QVariantMap QDeclarativeGeoRouteQuery::extraParameters() +{ + return routeRequest().extraParameters(); +} + +void QDeclarativeGeoRouteQuery::excludedAreaCoordinateChanged() +{ + if (!m_excludedAreaCoordinateChanged) { + m_excludedAreaCoordinateChanged = true; + QMetaObject::invokeMethod(this, "doCoordinateChanged", Qt::QueuedConnection); + } +} + +void QDeclarativeGeoRouteQuery::extraParameterChanged() +{ + m_extraParametersChanged = true; + if (complete_) { + emit extraParametersChanged(); + emit queryDetailsChanged(); + } +} + +void QDeclarativeGeoRouteQuery::waypointChanged() +{ + m_waypointsChanged = true; + if (complete_) { + emit waypointsChanged(); + emit queryDetailsChanged(); + } +} + +void QDeclarativeGeoRouteQuery::append(QQmlListProperty *p, QObject *v) +{ + QDeclarativeGeoRouteQuery *query = static_cast(p->object); + query->m_children.append(v); + + QDeclarativeGeoMapParameter *param = qobject_cast(v); + if (param) { + query->m_extraParametersChanged = true; + query->connect(param, &QGeoMapParameter::propertyUpdated, + query, &QDeclarativeGeoRouteQuery::extraParameterChanged); + if (query->complete_) { + emit query->extraParametersChanged(); + emit query->queryDetailsChanged(); + } + } +} + +int QDeclarativeGeoRouteQuery::count(QQmlListProperty *p) +{ + return static_cast(p->object)->m_children.count(); +} + +QObject *QDeclarativeGeoRouteQuery::at(QQmlListProperty *p, int idx) +{ + return static_cast(p->object)->m_children.at(idx); +} + +void QDeclarativeGeoRouteQuery::clear(QQmlListProperty *p) +{ + QDeclarativeGeoRouteQuery *query = static_cast(p->object); + for (auto kid : qAsConst(query->m_children)) { + auto val = qobject_cast(kid); + if (val) { + val->disconnect(val, nullptr, query, nullptr); + query->m_extraParametersChanged = true; + } + } + query->m_children.clear(); + if (query->m_extraParametersChanged && query->complete_) { + emit query->extraParametersChanged(); + emit query->queryDetailsChanged(); + } +} + +QQmlListProperty QDeclarativeGeoRouteQuery::declarativeChildren() +{ + return QQmlListProperty(this, nullptr, + &QDeclarativeGeoRouteQuery::append, + &QDeclarativeGeoRouteQuery::count, + &QDeclarativeGeoRouteQuery::at, + &QDeclarativeGeoRouteQuery::clear); +} + +void QDeclarativeGeoRouteQuery::doCoordinateChanged() +{ + m_excludedAreaCoordinateChanged = false; + if (complete_) + emit queryDetailsChanged(); +} + +/*! + \qmltype Waypoint + \instantiates QDeclarativeGeoWaypoint + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since QtLocation 5.11 + + \brief The Waypoint type provides a mean to specify a waypoint in a \l RouteQuery + in a more detailed way than by using a simple \l coordinate. + + A Waypoint is a type that allows to specify properties of a waypoint in a \l RouteQuery, + such as the waypoint coordinate, or the angle of approach to the waypoint. + + Additional information that are backend-specific can be specified by nesting \l MapParameter + elements. + + Changing properties of the waypoint or of its nested MapParameteters will cause the containing + \l RouteQuery to emit the queryDetailsChanged signal. + + \section2 Example Usage + + \code + Plugin { + id: aPlugin + name: "osm" + } + + Waypoint { + id: waypointStart + coordinate: ... + bearing: ... + } + Waypoint { + id: waypointFinish + coordinate: ... + bearing: ... + } + + RouteQuery { + id: aQuery + Component.onCompleted: { + travelModes = RouteQuery.CarTravel + addWaypoint(waypointStart) + var aWaypoint = Qt.createQmlObject ('import QtLocation 5.11; Waypoint { ... }', ...) + addWaypoint(aWaypoint) + addWaypoint(waypointFinish) + } + } + + RouteModel { + id: routeModel + plugin: aPlugin + query: aQuery + autoUpdate: true + } + \endcode + + \sa RouteQuery +*/ + + +/* + * + At the time of adding this class (2017.11), 3 routing services are natively supported in Qt: Esri, Here and OSRM. + Waypoint documentation for each of these: + Esri: http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 , called "stop" + HERE: https://developer.here.com/documentation/routing/topics/resource-param-type-waypoint.html + OSRM: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md , under Request Options + * + */ + + +static QGeoCoordinate convertWaypointToCoordinate(const QDeclarativeGeoWaypoint *value) +{ + return value->coordinate(); +} + +struct WaypointVariantConversions +{ + WaypointVariantConversions() + { + QMetaType::registerConverter(convertWaypointToCoordinate); + } +}; + +Q_GLOBAL_STATIC(WaypointVariantConversions, initWaypointConversions) + + +QDeclarativeGeoWaypoint::QDeclarativeGeoWaypoint(QObject *parent) : QGeoCoordinateObject(parent) +{ + initWaypointConversions(); + connect(this, &QGeoCoordinateObject::coordinateChanged, + this, &QDeclarativeGeoWaypoint::waypointDetailsChanged); +} + +QDeclarativeGeoWaypoint::~QDeclarativeGeoWaypoint() +{ + +} + +bool QDeclarativeGeoWaypoint::operator==(const QDeclarativeGeoWaypoint &other) const +{ + const QList params = quickChildren(); + const QList otherParams = other.quickChildren(); + + return coordinate() == other.coordinate() && + compareFloats(m_bearing, other.bearing()) && + compareParameterList(params, otherParams); +} + +/*! + \qmlproperty coordinate Waypoint::coordinate + + The waypoint's coordinate. The default value is undefined. +*/ + + +/*! + \qmlproperty real Waypoint::latitude + + The latitude of the waypoint's coordinate. The default value is NaN. + Changing this property will affect the \l Waypoint::coordinate property as well. +*/ +qreal QDeclarativeGeoWaypoint::latitude() const +{ + return m_coordinate.latitude(); +} + +void QDeclarativeGeoWaypoint::setLatitude(qreal latitude) +{ + if (compareFloats(latitude, m_coordinate.latitude())) + return; + + m_coordinate.setLatitude(latitude); + if (m_complete) { + emit coordinateChanged(); + emit waypointDetailsChanged(); + } +} + +/*! + \qmlproperty real Waypoint::longitude + + The longitude of the waypoint's coordinate. The default value is NaN. + Changing this property will affect the \l Waypoint::coordinate property as well. +*/ +qreal QDeclarativeGeoWaypoint::longitude() const +{ + return m_coordinate.longitude(); +} + +void QDeclarativeGeoWaypoint::setLongitude(qreal longitude) +{ + if (compareFloats(longitude, m_coordinate.longitude())) + return; + + m_coordinate.setLongitude(longitude); + if (m_complete) { + emit coordinateChanged(); + emit waypointDetailsChanged(); + } +} + +/*! + \qmlproperty real Waypoint::altitude + + The altitude of the waypoint's coordinate. The default value is NaN. + Changing this property will affect the \l Waypoint::coordinate property as well. +*/ +qreal QDeclarativeGeoWaypoint::altitude() const +{ + return m_coordinate.altitude(); +} + +void QDeclarativeGeoWaypoint::setAltitude(qreal altitude) +{ + if (compareFloats(altitude, m_coordinate.altitude())) + return; + + m_coordinate.setAltitude(altitude); + if (m_complete) { + emit coordinateChanged(); + emit waypointDetailsChanged(); + } +} + +bool QDeclarativeGeoWaypoint::isValid() const +{ + return m_coordinate.isValid(); +} + +/*! + \qmlproperty real Waypoint::bearing + + The bearing specifying the angle of approach of the waypoint, that is the bearing with which the waypoint is to be approached. + This information may be used by the provider to filter the road segment the waypoint will be placed on, and, + depending on the provider and the \l {QGeoRouteRequest::TravelMode} {travel mode} used, to restrict the maneuvers + allowed at the waypoint, potentially making the provider calculating and returning a different route. + + If set to NaN, this value will not be considered. + + The default value is NaN. +*/ +qreal QDeclarativeGeoWaypoint::bearing() const +{ + return m_bearing; +} + +void QDeclarativeGeoWaypoint::setBearing(qreal bearing) +{ + if (compareFloats(bearing, m_bearing)) + return; + + m_bearing = bearing; + + // Bearing is actually packed into QGeoRouteRequest::waypointMetadata() together with the extra parameters + m_metadataChanged = true; + if (m_complete) { + emit bearingChanged(); + emit waypointDetailsChanged(); + } +} + +/*! + \qmlproperty VariantMap Waypoint::metadata + \readonly + + The waypoint metadata. This property is read only. If the waypoint is + defined by the user, these can be set by using MapParameters. + If the waypoint comes from the engine via signals, or as part of a read-only route query, + the waypoint is intended to be read-only. +*/ +QVariantMap QDeclarativeGeoWaypoint::metadata() +{ + if (m_metadataChanged) { + m_metadataChanged = false; + m_metadata.clear(); + // Update metadata + const QList params = quickChildren(); + QVariantMap extraParameters; + for (const QDeclarativeGeoMapParameter *p: params) + extraParameters[p->type()] = p->toVariantMap(); + m_metadata[QStringLiteral("extra")] = extraParameters; + m_metadata[QStringLiteral("bearing")] = m_bearing; + } + return m_metadata; +} + +// Used only by QDeclarativeGeoRouteRequest +void QDeclarativeGeoWaypoint::setMetadata(const QVariantMap &meta) +{ + m_metadata = meta; + if (m_metadata.contains(QStringLiteral("bearing")) && m_metadata.value(QStringLiteral("bearing")).canConvert()) + m_bearing = m_metadata.value(QStringLiteral("bearing")).toDouble(); + m_metadataChanged = false; +} + +void QDeclarativeGeoWaypoint::extraParameterChanged() +{ + m_metadataChanged = true; + if (m_complete) { + emit extraParametersChanged(); + emit waypointDetailsChanged(); + } +} + +void QDeclarativeGeoWaypoint::append(QQmlListProperty *p, QObject *v) +{ + QDeclarativeGeoWaypoint *waypoint = static_cast(p->object); + waypoint->m_children.append(v); + + QDeclarativeGeoMapParameter *param = qobject_cast(v); + if (param) { + waypoint->connect(param, &QGeoMapParameter::propertyUpdated, + waypoint, &QDeclarativeGeoWaypoint::extraParameterChanged); + waypoint->extraParameterChanged(); + } +} + +int QDeclarativeGeoWaypoint::count(QQmlListProperty *p) +{ + return static_cast(p->object)->m_children.count(); +} + +QObject *QDeclarativeGeoWaypoint::at(QQmlListProperty *p, int idx) +{ + return static_cast(p->object)->m_children.at(idx); +} + +void QDeclarativeGeoWaypoint::clear(QQmlListProperty *p) +{ + QDeclarativeGeoWaypoint *waypoint = static_cast(p->object); + for (auto kid : qAsConst(waypoint->m_children)) { + auto val = qobject_cast(kid); + if (val) { + val->disconnect(waypoint); + waypoint->m_metadataChanged = true; + } + } + waypoint->m_children.clear(); + if (waypoint->m_metadataChanged && waypoint->m_complete) { + emit waypoint->extraParametersChanged(); + emit waypoint->waypointDetailsChanged(); + } +} + +QQmlListProperty QDeclarativeGeoWaypoint::declarativeChildren() +{ + return QQmlListProperty(this, nullptr, + &QDeclarativeGeoWaypoint::append, + &QDeclarativeGeoWaypoint::count, + &QDeclarativeGeoWaypoint::at, + &QDeclarativeGeoWaypoint::clear); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeoroutemodel_p.h b/src/location/declarativemaps/qdeclarativegeoroutemodel_p.h new file mode 100644 index 0000000..afffc21 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutemodel_p.h @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOROUTEMODEL_H +#define QDECLARATIVEGEOROUTEMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoServiceProvider; +class QGeoRoutingManager; +class QDeclarativeGeoRoute; +class QDeclarativeGeoRouteQuery; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRouteModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(Status) + Q_ENUMS(RouteError) + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QDeclarativeGeoRouteQuery *query READ query WRITE setQuery NOTIFY queryChanged) + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate NOTIFY autoUpdateChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + Q_PROPERTY(QString errorString READ errorString NOTIFY errorChanged) + Q_PROPERTY(RouteError error READ error NOTIFY errorChanged) + Q_PROPERTY(QLocale::MeasurementSystem measurementSystem READ measurementSystem WRITE setMeasurementSystem NOTIFY measurementSystemChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + enum Roles { + RouteRole = Qt::UserRole + 500 + }; + + enum Status { + Null, + Ready, + Loading, + Error + }; + + enum RouteError { + NoError = QGeoRouteReply::NoError, + EngineNotSetError = QGeoRouteReply::EngineNotSetError, + CommunicationError = QGeoRouteReply::CommunicationError, + ParseError = QGeoRouteReply::ParseError, + UnsupportedOptionError = QGeoRouteReply::UnsupportedOptionError, + UnknownError = QGeoRouteReply::UnknownError, + //we leave gap for future QGeoRouteReply errors + + //QGeoServiceProvider related errors start here + UnknownParameterError = 100, + MissingRequiredParameterError + }; + + explicit QDeclarativeGeoRouteModel(QObject *parent = 0); + ~QDeclarativeGeoRouteModel(); + + // From QQmlParserStatus + void classBegin() {} + void componentComplete(); + + // From QAbstractListModel + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + virtual QHash roleNames() const; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setQuery(QDeclarativeGeoRouteQuery *query); + QDeclarativeGeoRouteQuery *query() const; + + void setAutoUpdate(bool autoUpdate); + bool autoUpdate() const; + + void setMeasurementSystem(QLocale::MeasurementSystem ms); + QLocale::MeasurementSystem measurementSystem() const; + + Status status() const; + QString errorString() const; + RouteError error() const; + + int count() const; + Q_INVOKABLE QDeclarativeGeoRoute *get(int index); + Q_INVOKABLE void reset(); + Q_INVOKABLE void cancel(); + +Q_SIGNALS: + void countChanged(); + void pluginChanged(); + void queryChanged(); + void autoUpdateChanged(); + void statusChanged(); + void errorChanged(); //emitted also for errorString notification + void routesChanged(); + void measurementSystemChanged(); + void abortRequested(); + +public Q_SLOTS: + void update(); + +private Q_SLOTS: + void routingFinished(QGeoRouteReply *reply); + void routingError(QGeoRouteReply *reply, + QGeoRouteReply::Error error, + const QString &errorString); + void queryDetailsChanged(); + void pluginReady(); + +private: + void setStatus(Status status); + void setError(RouteError error, const QString &errorString); + + bool complete_; + + QDeclarativeGeoServiceProvider *plugin_; + QDeclarativeGeoRouteQuery *routeQuery_; + + QList routes_; + bool autoUpdate_; + Status status_; + QString errorString_; + RouteError error_; +}; + + + +// purpose of this class is to be convertible to a QGeoCoordinate (through QGeoWaypoint), but also +// to behave like it, so that in QML source compatibility would be preserved. This is, however, not possible to achieve at the present. +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoWaypoint : public QGeoCoordinateObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(double latitude READ latitude WRITE setLatitude STORED false) + Q_PROPERTY(double longitude READ longitude WRITE setLongitude STORED false) + Q_PROPERTY(double altitude READ altitude WRITE setAltitude STORED false) + Q_PROPERTY(bool isValid READ isValid STORED false) + + Q_PROPERTY(qreal bearing READ bearing WRITE setBearing NOTIFY bearingChanged) + Q_PROPERTY(QVariantMap metadata READ metadata) + Q_PROPERTY(QQmlListProperty quickChildren READ declarativeChildren DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "quickChildren") + Q_INTERFACES(QQmlParserStatus) + +public: + QDeclarativeGeoWaypoint(QObject *parent = 0); + virtual ~QDeclarativeGeoWaypoint(); + + bool operator==(const QDeclarativeGeoWaypoint &other) const; + + qreal latitude() const; + void setLatitude(qreal latitude); + + qreal longitude() const; + void setLongitude(qreal longitude); + + qreal altitude() const; + void setAltitude(qreal altitude); + + bool isValid() const; + + qreal bearing() const; + void setBearing(qreal bearing); + + template + QList quickChildren() const + { + QList res; + for (auto kid : qAsConst(m_children)) { + auto val = qobject_cast(kid); + if (val) + res.push_back(val); + } + return res; + } + + QVariantMap metadata(); + void setMetadata(const QVariantMap &meta); + +Q_SIGNALS: + void completed(); + void waypointDetailsChanged(); + void bearingChanged(); + void extraParametersChanged(); + +private Q_SLOTS: + void extraParameterChanged(); + +protected: + // From QQmlParserStatus + void classBegin() override {} + void componentComplete() override { m_complete = true; emit completed(); } + + // For quickChildren + static void append(QQmlListProperty *p, QObject *v); + static int count(QQmlListProperty *p); + static QObject *at(QQmlListProperty *p, int idx); + static void clear(QQmlListProperty *p); + QQmlListProperty declarativeChildren(); + QList m_children; + + // other data members + bool m_metadataChanged = false; + bool m_complete = false; + + qreal m_bearing = Q_QNAN; + QVariantMap m_metadata; +}; + + + + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRouteQuery : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(TravelMode) + Q_ENUMS(FeatureType) + Q_ENUMS(FeatureWeight) + Q_ENUMS(SegmentDetail) + Q_ENUMS(ManeuverDetail) + Q_ENUMS(RouteOptimization) + Q_FLAGS(RouteOptimizations) + Q_FLAGS(ManeuverDetails) + Q_FLAGS(SegmentDetails) + Q_FLAGS(TravelModes) + + Q_PROPERTY(int numberAlternativeRoutes READ numberAlternativeRoutes WRITE setNumberAlternativeRoutes NOTIFY numberAlternativeRoutesChanged) + Q_PROPERTY(TravelModes travelModes READ travelModes WRITE setTravelModes NOTIFY travelModesChanged) + Q_PROPERTY(RouteOptimizations routeOptimizations READ routeOptimizations WRITE setRouteOptimizations NOTIFY routeOptimizationsChanged) + Q_PROPERTY(SegmentDetail segmentDetail READ segmentDetail WRITE setSegmentDetail NOTIFY segmentDetailChanged) + Q_PROPERTY(ManeuverDetail maneuverDetail READ maneuverDetail WRITE setManeuverDetail NOTIFY maneuverDetailChanged) + Q_PROPERTY(QVariantList waypoints READ waypoints WRITE setWaypoints NOTIFY waypointsChanged) + Q_PROPERTY(QJSValue excludedAreas READ excludedAreas WRITE setExcludedAreas NOTIFY excludedAreasChanged) + Q_PROPERTY(QList featureTypes READ featureTypes NOTIFY featureTypesChanged) + Q_PROPERTY(QVariantMap extraParameters READ extraParameters REVISION 11) + Q_PROPERTY(QQmlListProperty quickChildren READ declarativeChildren DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "quickChildren") + Q_INTERFACES(QQmlParserStatus) + +public: + + explicit QDeclarativeGeoRouteQuery(QObject *parent = 0); + QDeclarativeGeoRouteQuery(const QGeoRouteRequest &request, QObject *parent = 0); // init from request. For instances intended to be read only + ~QDeclarativeGeoRouteQuery(); + + // From QQmlParserStatus + void classBegin() {} + void componentComplete(); + + QGeoRouteRequest routeRequest(); + QVariantMap extraParameters(); + + enum TravelMode { + CarTravel = QGeoRouteRequest::CarTravel, + PedestrianTravel = QGeoRouteRequest::PedestrianTravel, + BicycleTravel = QGeoRouteRequest::BicycleTravel, + PublicTransitTravel = QGeoRouteRequest::PublicTransitTravel, + TruckTravel = QGeoRouteRequest::TruckTravel + }; + Q_DECLARE_FLAGS(TravelModes, TravelMode) + + enum FeatureType { + NoFeature = QGeoRouteRequest::NoFeature, + TollFeature = QGeoRouteRequest::TollFeature, + HighwayFeature = QGeoRouteRequest::HighwayFeature, + PublicTransitFeature = QGeoRouteRequest::PublicTransitFeature, + FerryFeature = QGeoRouteRequest::FerryFeature, + TunnelFeature = QGeoRouteRequest::TunnelFeature, + DirtRoadFeature = QGeoRouteRequest::DirtRoadFeature, + ParksFeature = QGeoRouteRequest::ParksFeature, + MotorPoolLaneFeature = QGeoRouteRequest::MotorPoolLaneFeature, + TrafficFeature = QGeoRouteRequest::TrafficFeature + }; + Q_DECLARE_FLAGS(FeatureTypes, FeatureType) + + enum FeatureWeight { + NeutralFeatureWeight = QGeoRouteRequest::NeutralFeatureWeight, + PreferFeatureWeight = QGeoRouteRequest::PreferFeatureWeight, + RequireFeatureWeight = QGeoRouteRequest::RequireFeatureWeight, + AvoidFeatureWeight = QGeoRouteRequest::AvoidFeatureWeight, + DisallowFeatureWeight = QGeoRouteRequest::DisallowFeatureWeight + }; + Q_DECLARE_FLAGS(FeatureWeights, FeatureWeight) + + enum RouteOptimization { + ShortestRoute = QGeoRouteRequest::ShortestRoute, + FastestRoute = QGeoRouteRequest::FastestRoute, + MostEconomicRoute = QGeoRouteRequest::MostEconomicRoute, + MostScenicRoute = QGeoRouteRequest::MostScenicRoute + }; + Q_DECLARE_FLAGS(RouteOptimizations, RouteOptimization) + + enum SegmentDetail { + NoSegmentData = 0x0000, + BasicSegmentData = 0x0001 + }; + Q_DECLARE_FLAGS(SegmentDetails, SegmentDetail) + + enum ManeuverDetail { + NoManeuvers = 0x0000, + BasicManeuvers = 0x0001 + }; + Q_DECLARE_FLAGS(ManeuverDetails, ManeuverDetail) + + void setNumberAlternativeRoutes(int numberAlternativeRoutes); + int numberAlternativeRoutes() const; + + //QList featureTypes(); + QList featureTypes(); + + + QVariantList waypoints(); + Q_INVOKABLE QVariantList waypointObjects(); + void setWaypoints(const QVariantList &value); + + // READ functions for list properties + QJSValue excludedAreas() const; + void setExcludedAreas(const QJSValue &value); + + Q_INVOKABLE void addWaypoint(const QVariant &w); + Q_INVOKABLE void removeWaypoint(const QVariant &waypoint); + Q_INVOKABLE void clearWaypoints(); + void flushWaypoints(QList &waypoints); + + Q_INVOKABLE void addExcludedArea(const QGeoRectangle &area); + Q_INVOKABLE void removeExcludedArea(const QGeoRectangle &area); + Q_INVOKABLE void clearExcludedAreas(); + + Q_INVOKABLE void setFeatureWeight(FeatureType featureType, FeatureWeight featureWeight); + Q_INVOKABLE int featureWeight(FeatureType featureType); + Q_INVOKABLE void resetFeatureWeights(); + + /* + feature weights + */ + + void setTravelModes(TravelModes travelModes); + TravelModes travelModes() const; + + void setSegmentDetail(SegmentDetail segmentDetail); + SegmentDetail segmentDetail() const; + + void setManeuverDetail(ManeuverDetail maneuverDetail); + ManeuverDetail maneuverDetail() const; + + void setRouteOptimizations(RouteOptimizations optimization); + RouteOptimizations routeOptimizations() const; + + template + QList quickChildren() const + { + QList res; + for (auto kid : qAsConst(m_children)) { + auto val = qobject_cast(kid); + if (val) + res.push_back(val); + } + return res; + } + +Q_SIGNALS: + void numberAlternativeRoutesChanged(); + void travelModesChanged(); + void routeOptimizationsChanged(); + + void waypointsChanged(); + void excludedAreasChanged(); + + void featureTypesChanged(); + void maneuverDetailChanged(); + void segmentDetailChanged(); + + void queryDetailsChanged(); + Q_REVISION(11) void extraParametersChanged(); + +private Q_SLOTS: + void excludedAreaCoordinateChanged(); + void extraParameterChanged(); + void waypointChanged(); + +protected: + static void append(QQmlListProperty *p, QObject *v); + static int count(QQmlListProperty *p); + static QObject *at(QQmlListProperty *p, int idx); + static void clear(QQmlListProperty *p); + + QQmlListProperty declarativeChildren(); + QList m_children; + +private: + Q_INVOKABLE void doCoordinateChanged(); + + QGeoRouteRequest request_; + bool complete_; + bool m_excludedAreaCoordinateChanged; + bool m_extraParametersChanged = false; + bool m_waypointsChanged = false; + QList m_waypoints; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QDeclarativeGeoWaypoint*) + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp b/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp new file mode 100644 index 0000000..5f6fda5 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutesegment.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeoroutesegment_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype RouteSegment + \instantiates QDeclarativeGeoRouteSegment + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-routing + \since QtLocation 5.5 + + \brief The RouteSegment type represents a segment of a Route. + + A RouteSegment instance has information about the physical layout + of the route segment, the length of the route and estimated time required + to traverse the route segment and optional RouteManeuvers associated with + the end of the route segment. + + RouteSegment instances can be thought of as edges on a routing + graph, with RouteManeuver instances as optional labels attached to the + vertices of the graph. + + The primary means of acquiring Route objects is via Routes via \l RouteModel. + + \section1 Example + + The following QML snippet demonstrates how to print information about a + route segment: + + \snippet declarative/routing.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/routing.qml RouteSegment +*/ + +QDeclarativeGeoRouteSegment::QDeclarativeGeoRouteSegment(QObject *parent) + : QObject(parent) +{ + maneuver_ = new QDeclarativeGeoManeuver(this); +} + +QDeclarativeGeoRouteSegment::QDeclarativeGeoRouteSegment(const QGeoRouteSegment &segment, + QObject *parent) + : QObject(parent), + segment_(segment) +{ + maneuver_ = new QDeclarativeGeoManeuver(segment_.maneuver(), this); +} + +QDeclarativeGeoRouteSegment::~QDeclarativeGeoRouteSegment() {} + +/*! + \qmlproperty int QtLocation::RouteSegment::travelTime + + Read-only property which holds the estimated amount of time it will take to + traverse this segment, in seconds. + +*/ + +int QDeclarativeGeoRouteSegment::travelTime() const +{ + return segment_.travelTime(); +} + +/*! + \qmlproperty real QtLocation::RouteSegment::distance + + Read-only property which holds the distance covered by this segment of the route, in meters. + +*/ + +qreal QDeclarativeGeoRouteSegment::distance() const +{ + return segment_.distance(); +} + +/*! + \qmlproperty RouteManeuver QtLocation::RouteSegment::maneuver + + Read-only property which holds the maneuver for this route segment. + + Will return invalid maneuver if no information has been attached to the endpoint + of this route segment. +*/ + +QDeclarativeGeoManeuver *QDeclarativeGeoRouteSegment::maneuver() const +{ + return maneuver_; +} + +/*! + \qmlproperty list QtLocation::RouteSegment::path + + Read-only property which holds the geographical coordinates of this segment. + Coordinates are listed in the order in which they would be traversed by someone + traveling along this segment of the route. + + To access individual segments you can use standard list accessors: 'path.length' + indicates the number of objects and 'path[index starting from zero]' gives + the actual object. + + \sa QtPositioning::coordinate +*/ + +QJSValue QDeclarativeGeoRouteSegment::path() const +{ + QQmlContext *context = QQmlEngine::contextForObject(parent()); + QQmlEngine *engine = context->engine(); + QV4::ExecutionEngine *v4 = QQmlEnginePrivate::getV4Engine(engine); + + QV4::Scope scope(v4); + QV4::Scoped pathArray(scope, v4->newArrayObject(segment_.path().length())); + for (int i = 0; i < segment_.path().length(); ++i) { + const QGeoCoordinate &c = segment_.path().at(i); + + QV4::ScopedValue cv(scope, v4->fromVariant(QVariant::fromValue(c))); + pathArray->putIndexed(i, cv); + } + + return QJSValue(v4, pathArray.asReturnedValue()); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativegeoroutesegment_p.h b/src/location/declarativemaps/qdeclarativegeoroutesegment_p.h new file mode 100644 index 0000000..c3203ef --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoroutesegment_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOROUTESEGMENT_H +#define QDECLARATIVEGEOROUTESEGMENT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoRouteSegment : public QObject +{ + Q_OBJECT + + Q_PROPERTY(int travelTime READ travelTime CONSTANT) + Q_PROPERTY(qreal distance READ distance CONSTANT) + Q_PROPERTY(QJSValue path READ path CONSTANT) + Q_PROPERTY(QDeclarativeGeoManeuver *maneuver READ maneuver CONSTANT) + +public: + explicit QDeclarativeGeoRouteSegment(QObject *parent = 0); + QDeclarativeGeoRouteSegment(const QGeoRouteSegment &segment, QObject *parent = 0); + ~QDeclarativeGeoRouteSegment(); + + int travelTime() const; + qreal distance() const; + QJSValue path() const; + QDeclarativeGeoManeuver *maneuver() const; + +private: + QGeoRouteSegment segment_; + QDeclarativeGeoManeuver *maneuver_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativemaps/qdeclarativegeoserviceprovider.cpp b/src/location/declarativemaps/qdeclarativegeoserviceprovider.cpp new file mode 100644 index 0000000..09309bc --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoserviceprovider.cpp @@ -0,0 +1,923 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeoserviceprovider_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Plugin + \instantiates QDeclarativeGeoServiceProvider + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-common + \since QtLocation 5.5 + + \brief The Plugin type describes a Location based services plugin. + + The Plugin type is used to declaratively specify which available + GeoServices plugin should be used for various tasks in the Location API. + Plugins are used by \l Map, \l RouteModel, and \l GeocodeModel + types, as well as a variety of others. + + Plugins recognized by the system have a \l name property, a simple string + normally indicating the name of the service that the Plugin retrieves + data from. They also have a variety of features, which can be test for using the + \l {supportsRouting()}, \l {supportsGeocoding()}, \l {supportsMapping()}, + \l {supportsPlaces()} and \l {supportsNavigation()} methods. + + When a Plugin object is created, it is "detached" and not associated with + any actual service plugin. Once it has received information via setting + its \l name, \l preferred, or \l required properties, it will choose an + appropriate service plugin to attach to. Plugin objects can only be + attached once; to use multiple plugins, create multiple Plugin objects. + + \section2 Example Usage + + The following snippet shows a Plugin object being created with the + \l required and \l preferred properties set. This Plugin will attach to the + first found plugin that supports both mapping and geocoding, and will + prefer plugins named "here" or "osm" to any others. + + \code + Plugin { + id: plugin + preferred: ["here", "osm"] + required: Plugin.AnyMappingFeatures | Plugin.AnyGeocodingFeatures + } + \endcode +*/ + +QDeclarativeGeoServiceProvider::QDeclarativeGeoServiceProvider(QObject *parent) +: QObject(parent), + sharedProvider_(0), + required_(new QDeclarativeGeoServiceProviderRequirements), + complete_(false), + experimental_(false) +{ + locales_.append(QLocale().name()); +} + +QDeclarativeGeoServiceProvider::~QDeclarativeGeoServiceProvider() +{ + delete required_; + delete sharedProvider_; +} + + + +/*! + \qmlproperty string Plugin::name + + This property holds the name of the plugin. Setting this property + will cause the Plugin to only attach to a plugin with exactly this + name. The value of \l required will be ignored. +*/ +void QDeclarativeGeoServiceProvider::setName(const QString &name) +{ + if (name_ == name) + return; + + name_ = name; + + if (complete_) + tryAttach(); + + emit nameChanged(name_); +} + +/*! + \internal +*/ +bool QDeclarativeGeoServiceProvider::parametersReady() { + for (const QDeclarativeGeoServiceProviderParameter *p: qAsConst(parameters_)) { + if (!p->isInitialized()) + return false; + } + return true; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProvider::tryAttach() +{ + if (!parametersReady()) + return; + + delete sharedProvider_; + sharedProvider_ = nullptr; + + if (name_.isEmpty()) + return; + + sharedProvider_ = new QGeoServiceProvider(name_, parameterMap()); + sharedProvider_->setLocale(locales_.at(0)); + sharedProvider_->setAllowExperimental(experimental_); + + emit attached(); +} + +QString QDeclarativeGeoServiceProvider::name() const +{ + return name_; +} + + +/*! + \qmlproperty stringlist Plugin::availableServiceProviders + + This property holds a list of all available service plugins' names. This + can be used to manually enumerate the available plugins if the + control provided by \l name and \l required is not sufficient for your + needs. +*/ +QStringList QDeclarativeGeoServiceProvider::availableServiceProviders() +{ + return QGeoServiceProvider::availableServiceProviders(); +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProvider::componentComplete() +{ + complete_ = true; + + for (QDeclarativeGeoServiceProviderParameter *p: qAsConst(parameters_)) { + if (!p->isInitialized()) { + connect(p, &QDeclarativeGeoServiceProviderParameter::initialized, + this, &QDeclarativeGeoServiceProvider::tryAttach); + } + } + + if (!name_.isEmpty()) { + tryAttach(); + } else if (!prefer_.isEmpty() + || required_->mappingRequirements() != NoMappingFeatures + || required_->routingRequirements() != NoRoutingFeatures + || required_->geocodingRequirements() != NoGeocodingFeatures + || required_->placesRequirements() != NoPlacesFeatures) { + + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + + /* first check any preferred plugins */ + foreach (const QString &name, prefer_) { + if (providers.contains(name)) { + // so we don't try it again later + providers.removeAll(name); + + QGeoServiceProvider sp(name, parameterMap(), experimental_); + if (required_->matches(&sp)) { + setName(name); + return; + } + } + } + + /* then try the rest */ + foreach (const QString &name, providers) { + QGeoServiceProvider sp(name, parameterMap(), experimental_); + if (required_->matches(&sp)) { + setName(name); + return; + } + } + + qmlWarning(this) << "Could not find a plugin with the required features to attach to"; + } +} + +/*! + \qmlmethod bool Plugin::supportsGeocoding(GeocodingFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. \c True is returned if all specified \a features are + supported; otherwise \c false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoGeocodingFeatures + \li No geocoding features are supported. + \row + \li Plugin.OnlineGeocodingFeature + \li Online geocoding is supported. + \row + \li Plugin.OfflineGeocodingFeature + \li Offline geocoding is supported. + \row + \li Plugin.ReverseGeocodingFeature + \li Reverse geocoding is supported. + \row + \li Plugin.LocalizedGeocodingFeature + \li Supports returning geocoding results with localized addresses. + \row + \li Plugin.AnyGeocodingFeatures + \li Matches a geo service provider that provides any geocoding features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsGeocoding(const GeocodingFeatures &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::GeocodingFeatures f = + static_cast(int(feature)); + if (f == QGeoServiceProvider::AnyGeocodingFeatures) + return (sp && (sp->geocodingFeatures() != QGeoServiceProvider::NoGeocodingFeatures)); + else + return (sp && (sp->geocodingFeatures() & f) == f); +} + +/*! + \qmlmethod bool Plugin::supportsMapping(MappingFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoMappingFeatures + \li No mapping features are supported. + \row + \li Plugin.OnlineMappingFeature + \li Online mapping is supported. + \row + \li Plugin.OfflineMappingFeature + \li Offline mapping is supported. + \row + \li Plugin.LocalizedMappingFeature + \li Supports returning localized map data. + \row + \li Plugin.AnyMappingFeatures + \li Matches a geo service provider that provides any mapping features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsMapping(const MappingFeatures &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::MappingFeatures f = + static_cast(int(feature)); + if (f == QGeoServiceProvider::AnyMappingFeatures) + return (sp && (sp->mappingFeatures() != QGeoServiceProvider::NoMappingFeatures)); + else + return (sp && (sp->mappingFeatures() & f) == f); +} + +/*! + \qmlmethod bool Plugin::supportsRouting(RoutingFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoRoutingFeatures + \li No routing features are supported. + \row + \li Plugin.OnlineRoutingFeature + \li Online routing is supported. + \row + \li Plugin.OfflineRoutingFeature + \li Offline routing is supported. + \row + \li Plugin.LocalizedRoutingFeature + \li Supports returning routes with localized addresses and instructions. + \row + \li Plugin.RouteUpdatesFeature + \li Updating an existing route based on the current position is supported. + \row + \li Plugin.AlternativeRoutesFeature + \li Supports returning alternative routes. + \row + \li Plugin.ExcludeAreasRoutingFeature + \li Supports specifying a areas which the returned route must not cross. + \row + \li Plugin.AnyRoutingFeatures + \li Matches a geo service provider that provides any routing features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsRouting(const RoutingFeatures &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::RoutingFeatures f = + static_cast(int(feature)); + if (f == QGeoServiceProvider::AnyRoutingFeatures) + return (sp && (sp->routingFeatures() != QGeoServiceProvider::NoRoutingFeatures)); + else + return (sp && (sp->routingFeatures() & f) == f); +} + +/*! + \qmlmethod bool Plugin::supportsPlaces(PlacesFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoPlacesFeatures + \li No places features are supported. + \row + \li Plugin.OnlinePlacesFeature + \li Online places is supported. + \row + \li Plugin.OfflinePlacesFeature + \li Offline places is supported. + \row + \li Plugin.SavePlaceFeature + \li Saving categories is supported. + \row + \li Plugin.RemovePlaceFeature + \li Removing or deleting places is supported. + \row + \li Plugin.PlaceRecommendationsFeature + \li Searching for recommended places similar to another place is supported. + \row + \li Plugin.SearchSuggestionsFeature + \li Search suggestions is supported. + \row + \li Plugin.LocalizedPlacesFeature + \li Supports returning localized place data. + \row + \li Plugin.NotificationsFeature + \li Notifications of place and category changes is supported. + \row + \li Plugin.PlaceMatchingFeature + \li Supports matching places from two different geo service providers. + \row + \li Plugin.AnyPlacesFeatures + \li Matches a geo service provider that provides any places features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsPlaces(const PlacesFeatures &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::PlacesFeatures f = + static_cast(int(feature)); + if (f == QGeoServiceProvider::AnyPlacesFeatures) + return (sp && (sp->placesFeatures() != QGeoServiceProvider::NoPlacesFeatures)); + else + return (sp && (sp->placesFeatures() & f) == f); +} + +/*! + \qmlmethod bool Plugin::supportsNavigation(NavigationFeatures features) + + This method returns a boolean indicating whether the specified set of \a features are supported + by the geo service provider plugin. True is returned if all specified \a features are + supported; otherwise false is returned. + + The \a features parameter can be any flag combination of: + \table + \header + \li Feature + \li Description + \row + \li Plugin.NoNavigationFeatures + \li No navigation features are supported. + \row + \li Plugin.OnlineNavigationFeature + \li Online navigation is supported. + \row + \li Plugin.OfflineNavigationFeature + \li Offline navigation is supported. + \row + \li Plugin.AnyNavigationFeatures + \li Matches a geo service provider that provides any navigation features. + \endtable +*/ +bool QDeclarativeGeoServiceProvider::supportsNavigation(const QDeclarativeGeoServiceProvider::NavigationFeature &feature) const +{ + QGeoServiceProvider *sp = sharedGeoServiceProvider(); + QGeoServiceProvider::NavigationFeatures f = + static_cast(int(feature)); + if (f == QGeoServiceProvider::AnyNavigationFeatures) + return (sp && (sp->navigationFeatures() != QGeoServiceProvider::NoNavigationFeatures)); + else + return (sp && (sp->navigationFeatures() & f) == f); +} + +/*! + \qmlproperty enumeration Plugin::required + + This property contains the set of features that will be required by the + Plugin object when choosing which service plugin to attach to. If the + \l name property is set, this has no effect. + + Any of the following values or a bitwise combination of multiple values + may be set: + + \list + \li Plugin.NoFeatures + \li Plugin.GeocodingFeature + \li Plugin.ReverseGeocodingFeature + \li Plugin.RoutingFeature + \li Plugin.MappingFeature + \li Plugin.AnyPlacesFeature + \endlist +*/ +QDeclarativeGeoServiceProviderRequirements *QDeclarativeGeoServiceProvider::requirements() const +{ + return required_; +} + +void QDeclarativeGeoServiceProvider::setRequirements(QDeclarativeGeoServiceProviderRequirements *req) +{ + if (!name().isEmpty() || !req) + return; + + if (required_ && *required_ == *req) + return; + + delete required_; + required_ = req; + QQmlEngine::setObjectOwnership(req, QQmlEngine::CppOwnership); // To prevent the engine from making this object disappear +} + +/*! + \qmlproperty stringlist Plugin::preferred + + This property contains an ordered list of preferred plugin names, which + will be checked for the required features set in \l{Plugin::required}{required} + before any other available plugins are checked. +*/ +QStringList QDeclarativeGeoServiceProvider::preferred() const +{ + return prefer_; +} + +void QDeclarativeGeoServiceProvider::setPreferred(const QStringList &val) +{ + prefer_ = val; + emit preferredChanged(prefer_); +} + +/*! + \qmlproperty bool Plugin::isAttached + + This property indicates if the Plugin item is attached to a geoservice provider plugin. +*/ +bool QDeclarativeGeoServiceProvider::isAttached() const +{ + return (sharedProvider_ != 0); +} + +/*! + \qmlproperty bool Plugin::allowExperimental + + This property indicates if experimental plugins can be used. +*/ +bool QDeclarativeGeoServiceProvider::allowExperimental() const +{ + return experimental_; +} + +void QDeclarativeGeoServiceProvider::setAllowExperimental(bool allow) +{ + if (experimental_ == allow) + return; + + experimental_ = allow; + if (sharedProvider_) + sharedProvider_->setAllowExperimental(allow); + + emit allowExperimentalChanged(allow); +} + +/*! + \internal +*/ +QGeoServiceProvider *QDeclarativeGeoServiceProvider::sharedGeoServiceProvider() const +{ + return sharedProvider_; +} + +/*! + \qmlproperty stringlist Plugin::locales + + This property contains an ordered list of preferred plugin locales. If the first locale cannot be accommodated, then + the backend falls back to using the second, and so on. By default the locales property contains the system locale. + + The locales are specified as strings which have the format + "language[_script][_country]" or "C", where: + + \list + \li language is a lowercase, two-letter, ISO 639 language code, + \li script is a titlecase, four-letter, ISO 15924 script code, + \li country is an uppercase, two- or three-letter, ISO 3166 country code (also "419" as defined by United Nations), + \li the "C" locale is identical in behavior to English/UnitedStates as per QLocale + \endlist + + If the first specified locale cannot be accommodated, the \l {Plugin} falls back to the next and so forth. + Some \l {Plugin} backends may not support a set of locales which are rigidly defined. An arbitrary + example is that some \l {Place}'s in France could have French and English localizations, while + certain areas in America may only have the English localization available. In the above scenario, + the set of supported locales is context dependent on the search location. + + If the \l {Plugin} cannot accommodate any of the preferred locales, the manager falls + back to using a supported language that is backend specific. + + For \l {Plugin}'s that do not support locales, the locales list is always empty. + + The following code demonstrates how to set a single and multiple locales: + \snippet declarative/plugin.qml Plugin locale +*/ +QStringList QDeclarativeGeoServiceProvider::locales() const +{ + return locales_; +} + +void QDeclarativeGeoServiceProvider::setLocales(const QStringList &locales) +{ + if (locales_ == locales) + return; + + locales_ = locales; + + if (locales_.isEmpty()) + locales_.append(QLocale().name()); + + if (sharedProvider_) + sharedProvider_->setLocale(locales_.at(0)); + + emit localesChanged(); +} + +/*! + \qmlproperty list Plugin::parameters + \default + + This property holds the list of plugin parameters. +*/ +QQmlListProperty QDeclarativeGeoServiceProvider::parameters() +{ + return QQmlListProperty(this, + 0, + parameter_append, + parameter_count, + parameter_at, + parameter_clear); +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProvider::parameter_append(QQmlListProperty *prop, QDeclarativeGeoServiceProviderParameter *parameter) +{ + QDeclarativeGeoServiceProvider *p = static_cast(prop->object); + p->parameters_.append(parameter); + if (p->sharedProvider_) + p->sharedProvider_->setParameters(p->parameterMap()); +} + +/*! + \internal +*/ +int QDeclarativeGeoServiceProvider::parameter_count(QQmlListProperty *prop) +{ + return static_cast(prop->object)->parameters_.count(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProviderParameter *QDeclarativeGeoServiceProvider::parameter_at(QQmlListProperty *prop, int index) +{ + return static_cast(prop->object)->parameters_[index]; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProvider::parameter_clear(QQmlListProperty *prop) +{ + QDeclarativeGeoServiceProvider *p = static_cast(prop->object); + p->parameters_.clear(); + if (p->sharedProvider_) + p->sharedProvider_->setParameters(p->parameterMap()); +} + +/*! + \internal +*/ +QVariantMap QDeclarativeGeoServiceProvider::parameterMap() const +{ + QVariantMap map; + + for (int i = 0; i < parameters_.size(); ++i) { + QDeclarativeGeoServiceProviderParameter *parameter = parameters_.at(i); + map.insert(parameter->name(), parameter->value()); + } + + return map; +} + +/******************************************************************************* +*******************************************************************************/ + +QDeclarativeGeoServiceProviderRequirements::QDeclarativeGeoServiceProviderRequirements(QObject *parent) + : QObject(parent), + mapping_(QDeclarativeGeoServiceProvider::NoMappingFeatures), + routing_(QDeclarativeGeoServiceProvider::NoRoutingFeatures), + geocoding_(QDeclarativeGeoServiceProvider::NoGeocodingFeatures), + places_(QDeclarativeGeoServiceProvider::NoPlacesFeatures) +{ +} + +QDeclarativeGeoServiceProviderRequirements::~QDeclarativeGeoServiceProviderRequirements() +{ +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider::MappingFeatures QDeclarativeGeoServiceProviderRequirements::mappingRequirements() const +{ + return mapping_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setMappingRequirements(const QDeclarativeGeoServiceProvider::MappingFeatures &features) +{ + if (mapping_ == features) + return; + + mapping_ = features; + emit mappingRequirementsChanged(mapping_); + emit requirementsChanged(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider::RoutingFeatures QDeclarativeGeoServiceProviderRequirements::routingRequirements() const +{ + return routing_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setRoutingRequirements(const QDeclarativeGeoServiceProvider::RoutingFeatures &features) +{ + if (routing_ == features) + return; + + routing_ = features; + emit routingRequirementsChanged(routing_); + emit requirementsChanged(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider::GeocodingFeatures QDeclarativeGeoServiceProviderRequirements::geocodingRequirements() const +{ + return geocoding_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setGeocodingRequirements(const QDeclarativeGeoServiceProvider::GeocodingFeatures &features) +{ + if (geocoding_ == features) + return; + + geocoding_ = features; + emit geocodingRequirementsChanged(geocoding_); + emit requirementsChanged(); +} + +/*! + \internal + + */ +QDeclarativeGeoServiceProvider::PlacesFeatures QDeclarativeGeoServiceProviderRequirements::placesRequirements() const +{ + return places_; +} + +/*! + \internal +*/ +void QDeclarativeGeoServiceProviderRequirements::setPlacesRequirements(const QDeclarativeGeoServiceProvider::PlacesFeatures &features) +{ + if (places_ == features) + return; + + places_ = features; + emit placesRequirementsChanged(places_); + emit requirementsChanged(); +} + +/*! + \internal +*/ +bool QDeclarativeGeoServiceProviderRequirements::matches(const QGeoServiceProvider *provider) const +{ + QGeoServiceProvider::MappingFeatures mapping = + static_cast(int(mapping_)); + + // extra curlies here to avoid "dangling" else, which could belong to either if + // same goes for all the rest of these blocks + if (mapping == QGeoServiceProvider::AnyMappingFeatures) { + if (provider->mappingFeatures() == QGeoServiceProvider::NoMappingFeatures) + return false; + } else { + if ((provider->mappingFeatures() & mapping) != mapping) + return false; + } + + QGeoServiceProvider::RoutingFeatures routing = + static_cast(int(routing_)); + + if (routing == QGeoServiceProvider::AnyRoutingFeatures) { + if (provider->routingFeatures() == QGeoServiceProvider::NoRoutingFeatures) + return false; + } else { + if ((provider->routingFeatures() & routing) != routing) + return false; + } + + QGeoServiceProvider::GeocodingFeatures geocoding = + static_cast(int(geocoding_)); + + if (geocoding == QGeoServiceProvider::AnyGeocodingFeatures) { + if (provider->geocodingFeatures() == QGeoServiceProvider::NoGeocodingFeatures) + return false; + } else { + if ((provider->geocodingFeatures() & geocoding) != geocoding) + return false; + } + + QGeoServiceProvider::PlacesFeatures places = + static_cast(int(places_)); + + if (places == QGeoServiceProvider::AnyPlacesFeatures) { + if (provider->placesFeatures() == QGeoServiceProvider::NoPlacesFeatures) + return false; + } else { + if ((provider->placesFeatures() & places) != places) + return false; + } + + return true; +} + +bool QDeclarativeGeoServiceProviderRequirements::operator == (const QDeclarativeGeoServiceProviderRequirements &rhs) const +{ + return (mapping_ == rhs.mapping_ && routing_ == rhs.routing_ + && geocoding_ == rhs.geocoding_ && places_ == rhs.places_); +} + +/******************************************************************************* +*******************************************************************************/ + +/*! + \qmltype PluginParameter + \instantiates QDeclarativeGeoServiceProviderParameter + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-common + \since QtLocation 5.5 + + \brief The PluginParameter type describes a parameter to a \l Plugin. + + The PluginParameter object is used to provide a parameter of some kind + to a Plugin. Typically these parameters contain details like an application + token for access to a service, or a proxy server to use for network access. + + To set such a parameter, declare a PluginParameter inside a \l Plugin + object, and give it \l{name} and \l{value} properties. A list of valid + parameter names for each plugin is available from the + \l {Qt Location#Plugin References and Parameters}{plugin reference pages}. + + \section2 Example Usage + + The following example shows an instantiation of the \l {Qt Location HERE Plugin}{HERE} plugin + with a mapping API \e app_id and \e token pair specific to the application. + + \code + Plugin { + name: "here" + PluginParameter { name: "here.app_id"; value: "EXAMPLE_API_ID" } + PluginParameter { name: "here.token"; value: "EXAMPLE_TOKEN_123" } + } + \endcode +*/ + +QDeclarativeGeoServiceProviderParameter::QDeclarativeGeoServiceProviderParameter(QObject *parent) + : QObject(parent) {} + +QDeclarativeGeoServiceProviderParameter::~QDeclarativeGeoServiceProviderParameter() {} + +/*! + \qmlproperty string PluginParameter::name + + This property holds the name of the plugin parameter as a single formatted string. + This property is a write-once property. +*/ +void QDeclarativeGeoServiceProviderParameter::setName(const QString &name) +{ + if (!name_.isEmpty() || name.isEmpty()) + return; + + name_ = name; + + emit nameChanged(name_); + if (value_.isValid()) + emit initialized(); +} + +QString QDeclarativeGeoServiceProviderParameter::name() const +{ + return name_; +} + +/*! + \qmlproperty QVariant PluginParameter::value + + This property holds the value of the plugin parameter which support different types of values (variant). + This property is a write-once property. +*/ +void QDeclarativeGeoServiceProviderParameter::setValue(const QVariant &value) +{ + if (value_.isValid() || !value.isValid() || value.isNull()) + return; + + value_ = value; + + emit valueChanged(value_); + if (!name_.isEmpty()) + emit initialized(); +} + +QVariant QDeclarativeGeoServiceProviderParameter::value() const +{ + return value_; +} + +bool QDeclarativeGeoServiceProviderParameter::isInitialized() const +{ + return !name_.isEmpty() && value_.isValid(); +} + +/******************************************************************************* +*******************************************************************************/ + +QT_END_NAMESPACE + diff --git a/src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h b/src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h new file mode 100644 index 0000000..f6a663f --- /dev/null +++ b/src/location/declarativemaps/qdeclarativegeoserviceprovider_p.h @@ -0,0 +1,303 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEQGEOSERVICEPROVIDER_H +#define QDECLARATIVEQGEOSERVICEPROVIDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoServiceProviderParameter : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit QDeclarativeGeoServiceProviderParameter(QObject *parent = 0); + ~QDeclarativeGeoServiceProviderParameter(); + + void setName(const QString &name); + QString name() const; + + void setValue(const QVariant &value); + QVariant value() const; + + bool isInitialized() const; + +Q_SIGNALS: + void nameChanged(const QString &name); + void valueChanged(const QVariant &value); + void initialized(); + +private: + QString name_; + QVariant value_; +}; + +class QDeclarativeGeoServiceProviderRequirements; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoServiceProvider : public QObject, public QQmlParserStatus +{ + Q_OBJECT + Q_ENUMS(RoutingFeature) + Q_ENUMS(GeocodingFeature) + Q_ENUMS(MappingFeature) + Q_ENUMS(PlacesFeature) + + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QStringList availableServiceProviders READ availableServiceProviders CONSTANT) + Q_PROPERTY(QQmlListProperty parameters READ parameters) + Q_PROPERTY(QDeclarativeGeoServiceProviderRequirements *required READ requirements WRITE setRequirements) + Q_PROPERTY(QStringList locales READ locales WRITE setLocales NOTIFY localesChanged) + Q_PROPERTY(QStringList preferred READ preferred WRITE setPreferred NOTIFY preferredChanged) + Q_PROPERTY(bool allowExperimental READ allowExperimental WRITE setAllowExperimental NOTIFY allowExperimentalChanged) + Q_PROPERTY(bool isAttached READ isAttached NOTIFY attached) + + Q_CLASSINFO("DefaultProperty", "parameters") + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeGeoServiceProvider(QObject *parent = nullptr); + ~QDeclarativeGeoServiceProvider(); + + enum RoutingFeature { + NoRoutingFeatures = QGeoServiceProvider::NoRoutingFeatures, + OnlineRoutingFeature = QGeoServiceProvider::OnlineRoutingFeature, + OfflineRoutingFeature = QGeoServiceProvider::OfflineRoutingFeature, + LocalizedRoutingFeature = QGeoServiceProvider::LocalizedRoutingFeature, + RouteUpdatesFeature = QGeoServiceProvider::RouteUpdatesFeature, + AlternativeRoutesFeature = QGeoServiceProvider::AlternativeRoutesFeature, + ExcludeAreasRoutingFeature = QGeoServiceProvider::ExcludeAreasRoutingFeature, + AnyRoutingFeatures = QGeoServiceProvider::AnyRoutingFeatures + }; + + enum GeocodingFeature { + NoGeocodingFeatures = QGeoServiceProvider::NoGeocodingFeatures, + OnlineGeocodingFeature = QGeoServiceProvider::OnlineGeocodingFeature, + OfflineGeocodingFeature = QGeoServiceProvider::OfflineGeocodingFeature, + ReverseGeocodingFeature = QGeoServiceProvider::ReverseGeocodingFeature, + LocalizedGeocodingFeature = QGeoServiceProvider::LocalizedGeocodingFeature, + AnyGeocodingFeatures = QGeoServiceProvider::AnyGeocodingFeatures + }; + + enum MappingFeature { + NoMappingFeatures = QGeoServiceProvider::NoMappingFeatures, + OnlineMappingFeature = QGeoServiceProvider::OnlineMappingFeature, + OfflineMappingFeature = QGeoServiceProvider::OfflineMappingFeature, + LocalizedMappingFeature = QGeoServiceProvider::LocalizedMappingFeature, + AnyMappingFeatures = QGeoServiceProvider::AnyMappingFeatures + }; + + enum PlacesFeature { + NoPlacesFeatures = QGeoServiceProvider::NoPlacesFeatures, + OnlinePlacesFeature = QGeoServiceProvider::OnlinePlacesFeature, + OfflinePlacesFeature = QGeoServiceProvider::OfflinePlacesFeature, + SavePlaceFeature = QGeoServiceProvider::SavePlaceFeature, + RemovePlaceFeature = QGeoServiceProvider::RemovePlaceFeature, + SaveCategoryFeature = QGeoServiceProvider::SaveCategoryFeature, + RemoveCategoryFeature = QGeoServiceProvider::RemoveCategoryFeature, + PlaceRecommendationsFeature = QGeoServiceProvider::PlaceRecommendationsFeature, + SearchSuggestionsFeature = QGeoServiceProvider::SearchSuggestionsFeature, + LocalizedPlacesFeature = QGeoServiceProvider::LocalizedPlacesFeature, + NotificationsFeature = QGeoServiceProvider::NotificationsFeature, + PlaceMatchingFeature = QGeoServiceProvider::PlaceMatchingFeature, + AnyPlacesFeatures = QGeoServiceProvider::AnyPlacesFeatures + }; + + enum NavigationFeature { + NoNavigationFeatures = QGeoServiceProvider::NoNavigationFeatures, + OnlineNavigationFeature = QGeoServiceProvider::OnlineNavigationFeature, + OfflineNavigationFeature = QGeoServiceProvider::OfflineNavigationFeature, + AnyNavigationFeatures = QGeoServiceProvider::AnyNavigationFeatures + }; + + Q_DECLARE_FLAGS(RoutingFeatures, RoutingFeature) + Q_FLAGS(RoutingFeatures) + + Q_DECLARE_FLAGS(GeocodingFeatures, GeocodingFeature) + Q_FLAGS(GeocodingFeatures) + + Q_DECLARE_FLAGS(MappingFeatures, MappingFeature) + Q_FLAGS(MappingFeatures) + + Q_DECLARE_FLAGS(PlacesFeatures, PlacesFeature) + Q_FLAGS(PlacesFeatures) + + Q_DECLARE_FLAGS(NavigationFeatures, NavigationFeature) + Q_FLAGS(NavigationFeatures) + + // From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + void setName(const QString &name); + QString name() const; + + QQmlListProperty parameters(); + QVariantMap parameterMap() const; + + QStringList availableServiceProviders(); + + QDeclarativeGeoServiceProviderRequirements *requirements() const; + void setRequirements(QDeclarativeGeoServiceProviderRequirements *req); + + QStringList preferred() const; + void setPreferred(const QStringList &val); + + QGeoServiceProvider *sharedGeoServiceProvider() const; + + Q_INVOKABLE bool supportsRouting(const RoutingFeatures &feature = AnyRoutingFeatures) const; + Q_INVOKABLE bool supportsGeocoding(const GeocodingFeatures &feature = AnyGeocodingFeatures) const; + Q_INVOKABLE bool supportsMapping(const MappingFeatures &feature = AnyMappingFeatures) const; + Q_INVOKABLE bool supportsPlaces(const PlacesFeatures &feature = AnyPlacesFeatures) const; + Q_REVISION(11) Q_INVOKABLE bool supportsNavigation(const NavigationFeature &feature = AnyNavigationFeatures) const; + + QStringList locales() const; + void setLocales(const QStringList &locales); + + bool isAttached() const; + + void setAllowExperimental(bool allow); + bool allowExperimental() const; + +Q_SIGNALS: + void nameChanged(const QString &name); + void localesChanged(); + void attached(); + void preferredChanged(const QStringList &preferences); + void allowExperimentalChanged(bool allow); + +private: + bool parametersReady(); + void tryAttach(); + static void parameter_append(QQmlListProperty *prop, QDeclarativeGeoServiceProviderParameter *mapObject); + static int parameter_count(QQmlListProperty *prop); + static QDeclarativeGeoServiceProviderParameter *parameter_at(QQmlListProperty *prop, int index); + static void parameter_clear(QQmlListProperty *prop); + + QGeoServiceProvider *sharedProvider_; + QString name_; + QList parameters_; + QDeclarativeGeoServiceProviderRequirements *required_; + bool complete_; + bool experimental_; + QStringList locales_; + QStringList prefer_; + Q_DISABLE_COPY(QDeclarativeGeoServiceProvider) +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeGeoServiceProviderRequirements : public QObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeGeoServiceProvider::MappingFeatures mapping + READ mappingRequirements WRITE setMappingRequirements + NOTIFY mappingRequirementsChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider::RoutingFeatures routing + READ routingRequirements WRITE setRoutingRequirements + NOTIFY routingRequirementsChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider::GeocodingFeatures geocoding + READ geocodingRequirements WRITE setGeocodingRequirements + NOTIFY geocodingRequirementsChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider::PlacesFeatures places + READ placesRequirements WRITE setPlacesRequirements + NOTIFY placesRequirementsChanged) + +public: + explicit QDeclarativeGeoServiceProviderRequirements(QObject *parent = 0); + ~QDeclarativeGeoServiceProviderRequirements(); + + QDeclarativeGeoServiceProvider::MappingFeatures mappingRequirements() const; + void setMappingRequirements(const QDeclarativeGeoServiceProvider::MappingFeatures &features); + + QDeclarativeGeoServiceProvider::RoutingFeatures routingRequirements() const; + void setRoutingRequirements(const QDeclarativeGeoServiceProvider::RoutingFeatures &features); + + QDeclarativeGeoServiceProvider::GeocodingFeatures geocodingRequirements() const; + void setGeocodingRequirements(const QDeclarativeGeoServiceProvider::GeocodingFeatures &features); + + QDeclarativeGeoServiceProvider::PlacesFeatures placesRequirements() const; + void setPlacesRequirements(const QDeclarativeGeoServiceProvider::PlacesFeatures &features); + + Q_INVOKABLE bool matches(const QGeoServiceProvider *provider) const; + + bool operator == (const QDeclarativeGeoServiceProviderRequirements &rhs) const; + +Q_SIGNALS: + void mappingRequirementsChanged(const QDeclarativeGeoServiceProvider::MappingFeatures &features); + void routingRequirementsChanged(const QDeclarativeGeoServiceProvider::RoutingFeatures &features); + void geocodingRequirementsChanged(const QDeclarativeGeoServiceProvider::GeocodingFeatures &features); + void placesRequirementsChanged(const QDeclarativeGeoServiceProvider::PlacesFeatures &features); + + void requirementsChanged(); + +private: + QDeclarativeGeoServiceProvider::MappingFeatures mapping_; + QDeclarativeGeoServiceProvider::RoutingFeatures routing_; + QDeclarativeGeoServiceProvider::GeocodingFeatures geocoding_; + QDeclarativeGeoServiceProvider::PlacesFeatures places_; + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeGeoServiceProviderParameter) +QML_DECLARE_TYPE(QDeclarativeGeoServiceProviderRequirements) +QML_DECLARE_TYPE(QDeclarativeGeoServiceProvider) + +#endif diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp new file mode 100644 index 0000000..c0d7f24 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem.cpp @@ -0,0 +1,702 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL3$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see http://www.qt.io/terms-conditions. For further + ** information use the contact form at http://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 3 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPLv3 included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 3 requirements + ** will be met: https://www.gnu.org/licenses/lgpl.html. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 2.0 or later as published by the Free + ** Software Foundation and appearing in the file LICENSE.GPL included in + ** the packaging of this file. Please review the following information to + ** ensure the GNU General Public License version 2.0 requirements will be + ** met: http://www.gnu.org/licenses/gpl-2.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qdeclarativepolygonmapitem_p.h" +#include "qlocationutils_p.h" +#include "error_messages_p.h" +#include "locationvaluetypehelper_p.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* poly2tri triangulator includes */ +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapPolygon + \instantiates QDeclarativePolygonMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.5 + + \brief The MapPolygon type displays a polygon on a Map. + + The MapPolygon type displays a polygon on a Map, specified in terms of an ordered list of + \l {QtPositioning::coordinate}{coordinates}. For best appearance and results, polygons should be + simple (not self-intersecting). + + The \l {QtPositioning::coordinate}{coordinates} on the path cannot be directly changed after + being added to the Polygon. Instead, copy the \l path into a var, modify the copy and reassign + the copy back to the \l path. + + \code + var path = mapPolygon.path; + path[0].latitude = 5; + mapPolygon.path = path; + \endcode + + Coordinates can also be added and removed at any time using the \l addCoordinate and + \l removeCoordinate methods. + + For drawing rectangles with "straight" edges (same latitude across one + edge, same latitude across the other), the \l MapRectangle type provides + a simpler, two-point API. + + By default, the polygon is displayed as a 1 pixel black border with no + fill. To change its appearance, use the \l color, \l border.color and + \l border.width properties. + + \note Since MapPolygons are geographic items, dragging a MapPolygon + (through the use of \l MouseArea) causes its vertices to be + recalculated in the geographic coordinate space. The edges retain the + same geographic lengths (latitude and longitude differences between the + vertices), but they remain straight. Apparent stretching of the item occurs + when dragged to a different latitude. + + \section2 Performance + + MapPolygons have a rendering cost that is O(n) with respect to the number + of vertices. This means that the per frame cost of having a Polygon on the + Map grows in direct proportion to the number of points on the Polygon. There + is an additional triangulation cost (approximately O(n log n)) which is + currently paid with each frame, but in future may be paid only upon adding + or removing points. + + Like the other map objects, MapPolygon is normally drawn without a smooth + appearance. Setting the \l {Item::opacity}{opacity} property will force the object to + be blended, which decreases performance considerably depending on the hardware in use. + + \section2 Example Usage + + The following snippet shows a MapPolygon being used to display a triangle, + with three vertices near Brisbane, Australia. The triangle is filled in + green, with a 1 pixel black border. + + \code + Map { + MapPolygon { + color: 'green' + path: [ + { latitude: -27, longitude: 153.0 }, + { latitude: -27, longitude: 154.1 }, + { latitude: -28, longitude: 153.5 } + ] + } + } + \endcode + + \image api-mappolygon.png +*/ + +QGeoMapPolygonGeometry::QGeoMapPolygonGeometry() +: assumeSimple_(false) +{ +} + +/*! + \internal +*/ +void QGeoMapPolygonGeometry::updateSourcePoints(const QGeoMap &map, + const QList &path) +{ + if (!sourceDirty_) + return; + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + srcPath_ = QPainterPath(); + + // build the actual path + // The approach is the same as described in QGeoMapPolylineGeometry::updateSourcePoints + srcOrigin_ = geoLeftBound_; + double unwrapBelowX = 0; + QDoubleVector2D leftBoundWrapped = p.wrapMapProjection(p.geoToMapProjection(geoLeftBound_)); + if (preserveGeometry_) + unwrapBelowX = leftBoundWrapped.x(); + + QList wrappedPath; + wrappedPath.reserve(path.size()); + QDoubleVector2D wrappedLeftBound(qInf(), qInf()); + // 1) + for (int i = 0; i < path.size(); ++i) { + const QDoubleVector2D &coord = path.at(i); + QDoubleVector2D wrappedProjection = p.wrapMapProjection(coord); + + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y())) + return; + + const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x()); + // unwrap x to preserve geometry if moved to border of map + if (preserveGeometry_ && isPointLessThanUnwrapBelowX) { + double distance = wrappedProjection.x() - unwrapBelowX; + if (distance < 0.0) + distance += 1.0; + wrappedProjection.setX(unwrapBelowX + distance); + } + if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) { + wrappedLeftBound = wrappedProjection; + } + wrappedPath.append(wrappedProjection); + } + + // 2) + QList > clippedPaths; + const QList &visibleRegion = p.projectableGeometry(); + if (visibleRegion.size()) { + c2t::clip2tri clipper; + clipper.addSubjectPath(QClipperUtils::qListToPath(wrappedPath), true); + clipper.addClipPolygon(QClipperUtils::qListToPath(visibleRegion)); + Paths res = clipper.execute(c2t::clip2tri::Intersection, QtClipperLib::pftEvenOdd, QtClipperLib::pftEvenOdd); + clippedPaths = QClipperUtils::pathsToQList(res); + + // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X + QDoubleVector2D lb(qInf(), qInf()); + for (const QList &path: clippedPaths) + for (const QDoubleVector2D &p: path) + if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) + // y-minimization needed to find the same point on polygon and border + lb = p; + + if (qIsInf(lb.x())) // e.g., when the polygon is clipped entirely + return; + + // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which + // in turn will make the geometry wrap around. + lb.setX(qMax(wrappedLeftBound.x(), lb.x())); + leftBoundWrapped = lb; + srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(lb)); + } else { + clippedPaths.append(wrappedPath); + } + + // 3) + QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(leftBoundWrapped); + for (const QList &path: clippedPaths) { + QDoubleVector2D lastAddedPoint; + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); + point = point - origin; // (0,0) if point == geoLeftBound_ + + if (i == 0) { + srcPath_.moveTo(point.toPointF()); + lastAddedPoint = point; + } else { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + srcPath_.lineTo(point.toPointF()); + lastAddedPoint = point; + } + } + } + srcPath_.closeSubpath(); + } + + if (!assumeSimple_) + srcPath_ = srcPath_.simplified(); + + sourceBounds_ = srcPath_.boundingRect(); +} + +/*! + \internal +*/ +void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map, qreal strokeWidth) +{ + if (!screenDirty_) + return; + + if (map.viewportWidth() == 0 || map.viewportHeight() == 0) { + clear(); + return; + } + + // The geometry has already been clipped against the visible region projection in wrapped mercator space. + QPainterPath ppi = srcPath_; + clear(); + + // a polygon requires at least 3 points; + if (ppi.elementCount() < 3) + return; + + // translate the path into top-left-centric coordinates + QRectF bb = ppi.boundingRect(); + ppi.translate(-bb.left(), -bb.top()); + firstPointOffset_ = -1 * bb.topLeft(); + + ppi.closeSubpath(); + screenOutline_ = ppi; + + using Coord = double; + using N = uint32_t; + using Point = std::array; + + std::vector> polygon; + polygon.push_back(std::vector()); + std::vector &poly = polygon.front(); + // ... fill polygon structure with actual data + + for (int i = 0; i < ppi.elementCount(); ++i) { + const QPainterPath::Element e = ppi.elementAt(i); + if (e.isMoveTo() || i == ppi.elementCount() - 1 + || (qAbs(e.x - poly.front()[0]) < 0.1 + && qAbs(e.y - poly.front()[1]) < 0.1)) { + Point p = {{ e.x, e.y }}; + poly.push_back( p ); + } else if (e.isLineTo()) { + Point p = {{ e.x, e.y }}; + poly.push_back( p ); + } else { + qWarning("Unhandled element type in polygon painterpath"); + } + } + + if (poly.size() > 2) { + // Run tessellation + // Returns array of indices that refer to the vertices of the input polygon. + // Three subsequent indices form a triangle. + screenVertices_.clear(); + screenIndices_.clear(); + for (const auto &p : poly) + screenVertices_ << QPointF(p[0], p[1]); + std::vector indices = qt_mapbox::earcut(polygon); + for (const auto &i: indices) + screenIndices_ << quint32(i); + } + + screenBounds_ = ppi.boundingRect(); + if (strokeWidth != 0.0) + this->translate(QPointF(strokeWidth, strokeWidth)); +} + +QDeclarativePolygonMapItem::QDeclarativePolygonMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true), + updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&border_, SIGNAL(colorChanged(QColor)), + this, SLOT(handleBorderUpdated())); + QObject::connect(&border_, SIGNAL(widthChanged(qreal)), + this, SLOT(handleBorderUpdated())); +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::handleBorderUpdated() +{ + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +QDeclarativePolygonMapItem::~QDeclarativePolygonMapItem() +{ +} + +/*! + \qmlpropertygroup Location::MapPolygon::border + \qmlproperty int MapPolygon::border.width + \qmlproperty color MapPolygon::border.color + + This property is part of the border property group. The border property + group holds the width and color used to draw the border of the polygon. + + The width is in pixels and is independent of the zoom level of the map. + + The default values correspond to a black border with a width of 1 pixel. + For no line, use a width of 0 or a transparent color. +*/ + +QDeclarativeMapLineProperties *QDeclarativePolygonMapItem::border() +{ + return &border_; +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map) { + regenerateCache(); + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); + } +} + +/*! + \qmlproperty list MapPolygon::path + + This property holds the ordered list of coordinates which + define the polygon. + Having less than 3 different coordinates in the path results in undefined behavior. + + \sa addCoordinate, removeCoordinate +*/ +QJSValue QDeclarativePolygonMapItem::path() const +{ + return fromList(this, geopath_.path()); +} + +void QDeclarativePolygonMapItem::setPath(const QJSValue &value) +{ + if (!value.isArray()) + return; + + QList pathList = toList(this, value); + + // Equivalent to QDeclarativePolylineMapItem::setPathFromGeoList + if (geopath_.path() == pathList) + return; + + geopath_.setPath(pathList); + + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolygon::addCoordinate(coordinate) + + Adds a coordinate to the path. + + \sa removeCoordinate, path +*/ + +void QDeclarativePolygonMapItem::addCoordinate(const QGeoCoordinate &coordinate) +{ + if (!coordinate.isValid()) + return; + + geopath_.addCoordinate(coordinate); + updateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolygon::removeCoordinate(coordinate) + + Removes \a coordinate from the path. If there are multiple instances of the + same coordinate, the one added last is removed. + + If \a coordinate is not in the path this method does nothing. + + \sa addCoordinate, path +*/ +void QDeclarativePolygonMapItem::removeCoordinate(const QGeoCoordinate &coordinate) +{ + int length = geopath_.path().length(); + geopath_.removeCoordinate(coordinate); + if (geopath_.path().length() == length) + return; + + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlproperty color MapPolygon::color + + This property holds the color used to fill the polygon. + + The default value is transparent. +*/ + +QColor QDeclarativePolygonMapItem::color() const +{ + return color_; +} + +void QDeclarativePolygonMapItem::setColor(const QColor &color) +{ + if (color_ == color) + return; + + color_ = color; + dirtyMaterial_ = true; + update(); + emit colorChanged(color_); +} + +/*! + \internal +*/ +QSGNode *QDeclarativePolygonMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + MapPolygonNode *node = static_cast(oldNode); + + if (!node) + node = new MapPolygonNode(); + + //TODO: update only material + if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { + node->update(color_, border_.color(), &geometry_, &borderGeometry_); + geometry_.setPreserveGeometry(false); + borderGeometry_.setPreserveGeometry(false); + geometry_.markClean(); + borderGeometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::updatePolish() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + if (geopath_.path().length() == 0) { // Possibly cleared + geometry_.clear(); + borderGeometry_.clear(); + setWidth(0); + setHeight(0); + return; + } + + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + QScopedValueRollback rollback(updatingGeometry_); + updatingGeometry_ = true; + + geometry_.updateSourcePoints(*map(), geopathProjected_); + geometry_.updateScreenPoints(*map(), border_.width()); + + QList geoms; + geoms << &geometry_; + borderGeometry_.clear(); + + if (border_.color() != Qt::transparent && border_.width() > 0) { + QList closedPath = geopathProjected_; + closedPath << closedPath.first(); + + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + + const QGeoCoordinate &geometryOrigin = geometry_.origin(); + + borderGeometry_.srcPoints_.clear(); + borderGeometry_.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); + borderGeometry_.updateScreenPoints(*map(), border_.width()); + + geoms << &borderGeometry_; + } else { + borderGeometry_.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + setWidth(combined.width() + 2 * border_.width()); + setHeight(combined.height() + 2 * border_.width()); + + setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft() + + QPointF(border_.width(), border_.width())); +} + +void QDeclarativePolygonMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); + borderGeometry_.setPreserveGeometry(true, borderGeometry_.geoLeftBound()); + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::regenerateCache() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + geopathProjected_.clear(); + geopathProjected_.reserve(geopath_.path().size()); + for (const QGeoCoordinate &c : geopath_.path()) + geopathProjected_ << p.geoToMapProjection(c); +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::updateCache() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + geopathProjected_ << p.geoToMapProjection(geopath_.path().last()); +} + +/*! + \internal +*/ +bool QDeclarativePolygonMapItem::contains(const QPointF &point) const +{ + return (geometry_.contains(point) || borderGeometry_.contains(point)); +} + +const QGeoShape &QDeclarativePolygonMapItem::geoShape() const +{ + return geopath_; +} + +QGeoMap::ItemType QDeclarativePolygonMapItem::itemType() const +{ + return QGeoMap::MapPolygon; +} + +/*! + \internal +*/ +void QDeclarativePolygonMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + // TODO: change the algorithm to preserve the distances and size! + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; + + geopath_.translate(offsetLati, offsetLongi); + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + borderGeometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +////////////////////////////////////////////////////////////////////// + +MapPolygonNode::MapPolygonNode() : + border_(new MapPolylineNode()), + geometry_(QSGGeometry::defaultAttributes_Point2D(), 0) +{ + geometry_.setDrawingMode(QSGGeometry::DrawTriangles); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); + + appendChildNode(border_); +} + +MapPolygonNode::~MapPolygonNode() +{ +} + +/*! + \internal +*/ +void MapPolygonNode::update(const QColor &fillColor, const QColor &borderColor, + const QGeoMapItemGeometry *fillShape, + const QGeoMapItemGeometry *borderShape) +{ + /* Do the border update first */ + border_->update(borderColor, borderShape); + + /* If we have neither fill nor border with valid points, block the whole + * tree. We can't just block the fill without blocking the border too, so + * we're a little conservative here (maybe at the expense of rendering + * accuracy) */ + if (fillShape->size() == 0 && borderShape->size() == 0) { + setSubtreeBlocked(true); + return; + } + setSubtreeBlocked(false); + + + QSGGeometry *fill = QSGGeometryNode::geometry(); + fillShape->allocateAndFill(fill); + markDirty(DirtyGeometry); + + if (fillColor != fill_material_.color()) { + fill_material_.setColor(fillColor); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h new file mode 100644 index 0000000..8398365 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolygonmapitem_p.h @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYGONMAPITEM +#define QDECLARATIVEPOLYGONMAPITEM + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class MapPolygonNode; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolygonGeometry : public QGeoMapItemGeometry +{ +public: + QGeoMapPolygonGeometry(); + + inline void setAssumeSimple(bool value) { assumeSimple_ = value; } + + void updateSourcePoints(const QGeoMap &map, + const QList &path); + + void updateScreenPoints(const QGeoMap &map, qreal strokeWidth = 0.0); + +protected: + QPainterPath srcPath_; + bool assumeSimple_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolygonMapItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + + Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + +public: + explicit QDeclarativePolygonMapItem(QQuickItem *parent = 0); + ~QDeclarativePolygonMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; + //from QuickItem + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override; + + Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate); + + QJSValue path() const; + void setPath(const QJSValue &value); + + QColor color() const; + void setColor(const QColor &color); + + QDeclarativeMapLineProperties *border(); + + bool contains(const QPointF &point) const override; + const QGeoShape &geoShape() const override; + QGeoMap::ItemType itemType() const override; + +Q_SIGNALS: + void pathChanged(); + void colorChanged(const QColor &color); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void updatePolish() override; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + void handleBorderUpdated(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; + +private: + void regenerateCache(); + void updateCache(); + + QGeoPath geopath_; + QList geopathProjected_; + QDeclarativeMapLineProperties border_; + QColor color_; + bool dirtyMaterial_; + QGeoMapPolygonGeometry geometry_; + QGeoMapPolylineGeometry borderGeometry_; + bool updatingGeometry_; +}; + +////////////////////////////////////////////////////////////////////// + +class Q_LOCATION_PRIVATE_EXPORT MapPolygonNode : public MapItemGeometryNode +{ + +public: + MapPolygonNode(); + ~MapPolygonNode() override; + + void update(const QColor &fillColor, const QColor &borderColor, + const QGeoMapItemGeometry *fillShape, + const QGeoMapItemGeometry *borderShape); +private: + QSGFlatColorMaterial fill_material_; + MapPolylineNode *border_; + QSGGeometry geometry_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePolygonMapItem) + +#endif /* QDECLARATIVEPOLYGONMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp new file mode 100644 index 0000000..e661980 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolylinemapitem.cpp @@ -0,0 +1,1107 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL3$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see http://www.qt.io/terms-conditions. For further + ** information use the contact form at http://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 3 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPLv3 included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 3 requirements + ** will be met: https://www.gnu.org/licenses/lgpl.html. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 2.0 or later as published by the Free + ** Software Foundation and appearing in the file LICENSE.GPL included in + ** the packaging of this file. Please review the following information to + ** ensure the GNU General Public License version 2.0 requirements will be + ** met: http://www.gnu.org/licenses/gpl-2.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qdeclarativepolylinemapitem_p.h" +#include "qlocationutils_p.h" +#include "error_messages_p.h" +#include "locationvaluetypehelper_p.h" +#include "qdoublevector2d_p.h" +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + + +static const double kClipperScaleFactor = 281474976710656.0; // 48 bits of precision + +static inline IntPoint toIntPoint(const double x, const double y) +{ + return IntPoint(cInt(x * kClipperScaleFactor), cInt(y * kClipperScaleFactor)); +} + +static IntPoint toIntPoint(const QDoubleVector2D &p) +{ + return toIntPoint(p.x(), p.y()); +} + +static bool get_line_intersection(const double p0_x, + const double p0_y, + const double p1_x, + const double p1_y, + const double p2_x, + const double p2_y, + const double p3_x, + const double p3_y, + double *i_x, + double *i_y, + double *i_t) +{ + const double s10_x = p1_x - p0_x; + const double s10_y = p1_y - p0_y; + const double s32_x = p3_x - p2_x; + const double s32_y = p3_y - p2_y; + + const double denom = s10_x * s32_y - s32_x * s10_y; + if (denom == 0.0) + return false; // Collinear + const bool denomPositive = denom > 0; + + const double s02_x = p0_x - p2_x; + const double s02_y = p0_y - p2_y; + const double s_numer = s10_x * s02_y - s10_y * s02_x; + if ((s_numer < 0.0) == denomPositive) + return false; // No collision + + const double t_numer = s32_x * s02_y - s32_y * s02_x; + if ((t_numer < 0.0) == denomPositive) + return false; // No collision + + if (((s_numer > denom) == denomPositive) || ((t_numer > denom) == denomPositive)) + return false; // No collision + // Collision detected + *i_t = t_numer / denom; + *i_x = p0_x + (*i_t * s10_x); + *i_y = p0_y + (*i_t * s10_y); + + return true; +} + +enum SegmentType { + NoIntersection, + OneIntersection, + TwoIntersections +}; + +static QList > clipLine( + const QList &l, + const QList &poly) +{ + QList > res; + if (poly.size() < 3 || l.size() < 2) + return res; + + // Step 1: build edges + std::vector > edges; + for (int i = 1; i < poly.size(); i++) + edges.push_back({ { poly.at(i-1).x(), poly.at(i-1).y(), poly.at(i).x(), poly.at(i).y() } }); + edges.push_back({ { poly.at(poly.size()-1).x(), poly.at(poly.size()-1).y(), poly.at(0).x(), poly.at(0).y() } }); + + // Build Path to check for containment, for edges not intersecting + // This step could be speeded up by forcing the orientation of the polygon, and testing the cross products in the step + // below, thus avoiding to resort to clipper. + Path clip; + for (const auto &v: poly) + clip.push_back(toIntPoint(v)); + + // Step 2: check each segment against each edge + QList subLine; + std::array intersections = { { 0.0, 0.0, 0.0, 0.0 } }; + + for (int i = 0; i < l.size() - 1; ++i) { + SegmentType type = NoIntersection; + double t = -1; // valid values are in [0, 1]. Only written if intersects + double previousT = t; + double i_x, i_y; + + const int firstContained = c2t::clip2tri::pointInPolygon(toIntPoint(l.at(i).x(), l.at(i).y()), clip); + const int secondContained = c2t::clip2tri::pointInPolygon(toIntPoint(l.at(i+1).x(), l.at(i+1).y()), clip); + + if (firstContained && secondContained) { // Second most common condition, test early and skip inner loop if possible + if (!subLine.size()) + subLine.push_back(l.at(i)); // the initial element has to be pushed now. + subLine.push_back(l.at(i+1)); + continue; + } + + for (unsigned int j = 0; j < edges.size(); ++j) { + const bool intersects = get_line_intersection(l.at(i).x(), + l.at(i).y(), + l.at(i+1).x(), + l.at(i+1).y(), + edges.at(j).at(0), + edges.at(j).at(1), + edges.at(j).at(2), + edges.at(j).at(3), + &i_x, + &i_y, + &t); + if (intersects) { + if (previousT >= 0.0) { //One intersection already hit + if (t < previousT) { // Reorder + intersections[2] = intersections[0]; + intersections[3] = intersections[1]; + intersections[0] = i_x; + intersections[1] = i_y; + } else { + intersections[2] = i_x; + intersections[3] = i_y; + } + + type = TwoIntersections; + break; // no need to check anything else + } else { // First intersection + intersections[0] = i_x; + intersections[1] = i_y; + type = OneIntersection; + } + previousT = t; + } + } + + if (type == NoIntersection) { + if (!firstContained && !secondContained) { // Both outside + subLine.clear(); + } else if (firstContained && secondContained) { + // Handled above already. + } else { // Mismatch between PointInPolygon and get_line_intersection. Treat it as no intersection + if (subLine.size()) + res.push_back(subLine); + subLine.clear(); + } + } else if (type == OneIntersection) { // Need to check the following cases to avoid mismatch with PointInPolygon result. + if (firstContained <= 0 && secondContained > 0) { // subLine MUST be empty + if (!subLine.size()) + subLine.push_back(QDoubleVector2D(intersections[0], intersections[1])); + subLine.push_back(l.at(i+1)); + } else if (firstContained > 0 && secondContained <= 0) { // subLine MUST NOT be empty + if (!subLine.size()) + subLine.push_back(l.at(i)); + subLine.push_back(QDoubleVector2D(intersections[0], intersections[1])); + res.push_back(subLine); + subLine.clear(); + } else { + if (subLine.size()) + res.push_back(subLine); + subLine.clear(); + } + } else { // Two + // restart strip + subLine.clear(); + subLine.push_back(QDoubleVector2D(intersections[0], intersections[1])); + subLine.push_back(QDoubleVector2D(intersections[2], intersections[3])); + res.push_back(subLine); + subLine.clear(); + } + } + + if (subLine.size()) + res.push_back(subLine); + return res; +} + +/*! + \qmltype MapPolyline + \instantiates QDeclarativePolylineMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.0 + + \brief The MapPolyline type displays a polyline on a map. + + The MapPolyline type displays a polyline on a map, specified in terms of an ordered list of + \l {coordinate}{coordinates}. The \l {coordinate}{coordinates} on + the path cannot be directly changed after being added to the Polyline. Instead, copy the + \l path into a var, modify the copy and reassign the copy back to the \l path. + + \code + var path = mapPolyline.path; + path[0].latitude = 5; + mapPolyline.path = path; + \endcode + + Coordinates can also be added and removed at any time using the \l addCoordinate and + \l removeCoordinate methods. + + By default, the polyline is displayed as a 1-pixel thick black line. This + can be changed using the \l line.width and \l line.color properties. + + \section2 Performance + + MapPolylines have a rendering cost that is O(n) with respect to the number + of vertices. This means that the per frame cost of having a polyline on + the Map grows in direct proportion to the number of points in the polyline. + + Like the other map objects, MapPolyline is normally drawn without a smooth + appearance. Setting the \l {Item::opacity}{opacity} property will force the object to + be blended, which decreases performance considerably depending on the hardware in use. + + \section2 Example Usage + + The following snippet shows a MapPolyline with 4 points, making a shape + like the top part of a "question mark" (?), near Brisbane, Australia. + The line drawn is 3 pixels in width and green in color. + + \code + Map { + MapPolyline { + line.width: 3 + line.color: 'green' + path: [ + { latitude: -27, longitude: 153.0 }, + { latitude: -27, longitude: 154.1 }, + { latitude: -28, longitude: 153.5 }, + { latitude: -29, longitude: 153.5 } + ] + } + } + \endcode + + \image api-mappolyline.png +*/ + +QDeclarativeMapLineProperties::QDeclarativeMapLineProperties(QObject *parent) : + QObject(parent), + width_(1.0), + color_(Qt::black) +{ +} + +/*! + \internal +*/ +QColor QDeclarativeMapLineProperties::color() const +{ + return color_; +} + +/*! + \internal +*/ +void QDeclarativeMapLineProperties::setColor(const QColor &color) +{ + if (color_ == color) + return; + + color_ = color; + emit colorChanged(color_); +} + +/*! + \internal +*/ +qreal QDeclarativeMapLineProperties::width() const +{ + return width_; +} + +/*! + \internal +*/ +void QDeclarativeMapLineProperties::setWidth(qreal width) +{ + if (width_ == width) + return; + + width_ = width; + emit widthChanged(width_); +} + +struct Vertex +{ + QVector2D position; +}; + +QGeoMapPolylineGeometry::QGeoMapPolylineGeometry() +{ +} + +QList > QGeoMapPolylineGeometry::clipPath(const QGeoMap &map, + const QList &path, + QDoubleVector2D &leftBoundWrapped) +{ + /* + * Approach: + * 1) project coordinates to wrapped web mercator, and do unwrapBelowX + * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons) + * 2.1) recalculate the origin and geoLeftBound to prevent these parameters from ending in unprojectable areas + * 2.2) ensure the left bound does not wrap around due to QGeoCoordinate <-> clipper conversions + */ + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + srcOrigin_ = geoLeftBound_; + + double unwrapBelowX = 0; + leftBoundWrapped = p.wrapMapProjection(p.geoToMapProjection(geoLeftBound_)); + if (preserveGeometry_) + unwrapBelowX = leftBoundWrapped.x(); + + QList wrappedPath; + wrappedPath.reserve(path.size()); + QDoubleVector2D wrappedLeftBound(qInf(), qInf()); + // 1) + for (int i = 0; i < path.size(); ++i) { + const QDoubleVector2D &coord = path.at(i); + QDoubleVector2D wrappedProjection = p.wrapMapProjection(coord); + + // We can get NaN if the map isn't set up correctly, or the projection + // is faulty -- probably best thing to do is abort + if (!qIsFinite(wrappedProjection.x()) || !qIsFinite(wrappedProjection.y())) + return QList >(); + + const bool isPointLessThanUnwrapBelowX = (wrappedProjection.x() < leftBoundWrapped.x()); + // unwrap x to preserve geometry if moved to border of map + if (preserveGeometry_ && isPointLessThanUnwrapBelowX) { + double distance = wrappedProjection.x() - unwrapBelowX; + if (distance < 0.0) + distance += 1.0; + wrappedProjection.setX(unwrapBelowX + distance); + } + if (wrappedProjection.x() < wrappedLeftBound.x() || (wrappedProjection.x() == wrappedLeftBound.x() && wrappedProjection.y() < wrappedLeftBound.y())) { + wrappedLeftBound = wrappedProjection; + } + wrappedPath.append(wrappedProjection); + } + + // 2) + QList > clippedPaths; + const QList &visibleRegion = p.visibleGeometryExpanded(); + if (visibleRegion.size()) { + clippedPaths = clipLine(wrappedPath, visibleRegion); + + // 2.1) update srcOrigin_ and leftBoundWrapped with the point with minimum X + QDoubleVector2D lb(qInf(), qInf()); + for (const QList &path: clippedPaths) { + for (const QDoubleVector2D &p: path) { + if (p == leftBoundWrapped) { + lb = p; + break; + } else if (p.x() < lb.x() || (p.x() == lb.x() && p.y() < lb.y())) { + // y-minimization needed to find the same point on polygon and border + lb = p; + } + } + } + if (qIsInf(lb.x())) + return QList >(); + + // 2.2) Prevent the conversion to and from clipper from introducing negative offsets which + // in turn will make the geometry wrap around. + lb.setX(qMax(wrappedLeftBound.x(), lb.x())); + leftBoundWrapped = lb; + } else { + clippedPaths.append(wrappedPath); + } + + return clippedPaths; +} + +void QGeoMapPolylineGeometry::pathToScreen(const QGeoMap &map, + const QList > &clippedPaths, + const QDoubleVector2D &leftBoundWrapped) +{ + const QGeoProjectionWebMercator &p = static_cast(map.geoProjection()); + // 3) project the resulting geometry to screen position and calculate screen bounds + double minX = qInf(); + double minY = qInf(); + double maxX = -qInf(); + double maxY = -qInf(); + + srcOrigin_ = p.mapProjectionToGeo(p.unwrapMapProjection(leftBoundWrapped)); + QDoubleVector2D origin = p.wrappedMapProjectionToItemPosition(leftBoundWrapped); + for (const QList &path: clippedPaths) { + QDoubleVector2D lastAddedPoint; + for (int i = 0; i < path.size(); ++i) { + QDoubleVector2D point = p.wrappedMapProjectionToItemPosition(path.at(i)); + + point = point - origin; // (0,0) if point == geoLeftBound_ + + minX = qMin(point.x(), minX); + minY = qMin(point.y(), minY); + maxX = qMax(point.x(), maxX); + maxY = qMax(point.y(), maxY); + + if (i == 0) { + srcPoints_ << point.x() << point.y(); + srcPointTypes_ << QPainterPath::MoveToElement; + lastAddedPoint = point; + } else { + if ((point - lastAddedPoint).manhattanLength() > 3 || + i == path.size() - 1) { + srcPoints_ << point.x() << point.y(); + srcPointTypes_ << QPainterPath::LineToElement; + lastAddedPoint = point; + } + } + } + } + + sourceBounds_ = QRectF(QPointF(minX, minY), QPointF(maxX, maxY)); +} + +/*! + \internal +*/ +void QGeoMapPolylineGeometry::updateSourcePoints(const QGeoMap &map, + const QList &path, + const QGeoCoordinate geoLeftBound) +{ + if (!sourceDirty_) + return; + + geoLeftBound_ = geoLeftBound; + + // clear the old data and reserve enough memory + srcPoints_.clear(); + srcPoints_.reserve(path.size() * 2); + srcPointTypes_.clear(); + srcPointTypes_.reserve(path.size()); + + /* + * Approach: + * 1) project coordinates to wrapped web mercator, and do unwrapBelowX + * 2) if the scene is tilted, clip the geometry against the visible region (this may generate multiple polygons) + * 3) project the resulting geometry to screen position and calculate screen bounds + */ + + QDoubleVector2D leftBoundWrapped; + // 1, 2) + const QList > &clippedPaths = clipPath(map, path, leftBoundWrapped); + + // 3) + pathToScreen(map, clippedPaths, leftBoundWrapped); +} + +//////////////////////////////////////////////////////////////////////////// + +/*! + \internal +*/ +void QGeoMapPolylineGeometry::updateScreenPoints(const QGeoMap &map, + qreal strokeWidth, + bool adjustTranslation) +{ + if (!screenDirty_) + return; + + QPointF origin = map.geoProjection().coordinateToItemPosition(srcOrigin_, false).toPointF(); + + if (!qIsFinite(origin.x()) || !qIsFinite(origin.y()) || srcPointTypes_.size() < 2) { // the line might have been clipped away. + clear(); + return; + } + + // The geometry has already been clipped against the visible region projection in wrapped mercator space. + QVector points = srcPoints_; + QVector types = srcPointTypes_; + + QVectorPath vp(points.data(), types.size(), types.data()); + QTriangulatingStroker ts; + // As of Qt5.11, the clip argument is not actually used, in the call below. + ts.process(vp, QPen(QBrush(Qt::black), strokeWidth), QRectF(), QPainter::Qt4CompatiblePainting); + + clear(); + + // Nothing is on the screen + if (ts.vertexCount() == 0) + return; + + // QTriangulatingStroker#vertexCount is actually the length of the array, + // not the number of vertices + screenVertices_.reserve(ts.vertexCount()); + + QRectF bb; + + QPointF pt; + const float *vs = ts.vertices(); + for (int i = 0; i < (ts.vertexCount()/2*2); i += 2) { + pt = QPointF(vs[i], vs[i + 1]); + screenVertices_ << pt; + + if (!qIsFinite(pt.x()) || !qIsFinite(pt.y())) + break; + + if (!bb.contains(pt)) { + if (pt.x() < bb.left()) + bb.setLeft(pt.x()); + + if (pt.x() > bb.right()) + bb.setRight(pt.x()); + + if (pt.y() < bb.top()) + bb.setTop(pt.y()); + + if (pt.y() > bb.bottom()) + bb.setBottom(pt.y()); + } + } + + screenBounds_ = bb; + const QPointF strokeOffset = (adjustTranslation) ? QPointF(strokeWidth, strokeWidth) : QPointF(); + this->translate( -1 * sourceBounds_.topLeft() + strokeOffset); +} + +void QGeoMapPolylineGeometry::clearSource() +{ + srcPoints_.clear(); + srcPointTypes_.clear(); +} + +bool QGeoMapPolylineGeometry::contains(const QPointF &point) const +{ + // screenOutline_.contains(screenPoint) doesn't work, as, it appears, that + // screenOutline_ for QGeoMapPolylineGeometry is empty (QRectF(0,0 0x0)) + const QVector &verts = vertices(); + QPolygonF tri; + for (int i = 0; i < verts.size(); ++i) { + tri << verts[i]; + if (tri.size() == 3) { + if (tri.containsPoint(point,Qt::OddEvenFill)) + return true; + tri.remove(0); + } + } + + return false; +} + +QDeclarativePolylineMapItem::QDeclarativePolylineMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), line_(this), dirtyMaterial_(true), updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&line_, SIGNAL(colorChanged(QColor)), + this, SLOT(updateAfterLinePropertiesChanged())); + QObject::connect(&line_, SIGNAL(widthChanged(qreal)), + this, SLOT(updateAfterLinePropertiesChanged())); +} + +QDeclarativePolylineMapItem::~QDeclarativePolylineMapItem() +{ +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::updateAfterLinePropertiesChanged() +{ + // mark dirty just in case we're a width change + geometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (map) { + regenerateCache(); + geometry_.markSourceDirty(); + polishAndUpdate(); + } +} + +/*! + \qmlproperty list MapPolyline::path + + This property holds the ordered list of coordinates which + define the polyline. +*/ + +QJSValue QDeclarativePolylineMapItem::path() const +{ + return fromList(this, geopath_.path()); +} + +void QDeclarativePolylineMapItem::setPath(const QJSValue &value) +{ + if (!value.isArray()) + return; + + setPathFromGeoList(toList(this, value)); +} + +/*! + \qmlmethod int MapPolyline::setPath(geopath path) + + Sets the \l path using a \l QGeoPath type. + + \since 5.10 + + \sa path +*/ +void QDeclarativePolylineMapItem::setPath(const QGeoPath &path) +{ + if (geopath_.path() == path.path()) + return; + + geopath_ = path; + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::setPathFromGeoList(const QList &path) +{ + if (geopath_.path() == path) + return; + + geopath_.setPath(path); + + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod int MapPolyline::pathLength() + + Returns the number of coordinates of the polyline. + + \since QtLocation 5.6 + + \sa path +*/ +int QDeclarativePolylineMapItem::pathLength() const +{ + return geopath_.path().length(); +} + +/*! + \qmlmethod void MapPolyline::addCoordinate(coordinate) + + Adds a coordinate to the end of the path. + + \sa insertCoordinate, removeCoordinate, path +*/ +void QDeclarativePolylineMapItem::addCoordinate(const QGeoCoordinate &coordinate) +{ + if (!coordinate.isValid()) + return; + + geopath_.addCoordinate(coordinate); + + updateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolyline::insertCoordinate(index, coordinate) + + Inserts a \a coordinate to the path at the given \a index. + + \since QtLocation 5.6 + + \sa addCoordinate, removeCoordinate, path +*/ +void QDeclarativePolylineMapItem::insertCoordinate(int index, const QGeoCoordinate &coordinate) +{ + if (index < 0 || index > geopath_.path().length()) + return; + + geopath_.insertCoordinate(index, coordinate); + + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolyline::replaceCoordinate(index, coordinate) + + Replaces the coordinate in the current path at the given \a index + with the new \a coordinate. + + \since QtLocation 5.6 + + \sa addCoordinate, insertCoordinate, removeCoordinate, path +*/ +void QDeclarativePolylineMapItem::replaceCoordinate(int index, const QGeoCoordinate &coordinate) +{ + if (index < 0 || index >= geopath_.path().length()) + return; + + geopath_.replaceCoordinate(index, coordinate); + + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod coordinate MapPolyline::coordinateAt(index) + + Gets the coordinate of the polyline at the given \a index. + If the index is outside the path's bounds then an invalid + coordinate is returned. + + \since QtLocation 5.6 +*/ +QGeoCoordinate QDeclarativePolylineMapItem::coordinateAt(int index) const +{ + if (index < 0 || index >= geopath_.path().length()) + return QGeoCoordinate(); + + return geopath_.coordinateAt(index); +} + +/*! + \qmlmethod coordinate MapPolyline::containsCoordinate(coordinate) + + Returns true if the given \a coordinate is part of the path. + + \since QtLocation 5.6 +*/ +bool QDeclarativePolylineMapItem::containsCoordinate(const QGeoCoordinate &coordinate) +{ + return geopath_.containsCoordinate(coordinate); +} + +/*! + \qmlmethod void MapPolyline::removeCoordinate(coordinate) + + Removes \a coordinate from the path. If there are multiple instances of the + same coordinate, the one added last is removed. + + If \a coordinate is not in the path this method does nothing. + + \sa addCoordinate, insertCoordinate, path +*/ +void QDeclarativePolylineMapItem::removeCoordinate(const QGeoCoordinate &coordinate) +{ + int length = geopath_.path().length(); + geopath_.removeCoordinate(coordinate); + if (geopath_.path().length() == length) + return; + + regenerateCache(); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlmethod void MapPolyline::removeCoordinate(index) + + Removes a coordinate from the path at the given \a index. + + If \a index is invalid then this method does nothing. + + \since QtLocation 5.6 + + \sa addCoordinate, insertCoordinate, path +*/ +void QDeclarativePolylineMapItem::removeCoordinate(int index) +{ + if (index < 0 || index >= geopath_.path().length()) + return; + + geopath_.removeCoordinate(index); + + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); +} + +/*! + \qmlpropertygroup Location::MapPolyline::line + \qmlproperty int MapPolyline::line.width + \qmlproperty color MapPolyline::line.color + + This property is part of the line property group. The line + property group holds the width and color used to draw the line. + + The width is in pixels and is independent of the zoom level of the map. + The default values correspond to a black border with a width of 1 pixel. + + For no line, use a width of 0 or a transparent color. +*/ + +QDeclarativeMapLineProperties *QDeclarativePolylineMapItem::line() +{ + return &line_; +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (!map() || !geopath_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + // TODO: change the algorithm to preserve the distances and size! + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; + + geopath_.translate(offsetLati, offsetLongi); + regenerateCache(); + geometry_.setPreserveGeometry(true, geopath_.boundingGeoRectangle().topLeft()); + markSourceDirtyAndUpdate(); + emit pathChanged(); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + geometry_.setPreserveGeometry(true, geometry_.geoLeftBound()); + markSourceDirtyAndUpdate(); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::regenerateCache() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + geopathProjected_.clear(); + geopathProjected_.reserve(geopath_.path().size()); + for (const QGeoCoordinate &c : geopath_.path()) + geopathProjected_ << p.geoToMapProjection(c); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::updateCache() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + geopathProjected_ << p.geoToMapProjection(geopath_.path().last()); +} + +/*! + \internal +*/ +void QDeclarativePolylineMapItem::updatePolish() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + if (geopath_.path().length() == 0) { // Possibly cleared + geometry_.clear(); + setWidth(0); + setHeight(0); + return; + } + + QScopedValueRollback rollback(updatingGeometry_); + updatingGeometry_ = true; + + geometry_.updateSourcePoints(*map(), geopathProjected_, geopath_.boundingGeoRectangle().topLeft()); + geometry_.updateScreenPoints(*map(), line_.width()); + + setWidth(geometry_.sourceBoundingBox().width() + 2 * line_.width()); + setHeight(geometry_.sourceBoundingBox().height() + 2 * line_.width()); + + setPositionOnMap(geometry_.origin(), -1 * geometry_.sourceBoundingBox().topLeft() + QPointF(line_.width(), line_.width())); +} + +void QDeclarativePolylineMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \internal +*/ +QSGNode *QDeclarativePolylineMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + MapPolylineNode *node = static_cast(oldNode); + + if (!node) { + node = new MapPolylineNode(); + } + + //TODO: update only material + if (geometry_.isScreenDirty() || dirtyMaterial_ || !oldNode) { + node->update(line_.color(), &geometry_); + geometry_.setPreserveGeometry(false); + geometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +bool QDeclarativePolylineMapItem::contains(const QPointF &point) const +{ + return geometry_.contains(point); +} + +const QGeoShape &QDeclarativePolylineMapItem::geoShape() const +{ + return geopath_; +} + +QGeoMap::ItemType QDeclarativePolylineMapItem::itemType() const +{ + return QGeoMap::MapPolyline; +} + +////////////////////////////////////////////////////////////////////// + +/*! + \internal +*/ +VisibleNode::VisibleNode() : m_blocked{true}, m_visible{true} +{ + +} + +VisibleNode::~VisibleNode() +{ + +} + +/*! + \internal +*/ +bool VisibleNode::subtreeBlocked() const +{ + return m_blocked || !m_visible; +} + +/*! + \internal +*/ +void VisibleNode::setSubtreeBlocked(bool blocked) +{ + m_blocked = blocked; +} + +bool VisibleNode::visible() const +{ + return m_visible; +} + +/*! + \internal +*/ +void VisibleNode::setVisible(bool visible) +{ + m_visible = visible; +} + +/*! + \internal +*/ +MapItemGeometryNode::~MapItemGeometryNode() +{ + +} + +bool MapItemGeometryNode::isSubtreeBlocked() const +{ + return subtreeBlocked(); +} + + +/*! + \internal +*/ +MapPolylineNode::MapPolylineNode() : + geometry_(QSGGeometry::defaultAttributes_Point2D(),0) +{ + geometry_.setDrawingMode(QSGGeometry::DrawTriangleStrip); + QSGGeometryNode::setMaterial(&fill_material_); + QSGGeometryNode::setGeometry(&geometry_); +} + + +/*! + \internal +*/ +MapPolylineNode::~MapPolylineNode() +{ +} + +/*! + \internal +*/ +void MapPolylineNode::update(const QColor &fillColor, + const QGeoMapItemGeometry *shape) +{ + if (shape->size() == 0) { + setSubtreeBlocked(true); + return; + } else { + setSubtreeBlocked(false); + } + + QSGGeometry *fill = QSGGeometryNode::geometry(); + shape->allocateAndFill(fill); + markDirty(DirtyGeometry); + + if (fillColor != fill_material_.color()) { + fill_material_.setColor(fillColor); + setMaterial(&fill_material_); + markDirty(DirtyMaterial); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h new file mode 100644 index 0000000..225f21d --- /dev/null +++ b/src/location/declarativemaps/qdeclarativepolylinemapitem_p.h @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPOLYLINEMAPITEM +#define QDECLARATIVEPOLYLINEMAPITEM + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class MapPolylineNode; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeMapLineProperties : public QObject +{ + Q_OBJECT + + Q_PROPERTY(qreal width READ width WRITE setWidth NOTIFY widthChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + +public: + explicit QDeclarativeMapLineProperties(QObject *parent = 0); + + QColor color() const; + void setColor(const QColor &color); + + qreal width() const; + void setWidth(qreal width); + +Q_SIGNALS: + void widthChanged(qreal width); + void colorChanged(const QColor &color); + +private: + qreal width_; + QColor color_; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPolylineGeometry : public QGeoMapItemGeometry +{ +public: + QGeoMapPolylineGeometry(); + + void updateSourcePoints(const QGeoMap &map, + const QList &path, + const QGeoCoordinate geoLeftBound); + + void updateScreenPoints(const QGeoMap &map, + qreal strokeWidth, + bool adjustTranslation = true); + + void clearSource(); + + bool contains(const QPointF &point) const override; + + QList > clipPath(const QGeoMap &map, + const QList &path, + QDoubleVector2D &leftBoundWrapped); + + void pathToScreen(const QGeoMap &map, + const QList > &clippedPaths, + const QDoubleVector2D &leftBoundWrapped); + +private: + QVector srcPoints_; + QVector srcPointTypes_; + + friend class QDeclarativeCircleMapItem; + friend class QDeclarativePolygonMapItem; + friend class QDeclarativeRectangleMapItem; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePolylineMapItem : public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + + Q_PROPERTY(QJSValue path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *line READ line CONSTANT) + +public: + explicit QDeclarativePolylineMapItem(QQuickItem *parent = 0); + ~QDeclarativePolylineMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; + //from QuickItem + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override; + + Q_INVOKABLE int pathLength() const; + Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void insertCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE void replaceCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE QGeoCoordinate coordinateAt(int index) const; + Q_INVOKABLE bool containsCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(int index); + + QJSValue path() const; + virtual void setPath(const QJSValue &value); + Q_INVOKABLE void setPath(const QGeoPath &path); + + bool contains(const QPointF &point) const override; + const QGeoShape &geoShape() const override; + QGeoMap::ItemType itemType() const override; + + QDeclarativeMapLineProperties *line(); + +Q_SIGNALS: + void pathChanged(); + +protected: + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void setPathFromGeoList(const QList &path); + void updatePolish() override; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + void updateAfterLinePropertiesChanged(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; + +private: + void regenerateCache(); + void updateCache(); + + QGeoPath geopath_; + QList geopathProjected_; + QDeclarativeMapLineProperties line_; + QColor color_; + bool dirtyMaterial_; + QGeoMapPolylineGeometry geometry_; + bool updatingGeometry_; +}; + +////////////////////////////////////////////////////////////////////// + +class Q_LOCATION_PRIVATE_EXPORT VisibleNode +{ +public: + VisibleNode(); + virtual ~VisibleNode(); + + bool subtreeBlocked() const; + void setSubtreeBlocked(bool blocked); + bool visible() const; + void setVisible(bool visible); + + bool m_blocked : 1; + bool m_visible : 1; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapItemGeometryNode : public QSGGeometryNode, public VisibleNode +{ +public: + ~MapItemGeometryNode() override; + bool isSubtreeBlocked() const override; +}; + +class Q_LOCATION_PRIVATE_EXPORT MapPolylineNode : public MapItemGeometryNode +{ +public: + MapPolylineNode(); + ~MapPolylineNode() override; + + void update(const QColor &fillColor, const QGeoMapItemGeometry *shape); + +private: + QSGFlatColorMaterial fill_material_; + QSGGeometry geometry_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeMapLineProperties) +QML_DECLARE_TYPE(QDeclarativePolylineMapItem) + +#endif /* QDECLARATIVEPOLYLINEMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp new file mode 100644 index 0000000..e90c059 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativerectanglemapitem.cpp @@ -0,0 +1,407 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativerectanglemapitem_p.h" +#include "qdeclarativepolygonmapitem_p.h" +#include "qlocationutils_p.h" +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapRectangle + \instantiates QDeclarativeRectangleMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.5 + + \brief The MapRectangle type displays a rectangle on a Map. + + The MapRectangle type displays a rectangle on a Map. Rectangles are a + special case of Polygon with exactly 4 vertices and 4 "straight" edges. In + this case, "straight" means that the top-left point has the same latitude + as the top-right point (the top edge), and the bottom-left point has the + same latitude as the bottom-right point (the bottom edge). Similarly, the + points on the left side have the same longitude, and the points on the + right side have the same longitude. + + To specify the rectangle, it requires a \l topLeft and \l bottomRight point, + both given by a \l {coordinate}. + + By default, the rectangle is displayed with transparent fill and a 1-pixel + thick black border. This can be changed using the \l color, \l border.color + and \l border.width properties. + + \note Similar to the \l MapPolygon type, MapRectangles are geographic + items, thus dragging a MapRectangle causes its vertices to be recalculated + in the geographic coordinate space. Apparent stretching of the item + occurs when dragged to the a different latitude, however, its edges + remain straight. + + \section2 Performance + + MapRectangles have a rendering cost identical to a MapPolygon with 4 + vertices. + + Like the other map objects, MapRectangle is normally drawn without a smooth + appearance. Setting the \l opacity property will force the object to be + blended, which decreases performance considerably depending on the hardware + in use. + + \section2 Example Usage + + The following snippet shows a map containing a MapRectangle, spanning + from (-27, 153) to (-28, 153.5), near Brisbane, Australia. The rectangle + is filled in green, with a 2 pixel black border. + + \code + Map { + MapRectangle { + color: 'green' + border.width: 2 + topLeft { + latitude: -27 + longitude: 153 + } + bottomRight { + latitude: -28 + longitude: 153.5 + } + } + } + \endcode + + \image api-maprectangle.png +*/ + +QDeclarativeRectangleMapItem::QDeclarativeRectangleMapItem(QQuickItem *parent) +: QDeclarativeGeoMapItemBase(parent), border_(this), color_(Qt::transparent), dirtyMaterial_(true), + updatingGeometry_(false) +{ + setFlag(ItemHasContents, true); + QObject::connect(&border_, SIGNAL(colorChanged(QColor)), + this, SLOT(markSourceDirtyAndUpdate())); + QObject::connect(&border_, SIGNAL(widthChanged(qreal)), + this, SLOT(markSourceDirtyAndUpdate())); +} + +QDeclarativeRectangleMapItem::~QDeclarativeRectangleMapItem() +{ +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) +{ + QDeclarativeGeoMapItemBase::setMap(quickMap,map); + if (!map) + return; + updatePath(); + markSourceDirtyAndUpdate(); +} + +/*! + \qmlpropertygroup Location::MapRectangle::border + \qmlproperty int MapRectangle::border.width + \qmlproperty color MapRectangle::border.color + + This property is part of the border property group. The border property group + holds the width and color used to draw the border of the rectangle. + The width is in pixels and is independent of the zoom level of the map. + + The default values correspond to a black border with a width of 1 pixel. + For no line, use a width of 0 or a transparent color. +*/ +QDeclarativeMapLineProperties *QDeclarativeRectangleMapItem::border() +{ + return &border_; +} + +/*! + \qmlproperty coordinate MapRectangle::topLeft + + This property holds the top-left coordinate of the MapRectangle which + can be used to retrieve its longitude, latitude and altitude. +*/ +void QDeclarativeRectangleMapItem::setTopLeft(const QGeoCoordinate &topLeft) +{ + if (rectangle_.topLeft() == topLeft) + return; + + rectangle_.setTopLeft(topLeft); + updatePath(); + markSourceDirtyAndUpdate(); + emit topLeftChanged(topLeft); +} + +QGeoCoordinate QDeclarativeRectangleMapItem::topLeft() +{ + return rectangle_.topLeft(); +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::markSourceDirtyAndUpdate() +{ + geometry_.markSourceDirty(); + borderGeometry_.markSourceDirty(); + polishAndUpdate(); +} + +/*! + \qmlproperty coordinate MapRectangle::bottomRight + + This property holds the bottom-right coordinate of the MapRectangle which + can be used to retrieve its longitude, latitude and altitude. +*/ +void QDeclarativeRectangleMapItem::setBottomRight(const QGeoCoordinate &bottomRight) +{ + if (rectangle_.bottomRight() == bottomRight) + return; + + rectangle_.setBottomRight(bottomRight); + updatePath(); + markSourceDirtyAndUpdate(); + emit bottomRightChanged(bottomRight); +} + +QGeoCoordinate QDeclarativeRectangleMapItem::bottomRight() +{ + return rectangle_.bottomRight(); +} + +/*! + \qmlproperty color MapRectangle::color + + This property holds the fill color of the rectangle. For no fill, use + a transparent color. +*/ +QColor QDeclarativeRectangleMapItem::color() const +{ + return color_; +} + +void QDeclarativeRectangleMapItem::setColor(const QColor &color) +{ + if (color_ == color) + return; + color_ = color; + dirtyMaterial_ = true; + polishAndUpdate(); + emit colorChanged(color_); +} + +/*! + \qmlproperty real MapRectangle::opacity + + This property holds the opacity of the item. Opacity is specified as a + number between 0 (fully transparent) and 1 (fully opaque). The default is 1. + + An item with 0 opacity will still receive mouse events. To stop mouse events, set the + visible property of the item to false. +*/ + +/*! + \internal +*/ +QSGNode *QDeclarativeRectangleMapItem::updateMapItemPaintNode(QSGNode *oldNode, UpdatePaintNodeData *data) +{ + Q_UNUSED(data); + + MapPolygonNode *node = static_cast(oldNode); + + if (!node) { + node = new MapPolygonNode(); + } + + //TODO: update only material + if (geometry_.isScreenDirty() || borderGeometry_.isScreenDirty() || dirtyMaterial_) { + node->update(color_, border_.color(), &geometry_, &borderGeometry_); + geometry_.setPreserveGeometry(false); + borderGeometry_.setPreserveGeometry(false); + geometry_.markClean(); + borderGeometry_.markClean(); + dirtyMaterial_ = false; + } + return node; +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::updatePolish() +{ + if (!map() || map()->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + if (!topLeft().isValid() || !bottomRight().isValid()) { + geometry_.clear(); + borderGeometry_.clear(); + setWidth(0); + setHeight(0); + return; + } + + const QGeoProjectionWebMercator &p = static_cast(map()->geoProjection()); + + QScopedValueRollback rollback(updatingGeometry_); + updatingGeometry_ = true; + + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + geometry_.updateSourcePoints(*map(), pathMercator_); + geometry_.updateScreenPoints(*map(), border_.width()); + + QList geoms; + geoms << &geometry_; + borderGeometry_.clear(); + + if (border_.color() != Qt::transparent && border_.width() > 0) { + QList closedPath = pathMercator_; + closedPath << closedPath.first(); + + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + const QGeoCoordinate &geometryOrigin = geometry_.origin(); + + borderGeometry_.srcPoints_.clear(); + borderGeometry_.srcPointTypes_.clear(); + + QDoubleVector2D borderLeftBoundWrapped; + QList > clippedPaths = borderGeometry_.clipPath(*map(), closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + borderGeometry_.pathToScreen(*map(), clippedPaths, borderLeftBoundWrapped); + borderGeometry_.updateScreenPoints(*map(), border_.width()); + + geoms << &borderGeometry_; + } else { + borderGeometry_.clear(); + } + } + + QRectF combined = QGeoMapItemGeometry::translateToCommonOrigin(geoms); + setWidth(combined.width() + 2 * border_.width()); + setHeight(combined.height() + 2 * border_.width()); + + setPositionOnMap(geometry_.origin(), geometry_.firstPointOffset()); +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::afterViewportChanged(const QGeoMapViewportChangeEvent &event) +{ + if (event.mapSize.width() <= 0 || event.mapSize.height() <= 0) + return; + + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + markSourceDirtyAndUpdate(); +} + +/*! + \internal +*/ +bool QDeclarativeRectangleMapItem::contains(const QPointF &point) const +{ + return (geometry_.contains(point) || borderGeometry_.contains(point)); +} + +const QGeoShape &QDeclarativeRectangleMapItem::geoShape() const +{ + return rectangle_; +} + +QGeoMap::ItemType QDeclarativeRectangleMapItem::itemType() const +{ + return QGeoMap::MapRectangle; +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::updatePath() +{ + if (!map()) + return; + pathMercator_.clear(); + pathMercator_ << QWebMercator::coordToMercator(rectangle_.topLeft()); + pathMercator_ << QWebMercator::coordToMercator( + QGeoCoordinate(rectangle_.topLeft().latitude(), rectangle_.bottomRight().longitude())); + pathMercator_ << QWebMercator::coordToMercator(rectangle_.bottomRight()); + pathMercator_ << QWebMercator::coordToMercator( + QGeoCoordinate(rectangle_.bottomRight().latitude(), rectangle_.topLeft().longitude())); +} + +/*! + \internal +*/ +void QDeclarativeRectangleMapItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) +{ + if (!map() || !rectangle_.isValid() || updatingGeometry_ || newGeometry.topLeft() == oldGeometry.topLeft()) { + QDeclarativeGeoMapItemBase::geometryChanged(newGeometry, oldGeometry); + return; + } + // TODO: change the algorithm to preserve the distances and size + QGeoCoordinate newCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(newGeometry.center()), false); + QGeoCoordinate oldCenter = map()->geoProjection().itemPositionToCoordinate(QDoubleVector2D(oldGeometry.center()), false); + if (!newCenter.isValid() || !oldCenter.isValid()) + return; + double offsetLongi = newCenter.longitude() - oldCenter.longitude(); + double offsetLati = newCenter.latitude() - oldCenter.latitude(); + if (offsetLati == 0.0 && offsetLongi == 0.0) + return; + + rectangle_.translate(offsetLati, offsetLongi); + updatePath(); + geometry_.setPreserveGeometry(true, rectangle_.topLeft()); + borderGeometry_.setPreserveGeometry(true, rectangle_.topLeft()); + markSourceDirtyAndUpdate(); + emit topLeftChanged(rectangle_.topLeft()); + emit bottomRightChanged(rectangle_.bottomRight()); + + // Not calling QDeclarativeGeoMapItemBase::geometryChanged() as it will be called from a nested + // call to this function. +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h new file mode 100644 index 0000000..c475bf7 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativerectanglemapitem_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERECTANGLEMAPITEM_H_ +#define QDECLARATIVERECTANGLEMAPITEM_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRectangleMapItem: public QDeclarativeGeoMapItemBase +{ + Q_OBJECT + + Q_PROPERTY(QGeoCoordinate topLeft READ topLeft WRITE setTopLeft NOTIFY topLeftChanged) + Q_PROPERTY(QGeoCoordinate bottomRight READ bottomRight WRITE setBottomRight NOTIFY bottomRightChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + +public: + explicit QDeclarativeRectangleMapItem(QQuickItem *parent = 0); + ~QDeclarativeRectangleMapItem(); + + virtual void setMap(QDeclarativeGeoMap *quickMap, QGeoMap *map) override; + //from QuickItem + virtual QSGNode *updateMapItemPaintNode(QSGNode *, UpdatePaintNodeData *) override; + + QGeoCoordinate topLeft(); + void setTopLeft(const QGeoCoordinate ¢er); + + QGeoCoordinate bottomRight(); + void setBottomRight(const QGeoCoordinate ¢er); + + QColor color() const; + void setColor(const QColor &color); + + QDeclarativeMapLineProperties *border(); + + bool contains(const QPointF &point) const override; + const QGeoShape &geoShape() const override; + QGeoMap::ItemType itemType() const override; + +Q_SIGNALS: + void topLeftChanged(const QGeoCoordinate &topLeft); + void bottomRightChanged(const QGeoCoordinate &bottomRight); + void colorChanged(const QColor &color); + +protected: + void updatePath(); + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; + void updatePolish() override; + +protected Q_SLOTS: + void markSourceDirtyAndUpdate(); + virtual void afterViewportChanged(const QGeoMapViewportChangeEvent &event) override; + +private: + QGeoRectangle rectangle_; + QDeclarativeMapLineProperties border_; + QColor color_; + bool dirtyMaterial_; + QGeoMapPolygonGeometry geometry_; + QGeoMapPolylineGeometry borderGeometry_; + bool updatingGeometry_; + QList pathMercator_; +}; + +////////////////////////////////////////////////////////////////////// + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeRectangleMapItem) + +#endif /* QDECLARATIVERECTANGLEMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qdeclarativeroutemapitem.cpp b/src/location/declarativemaps/qdeclarativeroutemapitem.cpp new file mode 100644 index 0000000..fd939d6 --- /dev/null +++ b/src/location/declarativemaps/qdeclarativeroutemapitem.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qdeclarativeroutemapitem_p.h" +#include "qdeclarativepolylinemapitem_p.h" +#include "qdeclarativegeoroute_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapRoute + \instantiates QDeclarativeRouteMapItem + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-maps + \since QtLocation 5.0 + + \brief The MapRoute type displays a Route on a Map. + + The MapRoute type displays a Route obtained through a RouteModel or + other means, on the Map as a Polyline following the path of the Route. + + MapRoute is really a \l MapPolyline, but with the path specified using the + \l route property instead of directly in \l {coordinate}{coordinates}. + + By default, the route is displayed as a 1-pixel thick black line. This can + be changed using the \l line.width and \l line.color properties. + + \section2 Performance + + For notes about the performance on MapRoute, refer to the documentation for + \l MapPolyline. + + \section2 Example Usage + + Here is how to draw a \l{Route}{route} on a \l{Map}{map}: + + \snippet declarative/maps.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/maps.qml MapRoute +*/ + +/*! + \qmlpropertygroup Location::MapRoute::line + \qmlproperty int MapRoute::line.width + \qmlproperty color MapRoute::line.color + + This property is part of the line property group. The line + property group holds the width and color used to draw the line. + + The width is in pixels and is independent of the zoom level of the map. + The default values correspond to a black border with a width of 1 pixel. + + For no line, use a width of 0 or a transparent color. +*/ + + +QDeclarativeRouteMapItem::QDeclarativeRouteMapItem(QQuickItem *parent) +: QDeclarativePolylineMapItem(parent), route_(0) +{ + setFlag(ItemHasContents, true); +} + +QDeclarativeRouteMapItem::~QDeclarativeRouteMapItem() +{ +} + +/*! + \qmlproperty Route MapRoute::route + + This property holds the route to be drawn which can be used + to represent one geographical route. +*/ +QDeclarativeGeoRoute *QDeclarativeRouteMapItem::route() const +{ + return route_; +} + +void QDeclarativeRouteMapItem::setRoute(QDeclarativeGeoRoute *route) +{ + if (route_ == route) + return; + + route_ = route; + + connect(route_, SIGNAL(pathChanged()), this, SLOT(updateRoutePath())); + + if (route_) + setPathFromGeoList(route_->routePath()); + + emit routeChanged(route_); +} + +void QDeclarativeRouteMapItem::updateRoutePath() +{ + setPathFromGeoList(route_->routePath()); +} + +/*! + \internal void QDeclarativeRouteMapItem::setPath(const QJSValue &value) + + Used to disable path property on the RouteMapItem + */ +void QDeclarativeRouteMapItem::setPath(const QJSValue &value) +{ + Q_UNUSED(value); + qWarning() << "Can not set the path on QDeclarativeRouteMapItem." + << "Please use the route property instead."; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qdeclarativeroutemapitem_p.h b/src/location/declarativemaps/qdeclarativeroutemapitem_p.h new file mode 100644 index 0000000..a0b171b --- /dev/null +++ b/src/location/declarativemaps/qdeclarativeroutemapitem_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEROUTEMAPITEM_H_ +#define QDECLARATIVEROUTEMAPITEM_H_ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoRoute; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRouteMapItem : public QDeclarativePolylineMapItem +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeGeoRoute *route READ route WRITE setRoute NOTIFY routeChanged) + +public: + explicit QDeclarativeRouteMapItem(QQuickItem *parent = 0); + ~QDeclarativeRouteMapItem(); + + QDeclarativeGeoRoute *route() const; + void setRoute(QDeclarativeGeoRoute *route); + +Q_SIGNALS: + void routeChanged(const QDeclarativeGeoRoute *route); + +private slots: + void updateRoutePath(); + +protected: + void setPath(const QJSValue &value) override; + +private: + QDeclarativeGeoRoute *route_; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeRouteMapItem) + +#endif /* QDECLARATIVEROUTEMAPITEM_H_ */ diff --git a/src/location/declarativemaps/qgeomapitemgeometry.cpp b/src/location/declarativemaps/qgeomapitemgeometry.cpp new file mode 100644 index 0000000..2883c2b --- /dev/null +++ b/src/location/declarativemaps/qgeomapitemgeometry.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapitemgeometry_p.h" +#include "qdeclarativegeomap_p.h" +#include "qlocationutils_p.h" +#include +#include "qdoublevector2d_p.h" +#include + +QT_BEGIN_NAMESPACE + +QGeoMapItemGeometry::QGeoMapItemGeometry() +: sourceDirty_(true), screenDirty_(true), clipToViewport_(true), preserveGeometry_(false) +{ +} + +/*! + \internal +*/ +void QGeoMapItemGeometry::translate(const QPointF &offset) +{ + for (int i = 0; i < screenVertices_.size(); ++i) + screenVertices_[i] += offset; + + firstPointOffset_ += offset; + screenOutline_.translate(offset); + screenBounds_.translate(offset); +} + +/*! + \internal +*/ +void QGeoMapItemGeometry::allocateAndFill(QSGGeometry *geom) const +{ + const QVector &vx = screenVertices_; + const QVector &ix = screenIndices_; + + if (isIndexed()) { + geom->allocate(vx.size(), ix.size()); + if (geom->indexType() == QSGGeometry::UnsignedShortType) { + quint16 *its = geom->indexDataAsUShort(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } else if (geom->indexType() == QSGGeometry::UnsignedIntType) { + quint32 *its = geom->indexDataAsUInt(); + for (int i = 0; i < ix.size(); ++i) + its[i] = ix[i]; + } + } else { + geom->allocate(vx.size()); + } + + QSGGeometry::Point2D *pts = geom->vertexDataAsPoint2D(); + for (int i = 0; i < vx.size(); ++i) + pts[i].set(vx[i].x(), vx[i].y()); +} + +/*! + \internal +*/ +QRectF QGeoMapItemGeometry::translateToCommonOrigin(const QList &geoms) +{ + QGeoCoordinate origin = geoms.at(0)->origin(); + + QPainterPath brects; + + // first get max offset + QPointF maxOffset = geoms.at(0)->firstPointOffset(); + foreach (QGeoMapItemGeometry *g, geoms) { + QPointF o = g->firstPointOffset(); + maxOffset.setX(qMax(o.x(), maxOffset.x())); + maxOffset.setY(qMax(o.y(), maxOffset.y())); + } + + // then translate everything + foreach (QGeoMapItemGeometry *g, geoms) { + g->translate(maxOffset - g->firstPointOffset()); + brects.addRect(g->sourceBoundingBox()); + } + + return brects.boundingRect(); +} + +/*! + \internal +*/ +double QGeoMapItemGeometry::geoDistanceToScreenWidth(const QGeoMap &map, + const QGeoCoordinate &fromCoord, + const QGeoCoordinate &toCoord) +{ + // Do not wrap around half the globe + Q_ASSERT(!qFuzzyCompare(fromCoord.longitude(), toCoord.longitude())); + + QGeoCoordinate mapMid = map.geoProjection().itemPositionToCoordinate(QDoubleVector2D(map.viewportWidth()/2.0, 0)); + double halfGeoDist = toCoord.longitude() - fromCoord.longitude(); + if (toCoord.longitude() < fromCoord.longitude()) + halfGeoDist += 360; + halfGeoDist /= 2.0; + QGeoCoordinate geoDelta = QGeoCoordinate(0, + QLocationUtils::wrapLong(mapMid.longitude() + halfGeoDist)); + QDoubleVector2D halfScreenDist = map.geoProjection().coordinateToItemPosition(geoDelta, false) + - QDoubleVector2D(map.viewportWidth()/2.0, 0); + return halfScreenDist.x() * 2.0; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qgeomapitemgeometry_p.h b/src/location/declarativemaps/qgeomapitemgeometry_p.h new file mode 100644 index 0000000..ab81ff0 --- /dev/null +++ b/src/location/declarativemaps/qgeomapitemgeometry_p.h @@ -0,0 +1,157 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL3$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see http://www.qt.io/terms-conditions. For further + ** information use the contact form at http://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 3 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPLv3 included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 3 requirements + ** will be met: https://www.gnu.org/licenses/lgpl.html. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 2.0 or later as published by the Free + ** Software Foundation and appearing in the file LICENSE.GPL included in + ** the packaging of this file. Please review the following information to + ** ensure the GNU General Public License version 2.0 requirements will be + ** met: http://www.gnu.org/licenses/gpl-2.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#ifndef QGEOMAPITEMGEOMETRY_H +#define QGEOMAPITEMGEOMETRY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGGeometry; +class QGeoMap; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapItemGeometry +{ +public: + QGeoMapItemGeometry(); + + inline bool isSourceDirty() const { return sourceDirty_; } + inline bool isScreenDirty() const { return screenDirty_; } + inline void markSourceDirty() { sourceDirty_ = true; screenDirty_ = true; } + inline void markScreenDirty() { screenDirty_ = true; clipToViewport_ = true; } + inline void markFullScreenDirty() { screenDirty_ = true; clipToViewport_ = false;} + inline void markClean() { screenDirty_ = (sourceDirty_ = false); clipToViewport_ = true;} + + inline void setPreserveGeometry(bool value, const QGeoCoordinate &geoLeftBound = QGeoCoordinate()) + { + preserveGeometry_ = value; + if (preserveGeometry_) + geoLeftBound_ = geoLeftBound; + } + inline QGeoCoordinate geoLeftBound() { return geoLeftBound_; } + + inline QRectF sourceBoundingBox() const { return sourceBounds_; } + inline QRectF screenBoundingBox() const { return screenBounds_; } + + inline QPointF firstPointOffset() const { return firstPointOffset_; } + void translate(const QPointF &offset); + + inline const QGeoCoordinate &origin() const { return srcOrigin_; } + + QPainterPath screenOutline() const { + return screenOutline_; + } + + virtual bool contains(const QPointF &screenPoint) const { + return screenOutline_.contains(screenPoint); + } + + inline QVector2D vertex(quint32 index) const { + return QVector2D(screenVertices_[index]); + } + + inline QVector vertices() const { return screenVertices_; } + inline QVector indices() const { return screenIndices_; } + + inline bool isIndexed() const { return (!screenIndices_.isEmpty()); } + + /* Size is # of triangles */ + inline quint32 size() const + { + if (isIndexed()) + return screenIndices_.size() / 3; + else + return screenVertices_.size() / 3; + } + + inline void clear() { firstPointOffset_ = QPointF(0,0); + screenVertices_.clear(); screenIndices_.clear(); } + + void allocateAndFill(QSGGeometry *geom) const; + + double geoDistanceToScreenWidth(const QGeoMap &map, + const QGeoCoordinate &fromCoord, + const QGeoCoordinate &toCoord); + + static QRectF translateToCommonOrigin(const QList &geoms); + +private: + QGeoMapItemGeometry(const QGeoMapItemGeometry &other); // Or else it may crash on copy + QGeoMapItemGeometry &operator= (const QGeoMapItemGeometry & other); // Or else it may crash on copy + +protected: + bool sourceDirty_; + bool screenDirty_; + bool clipToViewport_; + bool preserveGeometry_; + QGeoCoordinate geoLeftBound_; + + QPointF firstPointOffset_; + + QPainterPath screenOutline_; + + QRectF sourceBounds_; + QRectF screenBounds_; + + QGeoCoordinate srcOrigin_; + + QVector screenVertices_; + QVector screenIndices_; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPITEMGEOMETRY_H diff --git a/src/location/declarativemaps/qgeomapobject.cpp b/src/location/declarativemaps/qgeomapobject.cpp new file mode 100644 index 0000000..9a2e37b --- /dev/null +++ b/src/location/declarativemaps/qgeomapobject.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeomap_p.h" +#include "qgeomapobject_p.h" +#include "qgeomapobject_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + + \qmltype GeoMapObject + \instantiates QGeoMapObject + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + + \brief The GeoObject type is a base class for geographical objects that can be added to a map. + + The difference between a GeoMapObject and a MapItem is twofold. First, GeoMapObject are always backed + by a plugin-specific implementation and do not come with a default implementation. If a plugin does + not support a specific GeoMapObject type, adding such a GeoMapObject will have no effect. + Second, GeoMapObject are not QQuickItems, thus being a much more lightweight way to add content to + a map. + + GeoMapObject support is plugin-dependent, and is documented per plugin. +*/ + +template<> +QGeoMapObjectPrivate *QExplicitlySharedDataPointer::clone() +{ + return d->clone(); +} + +QGeoMapObject::~QGeoMapObject() +{ + +} + +/*! + Returns whether this geographical object and \a other are equal. +*/ +bool QGeoMapObject::operator ==(const QGeoMapObject &other) const +{ + return ( (d_ptr.constData() == other.d_ptr.constData()) + || (*d_ptr) == (*other.d_ptr)); +} + +/*! + Returns whether this geographical object and \a other are not equal. +*/ +bool QGeoMapObject::operator !=(const QGeoMapObject &other) const +{ + return !(operator==(other)); +} + +/*! + \internal + Returns which features are supported by the geographical object +*/ +QGeoMapObject::Features QGeoMapObject::features() const +{ + return d_ptr->features(); +} + +QGeoMapObjectPrivate *QGeoMapObject::implementation() const +{ + return d_ptr.data(); +} + +bool QGeoMapObject::setImplementation(const QExplicitlySharedDataPointer &pimpl) +{ + if (d_ptr->type() != pimpl->type()) + return false; + d_ptr = pimpl; + return true; +} + +bool QGeoMapObject::implemented() const +{ + return !d_ptr->engineName().isEmpty(); +} + +bool QGeoMapObject::visible() const +{ + return d_ptr->visible(); +} + +void QGeoMapObject::setVisible(bool visible) +{ + if (visible == d_ptr->m_visible) + return; + + const bool oldVisible = QGeoMapObject::visible(); + d_ptr->setVisible(visible); + if (d_ptr->m_componentCompleted) + setChildrenVisibility(); + if (QGeoMapObject::visible() != oldVisible) + emit visibleChanged(); +} + +void QGeoMapObject::setParentVisiblity(bool visible) +{ + if (visible == d_ptr->m_parentVisible) + return; + + const bool oldVisible = QGeoMapObject::visible(); + d_ptr->setParentVisibility(visible); + if (d_ptr->m_componentCompleted) + setChildrenVisibility(); + if (QGeoMapObject::visible() != oldVisible) + emit visibleChanged(); +} + +QGeoMapObject::Type QGeoMapObject::type() const +{ + return d_ptr->type(); +} + +QList QGeoMapObject::geoMapObjectChildren() const +{ + return quickChildren(); +} + +QGeoMapObject::QGeoMapObject(const QExplicitlySharedDataPointer &dd, QObject *parent) + : QParameterizableObject(parent), d_ptr(dd) +{ +} + +void QGeoMapObject::setChildrenVisibility() +{ + const bool v = visible(); + const QList kids = geoMapObjectChildren(); + for (auto kid : qAsConst(kids)) + kid->setParentVisiblity(v); +} + +void QGeoMapObject::classBegin() +{ + +} + +void QGeoMapObject::completeComponent() +{ + d_ptr->m_componentCompleted = true; + setChildrenVisibility(); +} + +void QGeoMapObject::componentComplete() +{ + completeComponent(); + emit completed(); +} + +void QGeoMapObject::setMap(QGeoMap *map) +{ + if (d_ptr->m_map == map) + return; + + if (map) { + bool oldVisible = d_ptr->m_visible; + bool oldCmponentCompleted = d_ptr->m_componentCompleted; + if (!map->createMapObjectImplementation(this)) + qWarning() << "Unsupported type " << type(); + // old implementation gets destroyed if/when d_ptr gets replaced + d_ptr->m_componentCompleted = oldCmponentCompleted; + d_ptr->setVisible(oldVisible); + d_ptr->setMap(map); + } + + const QList kids = geoMapObjectChildren(); + for (auto kid : kids) + kid->setMap(map); + + // Each subclass is in charge to do the equivalent of + // if (!map) { + // // Map was set, now it has ben re-set to NULL, but not inside d_ptr. + // // so m_map inside d_ptr can still be used to remove itself, inside the destructor. + // d_ptr = new QMapCircleObjectPrivateDefault(*d); + // // Old pimpl deleted implicitly by QExplicitlySharedDataPointer + // } + // After this method is called. +} + +QGeoMap *QGeoMapObject::map() const +{ + return d_ptr->m_map; +} + + +// +// QGeoMapObjectPrivate +// + +QGeoMapObjectPrivate::QGeoMapObjectPrivate() +{ +} + +QGeoMapObjectPrivate::QGeoMapObjectPrivate(QGeoMapObject *q) : q(q) +{ +} + +QGeoMapObjectPrivate::QGeoMapObjectPrivate(const QGeoMapObjectPrivate &other) + :QSharedData(other) + ,q(other.q) + ,m_componentCompleted(other.m_componentCompleted) + ,m_visible(other.m_visible) +{ + +} + +QGeoMapObjectPrivate::~QGeoMapObjectPrivate() +{ +} + +bool QGeoMapObjectPrivate::operator ==(const QGeoMapObjectPrivate &other) const +{ + return (type() == other.type() && engineName() == other.engineName() + && equals(other)); +} + +QByteArray QGeoMapObjectPrivate::engineName() const +{ + return QByteArray(); +} + +QGeoMapObject::Type QGeoMapObjectPrivate::type() const +{ + return QGeoMapObject::InvalidType; +} + +QGeoMapObject::Features QGeoMapObjectPrivate::features() const +{ + return QGeoMapObject::NoFeature; +} + +bool QGeoMapObjectPrivate::equals(const QGeoMapObjectPrivate &other) const +{ + return (visible() == other.visible() && type() == other.type() + && engineName() == other.engineName() && features() == other.features() + && m_map == other.m_map); +} + +bool QGeoMapObjectPrivate::visible() const +{ + return m_visible && m_parentVisible; +} + +void QGeoMapObjectPrivate::setVisible(bool visible) +{ + m_visible = visible; +} + +void QGeoMapObjectPrivate::setParentVisibility(bool visible) +{ + m_parentVisible = visible; +} + +void QGeoMapObjectPrivate::setMap(QGeoMap *map) +{ + m_map = map; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qgeomapobject_p.h b/src/location/declarativemaps/qgeomapobject_p.h new file mode 100644 index 0000000..e661880 --- /dev/null +++ b/src/location/declarativemaps/qgeomapobject_p.h @@ -0,0 +1,133 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPOBJECTBASE_H +#define QGEOMAPOBJECTBASE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMapObjectPrivate; +class QGeoMap; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapObject : public QParameterizableObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged) + Q_PROPERTY(Type type READ type CONSTANT) + Q_INTERFACES(QQmlParserStatus) + +public: + enum Feature { + NoFeature = 0x0, + Clickable = 0x01, + Draggable = 0x02, + AllFeatures = 0xFFFFFFFF + }; + + enum Type { + InvalidType = 0, + ViewType = 1, + RouteType = 2, + RectangleType = 3, + CircleType = 4, + PolylineType = 5, + PolygonType = 6, + IconType = 7, + UserType = 0x0100 + }; + + Q_ENUM(Type) + Q_DECLARE_FLAGS(Features, Feature) + + virtual ~QGeoMapObject(); + + bool operator == (const QGeoMapObject &other) const; + bool operator != (const QGeoMapObject &other) const; + + Features features() const; + QGeoMapObjectPrivate *implementation() const; + bool setImplementation(const QExplicitlySharedDataPointer &pimpl); + bool implemented() const; + + bool visible() const; + void setVisible(bool visible); + void setParentVisiblity(bool visible); + + Type type() const; + + virtual QList geoMapObjectChildren() const; + virtual void setMap(QGeoMap *map); + QGeoMap *map() const; + +Q_SIGNALS: + void visibleChanged(); + void selected(); + void completed(); + +protected: + QGeoMapObject(const QExplicitlySharedDataPointer &dd, QObject *parent = nullptr); + QExplicitlySharedDataPointer d_ptr; + + void setChildrenVisibility(); + + // QQmlParserStatus interface + void classBegin() override; + void componentComplete() override; + void completeComponent(); + + friend class QGeoMap; + friend class QDeclarativeGeoMap; + friend class QGeoMapLayer; + friend class QDeclarativeNavigator; +}; +QT_END_NAMESPACE + +#endif // QGEOMAPOBJECTBASE_H diff --git a/src/location/declarativemaps/qgeomapobject_p_p.h b/src/location/declarativemaps/qgeomapobject_p_p.h new file mode 100644 index 0000000..242a93c --- /dev/null +++ b/src/location/declarativemaps/qgeomapobject_p_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPOBJECTBASE_P_H +#define QGEOMAPOBJECTBASE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include +#include "qgeomapobject_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoMapObject; +class Q_LOCATION_PRIVATE_EXPORT QGeoMapObjectPrivate : public QSharedData +{ +public: + virtual ~QGeoMapObjectPrivate(); + + bool operator == (const QGeoMapObjectPrivate &other) const; + + virtual QByteArray engineName() const; + virtual QGeoMapObject::Features features() const; + virtual bool equals(const QGeoMapObjectPrivate &other) const; + virtual QGeoMapObject::Type type() const; + virtual bool visible() const; + virtual void setVisible(bool visible); + virtual void setParentVisibility(bool visible); + virtual void setMap(QGeoMap *map); + virtual QGeoMapObjectPrivate *clone() = 0; // to allow proper detaching + + QGeoMapObject *q = nullptr; + QPointer m_map; + bool m_componentCompleted = false; + bool m_visible = true; + bool m_parentVisible = true; + +protected: + QGeoMapObjectPrivate(QGeoMapObject *q); + QGeoMapObjectPrivate(const QGeoMapObjectPrivate &other); + +private: + QGeoMapObjectPrivate(); +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPOBJECTBASE_P_H diff --git a/src/location/declarativemaps/qparameterizableobject.cpp b/src/location/declarativemaps/qparameterizableobject.cpp new file mode 100644 index 0000000..0e138b8 --- /dev/null +++ b/src/location/declarativemaps/qparameterizableobject.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qparameterizableobject_p.h" +#include "qdeclarativegeomapparameter_p.h" +#include + +QT_BEGIN_NAMESPACE + +QParameterizableObject::QParameterizableObject(QObject *parent) + : QObject(parent) +{} + +void QParameterizableObject::appendChild(QObject *v) +{ + m_children.append(v); +} + +void QParameterizableObject::clearChildren() +{ + m_children.clear(); +} + +QQmlListProperty QParameterizableObject::declarativeChildren() +{ + return QQmlListProperty(this, nullptr, + &QParameterizableObject::append, + &QParameterizableObject::count, + &QParameterizableObject::at, + &QParameterizableObject::clear); +} + +void QParameterizableObject::append(QQmlListProperty *p, QObject *v) +{ + QParameterizableObject *object = static_cast(p->object); + object->appendChild(v); +} + +int QParameterizableObject::count(QQmlListProperty *p) +{ + return static_cast(p->object)->m_children.count(); +} + +QObject *QParameterizableObject::at(QQmlListProperty *p, int idx) +{ + return static_cast(p->object)->m_children.at(idx); +} + +void QParameterizableObject::clear(QQmlListProperty *p) +{ + QParameterizableObject *object = static_cast(p->object); + object->clearChildren(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qparameterizableobject_p.h b/src/location/declarativemaps/qparameterizableobject_p.h new file mode 100644 index 0000000..e450c6e --- /dev/null +++ b/src/location/declarativemaps/qparameterizableobject_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPARAMETERIZABLEOBJECT_H +#define QPARAMETERIZABLEOBJECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +class QGeoMapParameter; +class Q_LOCATION_PRIVATE_EXPORT QParameterizableObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QQmlListProperty quickChildren READ declarativeChildren DESIGNABLE false) + Q_CLASSINFO("DefaultProperty", "quickChildren") + +public: + explicit QParameterizableObject(QObject *parent = nullptr); + + template + QList quickChildren() const + { + QList res; + for (auto kid : qAsConst(m_children)) { + auto val = qobject_cast(kid); + if (val) + res.push_back(val); + } + return res; + } + +protected: + virtual void appendChild(QObject *v); + virtual void clearChildren(); + + static void append(QQmlListProperty *p, QObject *v); + static int count(QQmlListProperty *p); + static QObject *at(QQmlListProperty *p, int idx); + static void clear(QQmlListProperty *p); + + QQmlListProperty declarativeChildren(); + QList m_children; +}; +QT_END_NAMESPACE + +#endif // QPARAMETERIZABLEOBJECT_H diff --git a/src/location/declarativemaps/qquickgeomapgesturearea.cpp b/src/location/declarativemaps/qquickgeomapgesturearea.cpp new file mode 100644 index 0000000..c6f4b42 --- /dev/null +++ b/src/location/declarativemaps/qquickgeomapgesturearea.cpp @@ -0,0 +1,1863 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickgeomapgesturearea_p.h" +#include "qquickgeocoordinateanimation_p.h" +#include "qdeclarativegeomap_p.h" +#include "error_messages_p.h" + +#include +#include +#if QT_CONFIG(wheelevent) +#include +#endif +#include +#include +#include +#include +#include +#include "math.h" +#include +#include "qgeomap_p.h" +#include "qdoublevector2d_p.h" +#include "qlocationutils_p.h" +#include + + +#define QML_MAP_FLICK_DEFAULTMAXVELOCITY 2500 +#define QML_MAP_FLICK_MINIMUMDECELERATION 500 +#define QML_MAP_FLICK_DEFAULTDECELERATION 2500 +#define QML_MAP_FLICK_MAXIMUMDECELERATION 10000 + +#define QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD 38 +// FlickThreshold determines how far the "mouse" must have moved +// before we perform a flick. +static const int FlickThreshold = 20; +// Really slow flicks can be annoying. +static const qreal MinimumFlickVelocity = 75.0; +// Tolerance for detecting two finger sliding start +static const qreal MaximumParallelPosition = 40.0; // in degrees +// Tolerance for detecting parallel sliding +static const qreal MaximumParallelSlidingAngle = 4.0; // in degrees +// Tolerance for starting rotation +static const qreal MinimumRotationStartingAngle = 15.0; // in degrees +// Tolerance for starting pinch +static const qreal MinimumPinchDelta = 40; // in pixels +// Tolerance for starting tilt when sliding vertical +static const qreal MinimumPanToTiltDelta = 80; // in pixels; + +static qreal distanceBetweenTouchPoints(const QPointF &p1, const QPointF &p2) +{ + return QLineF(p1, p2).length(); +} + +static qreal angleFromPoints(const QPointF &p1, const QPointF &p2) +{ + return QLineF(p1, p2).angle(); +} + +// Keeps it in +- 180 +static qreal touchAngle(const QPointF &p1, const QPointF &p2) +{ + qreal angle = angleFromPoints(p1, p2); + if (angle > 180) + angle -= 360; + return angle; +} + +// Deals with angles crossing the +-180 edge, assumes that the delta can't be > 180 +static qreal angleDelta(const qreal angle1, const qreal angle2) +{ + qreal delta = angle1 - angle2; + if (delta > 180.0) // detect crossing angle1 positive, angle2 negative, rotation counterclockwise, difference negative + delta = angle1 - angle2 - 360.0; + else if (delta < -180.0) // detect crossing angle1 negative, angle2 positive, rotation clockwise, difference positive + delta = angle1 - angle2 + 360.0; + + return delta; +} + +static bool pointDragged(const QPointF &pOld, const QPointF &pNew) +{ + static const int startDragDistance = qApp->styleHints()->startDragDistance(); + return ( qAbs(pNew.x() - pOld.x()) > startDragDistance + || qAbs(pNew.y() - pOld.y()) > startDragDistance); +} + +static qreal vectorSize(const QPointF &vector) +{ + return std::sqrt(vector.x() * vector.x() + vector.y() * vector.y()); +} + +// This linearizes the angles around 0, and keep it linear around 180, allowing to differentiate +// touch angles that are supposed to be parallel (0 or 180 depending on what finger goes first) +static qreal touchAngleTilting(const QPointF &p1, const QPointF &p2) +{ + qreal angle = angleFromPoints(p1, p2); + if (angle > 270) + angle -= 360; + return angle; +} + +static bool movingParallelVertical(const QPointF &p1old, const QPointF &p1new, const QPointF &p2old, const QPointF &p2new) +{ + if (!pointDragged(p1old, p1new) || !pointDragged(p2old, p2new)) + return false; + + QPointF v1 = p1new - p1old; + QPointF v2 = p2new - p2old; + qreal v1v2size = vectorSize(v1 + v2); + + if (v1v2size < vectorSize(v1) || v1v2size < vectorSize(v2)) // going in opposite directions + return false; + + const qreal newAngle = touchAngleTilting(p1new, p2new); + const qreal oldAngle = touchAngleTilting(p1old, p2old); + const qreal angleDiff = angleDelta(newAngle, oldAngle); + + if (qAbs(angleDiff) > MaximumParallelSlidingAngle) + return false; + + return true; +} + +QT_BEGIN_NAMESPACE + + +/*! + \qmltype MapPinchEvent + \instantiates QGeoMapPinchEvent + \inqmlmodule QtLocation + + \brief MapPinchEvent type provides basic information about pinch event. + + MapPinchEvent type provides basic information about pinch event. They are + present in handlers of MapPinch (for example pinchStarted/pinchUpdated). Events are only + guaranteed to be valid for the duration of the handler. + + Except for the \l accepted property, all properties are read-only. + + \section2 Example Usage + + The following example enables the pinch gesture on a map and reacts to the + finished event. + + \code + Map { + id: map + gesture.enabled: true + gesture.onPinchFinished:{ + var coordinate1 = map.toCoordinate(gesture.point1) + var coordinate2 = map.toCoordinate(gesture.point2) + console.log("Pinch started at:") + console.log(" Points (" + gesture.point1.x + ", " + gesture.point1.y + ") - (" + gesture.point2.x + ", " + gesture.point2.y + ")") + console.log(" Coordinates (" + coordinate1.latitude + ", " + coordinate1.longitude + ") - (" + coordinate2.latitude + ", " + coordinate2.longitude + ")") + } + } + \endcode + + \ingroup qml-QtLocation5-maps + \since QtLocation 5.0 +*/ + +/*! + \qmlproperty QPoint QtLocation::MapPinchEvent::center + + This read-only property holds the current center point. +*/ + +/*! + \qmlproperty real QtLocation::MapPinchEvent::angle + + This read-only property holds the current angle between the two points in + the range -180 to 180. Positive values for the angles mean counter-clockwise + while negative values mean the clockwise direction. Zero degrees is at the + 3 o'clock position. +*/ + +/*! + \qmlproperty QPoint QtLocation::MapPinchEvent::point1 + \qmlproperty QPoint QtLocation::MapPinchEvent::point2 + + These read-only properties hold the actual touch points generating the pinch. + The points are not in any particular order. +*/ + +/*! + \qmlproperty int QtLocation::MapPinchEvent::pointCount + + This read-only property holds the number of points currently touched. + The MapPinch will not react until two touch points have initiated a gesture, + but will remain active until all touch points have been released. +*/ + +/*! + \qmlproperty bool QtLocation::MapPinchEvent::accepted + + Setting this property to false in the \c MapPinch::onPinchStarted handler + will result in no further pinch events being generated, and the gesture + ignored. +*/ + +/*! + \qmltype MapGestureArea + \instantiates QQuickGeoMapGestureArea + + \inqmlmodule QtLocation + + \brief The MapGestureArea type provides Map gesture interaction. + + MapGestureArea objects are used as part of a Map, to provide for panning, + flicking and pinch-to-zoom gesture used on touch displays, as well as two finger rotation + and two finger parallel vertical sliding to tilt the map. + On platforms supporting \l QWheelEvent, using the scroll wheel alone, or in combination with + key modifiers Shift or Control will also zoom, rotate or tilt the map, respectively. + + A MapGestureArea is automatically created with a new Map and available with + the \l{Map::gesture}{gesture} property. This is the only way + to create a MapGestureArea, and once created this way cannot be destroyed + without its parent Map. + + The two most commonly used properties of the MapGestureArea are the \l enabled + and \l acceptedGestures properties. Both of these must be set before a + MapGestureArea will have any effect upon interaction with the Map. + The \l flickDeceleration property controls how quickly the map pan slows after contact + is released while panning the map. + + \section2 Performance + + The MapGestureArea, when enabled, must process all incoming touch events in + order to track the shape and size of the "pinch". The overhead added on + touch events can be considered constant time. + + \section2 Example Usage + + The following example enables the pinch and pan gestures on the map, but not flicking. So the + map scrolling will halt immediately on releasing the mouse button / touch. + + \code + Map { + gesture.enabled: true + gesture.acceptedGestures: MapGestureArea.PinchGesture | MapGestureArea.PanGesture + } + \endcode + + \ingroup qml-QtLocation5-maps + \since QtLocation 5.0 +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::enabled + + This property holds whether the gestures are enabled. +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::pinchActive + + This read-only property holds whether the pinch gesture is active. +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::panActive + + This read-only property holds whether the pan gesture is active. + + \note Change notifications for this property were introduced in Qt 5.5. +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::rotationActive + + This read-only property holds whether the two-finger rotation gesture is active. + + \since QtLocation 5.9 +*/ + +/*! + \qmlproperty bool QtLocation::MapGestureArea::tiltActive + + This read-only property holds whether the two-finger tilt gesture is active. + + \since QtLocation 5.9 +*/ + +/*! + \qmlproperty real QtLocation::MapGestureArea::maximumZoomLevelChange + + This property holds the maximum zoom level change per pinch, essentially + meant to be used for setting the zoom sensitivity. + + It is an indicative measure calculated from the dimensions of the + map area, roughly corresponding how much zoom level could change with + maximum pinch zoom. Default value is 4.0, maximum value is 10.0 +*/ + +/*! + \qmlproperty real MapGestureArea::flickDeceleration + + This property holds the rate at which a flick will decelerate. + + The default value is 2500. +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::pinchStarted(PinchEvent event) + + This signal is emitted when a pinch gesture is started. + + The corresponding handler is \c onPinchStarted. + + \sa pinchUpdated, pinchFinished +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::pinchUpdated(PinchEvent event) + + This signal is emitted as the user's fingers move across the map, + after the \l pinchStarted signal is emitted. + + The corresponding handler is \c onPinchUpdated. + + \sa pinchStarted, pinchFinished +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::pinchFinished(PinchEvent event) + + This signal is emitted at the end of a pinch gesture. + + The corresponding handler is \c onPinchFinished. + + \sa pinchStarted, pinchUpdated +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::panStarted() + + This signal is emitted when the map begins to move due to user + interaction. Typically this means that the user is dragging a finger - + or a mouse with one of more mouse buttons pressed - on the map. + + The corresponding handler is \c onPanStarted. +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::panFinished() + + This signal is emitted when the map stops moving due to user + interaction. If a flick was generated, this signal is + emitted before flick starts. If a flick was not + generated, this signal is emitted when the + user stops dragging - that is a mouse or touch release. + + The corresponding handler is \c onPanFinished. + +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::flickStarted() + + This signal is emitted when the map is flicked. A flick + starts from the point where the mouse or touch was released, + while still in motion. + + The corresponding handler is \c onFlichStarted. +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::flickFinished() + + This signal is emitted when the map stops moving due to a flick. + + The corresponding handler is \c onFlickFinished. +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::rotationStarted(PinchEvent event) + + This signal is emitted when a two-finger rotation gesture is started. + + The corresponding handler is \c onRotationStarted. + + \sa rotationUpdated, rotationFinished + + \since QtLocation 5.9 +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::rotationUpdated(PinchEvent event) + + This signal is emitted as the user's fingers move across the map, + after the \l rotationStarted signal is emitted. + + The corresponding handler is \c onRotationUpdated. + + \sa rotationStarted, rotationFinished + + \since QtLocation 5.9 +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::rotationFinished(PinchEvent event) + + This signal is emitted at the end of a two-finger rotation gesture. + + The corresponding handler is \c onRotationFinished. + + \sa rotationStarted, rotationUpdated + + \since QtLocation 5.9 +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::tiltStarted(PinchEvent event) + + This signal is emitted when a two-finger tilt gesture is started. + + The corresponding handler is \c onTiltStarted. + + \sa tiltUpdated, tiltFinished + + \since QtLocation 5.9 +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::tiltUpdated(PinchEvent event) + + This signal is emitted as the user's fingers move across the map, + after the \l tiltStarted signal is emitted. + + The corresponding handler is \c onTiltUpdated. + + \sa tiltStarted, tiltFinished + + \since QtLocation 5.9 +*/ + +/*! + \qmlsignal QtLocation::MapGestureArea::tiltFinished(PinchEvent event) + + This signal is emitted at the end of a two-finger tilt gesture. + + The corresponding handler is \c onTiltFinished. + + \sa tiltStarted, tiltUpdated + + \since QtLocation 5.9 +*/ + +QQuickGeoMapGestureArea::QQuickGeoMapGestureArea(QDeclarativeGeoMap *map) + : QQuickItem(map), + m_map(0), + m_declarativeMap(map), + m_enabled(true), + m_acceptedGestures(PinchGesture | PanGesture | FlickGesture | RotationGesture | TiltGesture), + m_preventStealing(false) +{ + m_touchPointState = touchPoints0; + m_pinchState = pinchInactive; + m_flickState = flickInactive; + m_rotationState = rotationInactive; + m_tiltState = tiltInactive; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setMap(QGeoMap *map) +{ + if (m_map || !map) + return; + + m_map = map; + m_flick.m_animation = new QQuickGeoCoordinateAnimation(this); + m_flick.m_animation->setTargetObject(m_declarativeMap); + m_flick.m_animation->setProperty(QStringLiteral("center")); + m_flick.m_animation->setEasing(QEasingCurve(QEasingCurve::OutQuad)); + connect(m_flick.m_animation, &QQuickAbstractAnimation::stopped, this, &QQuickGeoMapGestureArea::handleFlickAnimationStopped); + m_map->setAcceptedGestures(panEnabled(), flickEnabled(), pinchEnabled(), rotationEnabled(), tiltEnabled()); +} + +/*! + \qmlproperty bool QtQuick::MapGestureArea::preventStealing + This property holds whether the mouse events may be stolen from this + MapGestureArea. + + If a Map is placed within an item that filters child mouse + and touch events, such as Flickable, the mouse and touch events + may be stolen from the MapGestureArea if a gesture is recognized + by the parent item, e.g. a flick gesture. If preventStealing is + set to true, no item will steal the mouse and touch events. + + Note that setting preventStealing to true once an item has started + stealing events will have no effect until the next press event. + + By default this property is false. +*/ + +bool QQuickGeoMapGestureArea::preventStealing() const +{ + return m_preventStealing; +} + +void QQuickGeoMapGestureArea::setPreventStealing(bool prevent) +{ + if (prevent != m_preventStealing) { + m_preventStealing = prevent; + m_declarativeMap->setKeepMouseGrab(m_preventStealing && m_enabled); + m_declarativeMap->setKeepTouchGrab(m_preventStealing && m_enabled); + emit preventStealingChanged(); + } +} + +QQuickGeoMapGestureArea::~QQuickGeoMapGestureArea() +{ +} + +/*! + \qmlproperty enumeration QtLocation::MapGestureArea::acceptedGestures + + This property holds the gestures that will be active. By default + all gestures are enabled. + + \list + \li MapGestureArea.NoGesture - Don't support any additional gestures (value: 0x0000). + \li MapGestureArea.PinchGesture - Support the map pinch gesture (value: 0x0001). + \li MapGestureArea.PanGesture - Support the map pan gesture (value: 0x0002). + \li MapGestureArea.FlickGesture - Support the map flick gesture (value: 0x0004). + \li MapGestureArea.RotationGesture - Support the map rotation gesture (value: 0x0008). + \li MapGestureArea.TiltGesture - Support the map tilt gesture (value: 0x0010). + \endlist +*/ + +QQuickGeoMapGestureArea::AcceptedGestures QQuickGeoMapGestureArea::acceptedGestures() const +{ + return m_acceptedGestures; +} + + +void QQuickGeoMapGestureArea::setAcceptedGestures(AcceptedGestures acceptedGestures) +{ + if (acceptedGestures == m_acceptedGestures) + return; + m_acceptedGestures = acceptedGestures; + + if (enabled()) { + setPanEnabled(acceptedGestures & PanGesture); + setFlickEnabled(acceptedGestures & FlickGesture); + setPinchEnabled(acceptedGestures & PinchGesture); + setRotationEnabled(acceptedGestures & RotationGesture); + setTiltEnabled(acceptedGestures & TiltGesture); + } + + if (m_map) + m_map->setAcceptedGestures(panEnabled(), flickEnabled(), pinchEnabled(), rotationEnabled(), tiltEnabled()); + + emit acceptedGesturesChanged(); +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::isPinchActive() const +{ + return m_pinchState == pinchActive; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::isRotationActive() const +{ + return m_rotationState == rotationActive; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::isTiltActive() const +{ + return m_tiltState == tiltActive; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::isPanActive() const +{ + return m_flickState == panActive || m_flickState == flickActive; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::enabled() const +{ + return m_enabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setEnabled(bool enabled) +{ + if (enabled == m_enabled) + return; + m_enabled = enabled; + + if (enabled) { + setPanEnabled(m_acceptedGestures & PanGesture); + setFlickEnabled(m_acceptedGestures & FlickGesture); + setPinchEnabled(m_acceptedGestures & PinchGesture); + setRotationEnabled(m_acceptedGestures & RotationGesture); + setTiltEnabled(m_acceptedGestures & TiltGesture); + } else { + setPanEnabled(false); + setFlickEnabled(false); + setPinchEnabled(false); + setRotationEnabled(false); + setTiltEnabled(false); + } + if (m_map) + m_map->setAcceptedGestures(panEnabled(), flickEnabled(), pinchEnabled(), rotationEnabled(), tiltEnabled()); + + emit enabledChanged(); +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::pinchEnabled() const +{ + return m_pinch.m_pinchEnabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setPinchEnabled(bool enabled) +{ + m_pinch.m_pinchEnabled = enabled; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::rotationEnabled() const +{ + return m_pinch.m_rotationEnabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setRotationEnabled(bool enabled) +{ + m_pinch.m_rotationEnabled = enabled; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::tiltEnabled() const +{ + return m_pinch.m_tiltEnabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setTiltEnabled(bool enabled) +{ + m_pinch.m_tiltEnabled = enabled; +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::panEnabled() const +{ + return m_flick.m_panEnabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setPanEnabled(bool enabled) +{ + if (enabled == m_flick.m_panEnabled) + return; + m_flick.m_panEnabled = enabled; + + // unlike the pinch, the pan existing functionality is to stop immediately + if (!enabled) { + stopPan(); + m_flickState = flickInactive; + } +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::flickEnabled() const +{ + return m_flick.m_flickEnabled; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setFlickEnabled(bool enabled) +{ + if (enabled == m_flick.m_flickEnabled) + return; + m_flick.m_flickEnabled = enabled; + // unlike the pinch, the flick existing functionality is to stop immediately + if (!enabled) { + bool stateActive = (m_flickState != flickInactive); + stopFlick(); + if (stateActive) { + if (m_flick.m_panEnabled) + m_flickState = panActive; + else + m_flickState = flickInactive; + } + } +} + +/*! + \internal + Used internally to set the minimum zoom level of the gesture area. + The caller is responsible to only send values that are valid + for the map plugin. Negative values are ignored. + */ +void QQuickGeoMapGestureArea::setMinimumZoomLevel(qreal min) +{ + // TODO: remove m_zoom.m_minimum and m_maximum and use m_declarativeMap directly instead. + if (min >= 0) + m_pinch.m_zoom.m_minimum = min; +} + +/*! + \internal + */ +qreal QQuickGeoMapGestureArea::minimumZoomLevel() const +{ + return m_pinch.m_zoom.m_minimum; +} + +/*! + \internal + Used internally to set the maximum zoom level of the gesture area. + The caller is responsible to only send values that are valid + for the map plugin. Negative values are ignored. + */ +void QQuickGeoMapGestureArea::setMaximumZoomLevel(qreal max) +{ + if (max >= 0) + m_pinch.m_zoom.m_maximum = max; +} + +/*! + \internal + */ +qreal QQuickGeoMapGestureArea::maximumZoomLevel() const +{ + return m_pinch.m_zoom.m_maximum; +} + +/*! + \internal +*/ +qreal QQuickGeoMapGestureArea::maximumZoomLevelChange() const +{ + return m_pinch.m_zoom.maximumChange; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setMaximumZoomLevelChange(qreal maxChange) +{ + if (maxChange == m_pinch.m_zoom.maximumChange || maxChange < 0.1 || maxChange > 10.0) + return; + m_pinch.m_zoom.maximumChange = maxChange; + emit maximumZoomLevelChangeChanged(); +} + +/*! + \internal +*/ +qreal QQuickGeoMapGestureArea::flickDeceleration() const +{ + return m_flick.m_deceleration; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::setFlickDeceleration(qreal deceleration) +{ + if (deceleration < QML_MAP_FLICK_MINIMUMDECELERATION) + deceleration = QML_MAP_FLICK_MINIMUMDECELERATION; + else if (deceleration > QML_MAP_FLICK_MAXIMUMDECELERATION) + deceleration = QML_MAP_FLICK_MAXIMUMDECELERATION; + if (deceleration == m_flick.m_deceleration) + return; + m_flick.m_deceleration = deceleration; + emit flickDecelerationChanged(); +} + +/*! + \internal +*/ +QTouchEvent::TouchPoint* createTouchPointFromMouseEvent(QMouseEvent *event, Qt::TouchPointState state) +{ + // this is only partially filled. But since it is only partially used it works + // more robust would be to store a list of QPointFs rather than TouchPoints + QTouchEvent::TouchPoint* newPoint = new QTouchEvent::TouchPoint(); + newPoint->setPos(event->localPos()); + newPoint->setScenePos(event->windowPos()); + newPoint->setScreenPos(event->screenPos()); + newPoint->setState(state); + newPoint->setId(0); + return newPoint; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMousePressEvent(QMouseEvent *event) +{ + if (m_map && m_map->handleEvent(event)) { + event->accept(); + return; + } + + m_mousePoint.reset(createTouchPointFromMouseEvent(event, Qt::TouchPointPressed)); + if (m_touchPoints.isEmpty()) + update(); + event->accept(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMouseMoveEvent(QMouseEvent *event) +{ + if (m_map && m_map->handleEvent(event)) { + event->accept(); + return; + } + + m_mousePoint.reset(createTouchPointFromMouseEvent(event, Qt::TouchPointMoved)); + if (m_touchPoints.isEmpty()) + update(); + event->accept(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMouseReleaseEvent(QMouseEvent *event) +{ + if (m_map && m_map->handleEvent(event)) { + event->accept(); + return; + } + + if (!m_mousePoint.isNull()) { + //this looks super ugly , however is required in case we do not get synthesized MouseReleaseEvent + //and we reset the point already in handleTouchUngrabEvent + m_mousePoint.reset(createTouchPointFromMouseEvent(event, Qt::TouchPointReleased)); + if (m_touchPoints.isEmpty()) + update(); + } + event->accept(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleMouseUngrabEvent() +{ + + if (m_touchPoints.isEmpty() && !m_mousePoint.isNull()) { + m_mousePoint.reset(); + update(); + } else { + m_mousePoint.reset(); + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleTouchUngrabEvent() +{ + m_touchPoints.clear(); + //this is needed since in some cases mouse release is not delivered + //(second touch point breaks mouse synthesized events) + m_mousePoint.reset(); + update(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::handleTouchEvent(QTouchEvent *event) +{ + if (m_map && m_map->handleEvent(event)) { + event->accept(); + return; + } + + m_touchPoints.clear(); + m_mousePoint.reset(); + + for (int i = 0; i < event->touchPoints().count(); ++i) { + auto point = event->touchPoints().at(i); + if (point.state() != Qt::TouchPointReleased) + m_touchPoints << point; + } + if (event->touchPoints().count() >= 2) + event->accept(); + else + event->ignore(); + update(); +} + +#if QT_CONFIG(wheelevent) +void QQuickGeoMapGestureArea::handleWheelEvent(QWheelEvent *event) +{ + if (!m_map) + return; + + if (m_map->handleEvent(event)) { + event->accept(); + return; + } + + const QGeoCoordinate &wheelGeoPos = m_declarativeMap->toCoordinate(event->posF(), false); + const QPointF &preZoomPoint = event->posF(); + + // Not using AltModifier as, for some reason, it causes angleDelta to be 0 + if (event->modifiers() & Qt::ShiftModifier && rotationEnabled()) { + emit rotationStarted(&m_pinch.m_event); + // First set bearing + const double bearingDelta = event->angleDelta().y() * qreal(0.05); + m_declarativeMap->setBearing(m_declarativeMap->bearing() + bearingDelta, wheelGeoPos); + emit rotationUpdated(&m_pinch.m_event); + emit rotationFinished(&m_pinch.m_event); + } else if (event->modifiers() & Qt::ControlModifier && tiltEnabled()) { + emit tiltStarted(&m_pinch.m_event); + const double tiltDelta = event->angleDelta().y() * qreal(0.05); + m_declarativeMap->setTilt(m_declarativeMap->tilt() + tiltDelta); + emit tiltUpdated(&m_pinch.m_event); + emit tiltFinished(&m_pinch.m_event); + } else if (pinchEnabled()) { + const double zoomLevelDelta = event->angleDelta().y() * qreal(0.001); + // Gesture area should always honor maxZL, but Map might not. + m_declarativeMap->setZoomLevel(qMin(m_declarativeMap->zoomLevel() + zoomLevelDelta, maximumZoomLevel()), + false); + const QPointF &postZoomPoint = m_declarativeMap->fromCoordinate(wheelGeoPos, false); + + if (preZoomPoint != postZoomPoint) // need to re-anchor the wheel geoPos to the event position + m_declarativeMap->alignCoordinateToPoint(wheelGeoPos, preZoomPoint); + } + event->accept(); +} +#endif + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::clearTouchData() +{ + m_flickVector = QVector2D(); + m_touchPointsCentroid.setX(0); + m_touchPointsCentroid.setY(0); + m_touchCenterCoord.setLongitude(0); + m_touchCenterCoord.setLatitude(0); + m_startCoord.setLongitude(0); + m_startCoord.setLatitude(0); +} + + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateFlickParameters(const QPointF &pos) +{ + // Take velocity samples every sufficient period of time, used later to determine the flick + // duration and speed (when mouse is released). + qreal elapsed = qreal(m_lastPosTime.elapsed()); + + if (elapsed >= QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD) { + elapsed /= 1000.; + qreal vel = distanceBetweenTouchPoints(pos, m_lastPos) / elapsed; + m_flickVector = (QVector2D(pos) - QVector2D(m_lastPos)).normalized(); + m_flickVector *= qBound(-m_flick.m_maxVelocity, vel, m_flick.m_maxVelocity); + + m_lastPos = pos; + m_lastPosTime.restart(); + } +} + +void QQuickGeoMapGestureArea::setTouchPointState(const QQuickGeoMapGestureArea::TouchPointState state) +{ + m_touchPointState = state; +} + +void QQuickGeoMapGestureArea::setFlickState(const QQuickGeoMapGestureArea::FlickState state) +{ + m_flickState = state; +} + +void QQuickGeoMapGestureArea::setTiltState(const QQuickGeoMapGestureArea::TiltState state) +{ + m_tiltState = state; +} + +void QQuickGeoMapGestureArea::setRotationState(const QQuickGeoMapGestureArea::RotationState state) +{ + m_rotationState = state; +} + +void QQuickGeoMapGestureArea::setPinchState(const QQuickGeoMapGestureArea::PinchState state) +{ + m_pinchState = state; +} + +/*! + \internal +*/ + +bool QQuickGeoMapGestureArea::isActive() const +{ + return isPanActive() || isPinchActive() || isRotationActive() || isTiltActive(); +} + +/*! + \internal +*/ +// simplify the gestures by using a state-machine format (easy to move to a future state machine) +void QQuickGeoMapGestureArea::update() +{ + if (!m_map) + return; + // First state machine is for the number of touch points + + //combine touch with mouse event + m_allPoints.clear(); + m_allPoints << m_touchPoints; + if (m_allPoints.isEmpty() && !m_mousePoint.isNull()) + m_allPoints << *m_mousePoint.data(); + + touchPointStateMachine(); + + // Parallel state machine for tilt. Tilt goes first as it blocks anything else, when started. + // But tilting can also only start if nothing else is active. + if (isTiltActive() || m_pinch.m_tiltEnabled) + tiltStateMachine(); + + // Parallel state machine for pinch + if (isPinchActive() || m_pinch.m_pinchEnabled) + pinchStateMachine(); + + // Parallel state machine for rotation. + if (isRotationActive() || m_pinch.m_rotationEnabled) + rotationStateMachine(); + + // Parallel state machine for pan (since you can pan at the same time as pinching) + // The stopPan function ensures that pan stops immediately when disabled, + // but the isPanActive() below allows pan continue its current gesture if you disable + // the whole gesture. + // Pan goes last because it does reanchoring in updatePan() which makes the map + // properly rotate around the touch point centroid. + if (isPanActive() || m_flick.m_flickEnabled || m_flick.m_panEnabled) + panStateMachine(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::touchPointStateMachine() +{ + // Transitions: + switch (m_touchPointState) { + case touchPoints0: + if (m_allPoints.count() == 1) { + clearTouchData(); + startOneTouchPoint(); + setTouchPointState(touchPoints1); + } else if (m_allPoints.count() >= 2) { + clearTouchData(); + startTwoTouchPoints(); + setTouchPointState(touchPoints2); + } + break; + case touchPoints1: + if (m_allPoints.count() == 0) { + setTouchPointState(touchPoints0); + } else if (m_allPoints.count() == 2) { + m_touchCenterCoord = m_declarativeMap->toCoordinate(m_touchPointsCentroid, false); + startTwoTouchPoints(); + setTouchPointState(touchPoints2); + } + break; + case touchPoints2: + if (m_allPoints.count() == 0) { + setTouchPointState(touchPoints0); + } else if (m_allPoints.count() == 1) { + m_touchCenterCoord = m_declarativeMap->toCoordinate(m_touchPointsCentroid, false); + startOneTouchPoint(); + setTouchPointState(touchPoints1); + } + break; + }; + + // Update + switch (m_touchPointState) { + case touchPoints0: + break; // do nothing if no touch points down + case touchPoints1: + updateOneTouchPoint(); + break; + case touchPoints2: + updateTwoTouchPoints(); + break; + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startOneTouchPoint() +{ + m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_lastPos = m_sceneStartPoint1; + m_lastPosTime.start(); + QGeoCoordinate startCoord = m_declarativeMap->toCoordinate(m_sceneStartPoint1, false); + // ensures a smooth transition for panning + m_startCoord.setLongitude(m_startCoord.longitude() + startCoord.longitude() - + m_touchCenterCoord.longitude()); + m_startCoord.setLatitude(m_startCoord.latitude() + startCoord.latitude() - + m_touchCenterCoord.latitude()); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateOneTouchPoint() +{ + m_touchPointsCentroid = mapFromScene(m_allPoints.at(0).scenePos()); + updateFlickParameters(m_touchPointsCentroid); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startTwoTouchPoints() +{ + m_sceneStartPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_sceneStartPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + QPointF startPos = (m_sceneStartPoint1 + m_sceneStartPoint2) * 0.5; + m_lastPos = startPos; + m_lastPosTime.start(); + QGeoCoordinate startCoord = m_declarativeMap->toCoordinate(startPos, false); + m_startCoord.setLongitude(m_startCoord.longitude() + startCoord.longitude() - + m_touchCenterCoord.longitude()); + m_startCoord.setLatitude(m_startCoord.latitude() + startCoord.latitude() - + m_touchCenterCoord.latitude()); + m_twoTouchAngleStart = touchAngle(m_sceneStartPoint1, m_sceneStartPoint2); // Initial angle used for calculating rotation + m_distanceBetweenTouchPointsStart = distanceBetweenTouchPoints(m_sceneStartPoint1, m_sceneStartPoint2); + m_twoTouchPointsCentroidStart = (m_sceneStartPoint1 + m_sceneStartPoint2) / 2; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateTwoTouchPoints() +{ + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos()); + m_distanceBetweenTouchPoints = distanceBetweenTouchPoints(p1, p2); + m_touchPointsCentroid = (p1 + p2) / 2; + updateFlickParameters(m_touchPointsCentroid); + + m_twoTouchAngle = touchAngle(p1, p2); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::tiltStateMachine() +{ + TiltState lastState = m_tiltState; + // Transitions: + switch (m_tiltState) { + case tiltInactive: + if (m_allPoints.count() >= 2) { + if (!isRotationActive() && !isPinchActive() && canStartTilt()) { // only gesture that can be overridden: pan/flick + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startTilt(); + setTiltState(tiltActive); + } else { + setTiltState(tiltInactiveTwoPoints); + } + } + break; + case tiltInactiveTwoPoints: + if (m_allPoints.count() <= 1) { + setTiltState(tiltInactive); + } else { + if (!isRotationActive() && !isPinchActive() && canStartTilt()) { // only gesture that can be overridden: pan/flick + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startTilt(); + setTiltState(tiltActive); + } + } + break; + case tiltActive: + if (m_allPoints.count() <= 1) { + setTiltState(tiltInactive); + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + m_declarativeMap->setKeepTouchGrab(m_preventStealing); + endTilt(); + } + break; + } + // This line implements an exclusive state machine, where the transitions and updates don't + // happen on the same frame + if (m_tiltState != lastState) { + emit tiltActiveChanged(); + return; + } + + // Update + switch (m_tiltState) { + case tiltInactive: + case tiltInactiveTwoPoints: + break; // do nothing + case tiltActive: + updateTilt(); + break; + } +} + +bool validateTouchAngleForTilting(const qreal angle) +{ + return ((qAbs(angle) - 180.0) < MaximumParallelPosition) || (qAbs(angle) < MaximumParallelPosition); +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::canStartTilt() +{ + if (m_allPoints.count() >= 2) { + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos()); + if (validateTouchAngleForTilting(m_twoTouchAngle) + && movingParallelVertical(m_sceneStartPoint1, p1, m_sceneStartPoint2, p2) + && qAbs(m_twoTouchPointsCentroidStart.y() - m_touchPointsCentroid.y()) > MinimumPanToTiltDelta) { + m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + emit tiltStarted(&m_pinch.m_event); + return true; + } + } + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startTilt() +{ + if (isPanActive()) { + stopPan(); + setFlickState(flickInactive); + } + + m_pinch.m_tilt.m_startTouchCentroid = m_touchPointsCentroid; + m_pinch.m_tilt.m_startTilt = m_declarativeMap->tilt(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateTilt() +{ + // Calculate the new tilt + qreal verticalDisplacement = (m_touchPointsCentroid - m_pinch.m_tilt.m_startTouchCentroid).y(); + + // Approach: 10pixel = 1 degree. + qreal tilt = verticalDisplacement / 10.0; + qreal newTilt = m_pinch.m_tilt.m_startTilt - tilt; + m_declarativeMap->setTilt(newTilt); + + m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + m_pinch.m_event.setPoint1(m_pinch.m_lastPoint1); + m_pinch.m_event.setPoint2(m_pinch.m_lastPoint2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + + emit tiltUpdated(&m_pinch.m_event); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::endTilt() +{ + QPointF p1 = mapFromScene(m_pinch.m_lastPoint1); + QPointF p2 = mapFromScene(m_pinch.m_lastPoint2); + m_pinch.m_event.setCenter((p1 + p2) / 2); + m_pinch.m_event.setAngle(m_pinch.m_lastAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setAccepted(true); + m_pinch.m_event.setPointCount(0); + emit tiltFinished(&m_pinch.m_event); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::rotationStateMachine() +{ + RotationState lastState = m_rotationState; + // Transitions: + switch (m_rotationState) { + case rotationInactive: + if (m_allPoints.count() >= 2) { + if (!isTiltActive() && canStartRotation()) { + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startRotation(); + setRotationState(rotationActive); + } else { + setRotationState(rotationInactiveTwoPoints); + } + } + break; + case rotationInactiveTwoPoints: + if (m_allPoints.count() <= 1) { + setRotationState(rotationInactive); + } else { + if (!isTiltActive() && canStartRotation()) { + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startRotation(); + setRotationState(rotationActive); + } + } + break; + case rotationActive: + if (m_allPoints.count() <= 1) { + setRotationState(rotationInactive); + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + m_declarativeMap->setKeepTouchGrab(m_preventStealing); + endRotation(); + } + break; + } + // This line implements an exclusive state machine, where the transitions and updates don't + // happen on the same frame + if (m_rotationState != lastState) { + emit rotationActiveChanged(); + return; + } + + // Update + switch (m_rotationState) { + case rotationInactive: + case rotationInactiveTwoPoints: + break; // do nothing + case rotationActive: + updateRotation(); + break; + } +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::canStartRotation() +{ + if (m_allPoints.count() >= 2) { + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos()); + if (pointDragged(m_sceneStartPoint1, p1) || pointDragged(m_sceneStartPoint2, p2)) { + qreal delta = angleDelta(m_twoTouchAngleStart, m_twoTouchAngle); + if (qAbs(delta) < MinimumRotationStartingAngle) { + return false; + } + m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + emit rotationStarted(&m_pinch.m_event); + return m_pinch.m_event.accepted(); + } + } + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startRotation() +{ + m_pinch.m_rotation.m_startBearing = m_declarativeMap->bearing(); + m_pinch.m_rotation.m_previousTouchAngle = m_twoTouchAngle; + m_pinch.m_rotation.m_totalAngle = 0.0; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updateRotation() +{ + // Calculate the new bearing + qreal angle = angleDelta(m_pinch.m_rotation.m_previousTouchAngle, m_twoTouchAngle); + if (qAbs(angle) < 0.2) // avoiding too many updates + return; + + m_pinch.m_rotation.m_previousTouchAngle = m_twoTouchAngle; + m_pinch.m_rotation.m_totalAngle += angle; + qreal newBearing = m_pinch.m_rotation.m_startBearing - m_pinch.m_rotation.m_totalAngle; + m_declarativeMap->setBearing(newBearing); + + m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + m_pinch.m_event.setPoint1(m_pinch.m_lastPoint1); + m_pinch.m_event.setPoint2(m_pinch.m_lastPoint2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + + emit rotationUpdated(&m_pinch.m_event); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::endRotation() +{ + QPointF p1 = mapFromScene(m_pinch.m_lastPoint1); + QPointF p2 = mapFromScene(m_pinch.m_lastPoint2); + m_pinch.m_event.setCenter((p1 + p2) / 2); + m_pinch.m_event.setAngle(m_pinch.m_lastAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setAccepted(true); + m_pinch.m_event.setPointCount(0); + emit rotationFinished(&m_pinch.m_event); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::pinchStateMachine() +{ + PinchState lastState = m_pinchState; + // Transitions: + switch (m_pinchState) { + case pinchInactive: + if (m_allPoints.count() >= 2) { + if (!isTiltActive() && canStartPinch()) { + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startPinch(); + setPinchState(pinchActive); + } else { + setPinchState(pinchInactiveTwoPoints); + } + } + break; + case pinchInactiveTwoPoints: + if (m_allPoints.count() <= 1) { + setPinchState(pinchInactive); + } else { + if (!isTiltActive() && canStartPinch()) { + m_declarativeMap->setKeepMouseGrab(true); + m_declarativeMap->setKeepTouchGrab(true); + startPinch(); + setPinchState(pinchActive); + } + } + break; + case pinchActive: + if (m_allPoints.count() <= 1) { // Once started, pinch goes off only when finger(s) are release + setPinchState(pinchInactive); + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + m_declarativeMap->setKeepTouchGrab(m_preventStealing); + endPinch(); + } + break; + } + // This line implements an exclusive state machine, where the transitions and updates don't + // happen on the same frame + if (m_pinchState != lastState) { + emit pinchActiveChanged(); + return; + } + + // Update + switch (m_pinchState) { + case pinchInactive: + case pinchInactiveTwoPoints: + break; // do nothing + case pinchActive: + updatePinch(); + break; + } +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::canStartPinch() +{ + if (m_allPoints.count() >= 2) { + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + QPointF p2 = mapFromScene(m_allPoints.at(1).scenePos()); + if (qAbs(m_distanceBetweenTouchPoints - m_distanceBetweenTouchPointsStart) > MinimumPinchDelta) { + m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + emit pinchStarted(&m_pinch.m_event); + return m_pinch.m_event.accepted(); + } + } + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startPinch() +{ + m_pinch.m_startDist = m_distanceBetweenTouchPoints; + m_pinch.m_zoom.m_previous = m_declarativeMap->zoomLevel(); + m_pinch.m_lastAngle = m_twoTouchAngle; + + m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + + m_pinch.m_zoom.m_start = m_declarativeMap->zoomLevel(); +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updatePinch() +{ + // Calculate the new zoom level if we have distance ( >= 2 touchpoints), otherwise stick with old. + qreal newZoomLevel = m_pinch.m_zoom.m_previous; + if (m_distanceBetweenTouchPoints) { + newZoomLevel = + // How much further/closer the current touchpoints are (in pixels) compared to pinch start + ((m_distanceBetweenTouchPoints - m_pinch.m_startDist) * + // How much one pixel corresponds in units of zoomlevel (and multiply by above delta) + (m_pinch.m_zoom.maximumChange / ((width() + height()) / 2))) + + // Add to starting zoom level. Sign of (dist-pinchstartdist) takes care of zoom in / out + m_pinch.m_zoom.m_start; + } + + m_pinch.m_event.setCenter(mapFromScene(m_touchPointsCentroid)); + m_pinch.m_event.setAngle(m_twoTouchAngle); + + m_pinch.m_lastPoint1 = mapFromScene(m_allPoints.at(0).scenePos()); + m_pinch.m_lastPoint2 = mapFromScene(m_allPoints.at(1).scenePos()); + m_pinch.m_event.setPoint1(m_pinch.m_lastPoint1); + m_pinch.m_event.setPoint2(m_pinch.m_lastPoint2); + m_pinch.m_event.setPointCount(m_allPoints.count()); + m_pinch.m_event.setAccepted(true); + + m_pinch.m_lastAngle = m_twoTouchAngle; + emit pinchUpdated(&m_pinch.m_event); + + if (m_acceptedGestures & PinchGesture) { + // Take maximum and minimumzoomlevel into account + qreal perPinchMinimumZoomLevel = qMax(m_pinch.m_zoom.m_start - m_pinch.m_zoom.maximumChange, m_pinch.m_zoom.m_minimum); + qreal perPinchMaximumZoomLevel = qMin(m_pinch.m_zoom.m_start + m_pinch.m_zoom.maximumChange, m_pinch.m_zoom.m_maximum); + newZoomLevel = qMin(qMax(perPinchMinimumZoomLevel, newZoomLevel), perPinchMaximumZoomLevel); + m_declarativeMap->setZoomLevel(qMin(newZoomLevel, maximumZoomLevel()), false); + m_pinch.m_zoom.m_previous = newZoomLevel; + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::endPinch() +{ + QPointF p1 = mapFromScene(m_pinch.m_lastPoint1); + QPointF p2 = mapFromScene(m_pinch.m_lastPoint2); + m_pinch.m_event.setCenter((p1 + p2) / 2); + m_pinch.m_event.setAngle(m_pinch.m_lastAngle); + m_pinch.m_event.setPoint1(p1); + m_pinch.m_event.setPoint2(p2); + m_pinch.m_event.setAccepted(true); + m_pinch.m_event.setPointCount(0); + emit pinchFinished(&m_pinch.m_event); + m_pinch.m_startDist = 0; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::panStateMachine() +{ + FlickState lastState = m_flickState; + + // Transitions + switch (m_flickState) { + case flickInactive: + if (!isTiltActive() && canStartPan()) { + // Update startCoord_ to ensure smooth start for panning when going over startDragDistance + QGeoCoordinate newStartCoord = m_declarativeMap->toCoordinate(m_touchPointsCentroid, false); + m_startCoord.setLongitude(newStartCoord.longitude()); + m_startCoord.setLatitude(newStartCoord.latitude()); + m_declarativeMap->setKeepMouseGrab(true); + setFlickState(panActive); + } + break; + case panActive: + if (m_allPoints.count() == 0) { + if (!tryStartFlick()) + { + setFlickState(flickInactive); + // mark as inactive for use by camera + if (m_pinchState == pinchInactive && m_rotationState == rotationInactive && m_tiltState == tiltInactive) { + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + m_map->prefetchData(); + } + emit panFinished(); + } else { + setFlickState(flickActive); + emit panFinished(); + emit flickStarted(); + } + } + break; + case flickActive: + if (m_allPoints.count() > 0) { // re touched before movement ended + stopFlick(); + m_declarativeMap->setKeepMouseGrab(true); + setFlickState(panActive); + } + break; + } + + if (m_flickState != lastState) + emit panActiveChanged(); + + // Update + switch (m_flickState) { + case flickInactive: // do nothing + break; + case panActive: + updatePan(); + // this ensures 'panStarted' occurs after the pan has actually started + if (lastState != panActive) + emit panStarted(); + break; + case flickActive: + break; + } +} +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::canStartPan() +{ + if (m_allPoints.count() == 0 || (m_acceptedGestures & PanGesture) == 0 + || (m_mousePoint && m_mousePoint->state() == Qt::TouchPointReleased)) // mouseReleaseEvent handling does not clear m_mousePoint, only ungrabMouse does -- QTBUG-66534 + return false; + + // Check if thresholds for normal panning are met. + // (normal panning vs flicking: flicking will start from mouse release event). + const int startDragDistance = qApp->styleHints()->startDragDistance() * 2; + QPointF p1 = mapFromScene(m_allPoints.at(0).scenePos()); + int dyFromPress = int(p1.y() - m_sceneStartPoint1.y()); + int dxFromPress = int(p1.x() - m_sceneStartPoint1.x()); + if ((qAbs(dyFromPress) >= startDragDistance || qAbs(dxFromPress) >= startDragDistance)) + return true; + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::updatePan() +{ + m_declarativeMap->alignCoordinateToPoint(m_startCoord, m_touchPointsCentroid); +} + +/*! + \internal +*/ +bool QQuickGeoMapGestureArea::tryStartFlick() +{ + if ((m_acceptedGestures & FlickGesture) == 0) + return false; + // if we drag then pause before release we should not cause a flick. + qreal flickSpeed = 0.0; + if (m_lastPosTime.elapsed() < QML_MAP_FLICK_VELOCITY_SAMPLE_PERIOD) + flickSpeed = m_flickVector.length(); + + int flickTime = 0; + int flickPixels = 0; + QVector2D flickVector; + + if (qAbs(flickSpeed) > MinimumFlickVelocity + && distanceBetweenTouchPoints(m_touchPointsCentroid, m_sceneStartPoint1) > FlickThreshold) { + qreal acceleration = m_flick.m_deceleration; + if ((flickSpeed > 0.0f) == (m_flick.m_deceleration > 0.0f)) + acceleration = acceleration * -1.0f; + flickTime = static_cast(-1000 * flickSpeed / acceleration); + flickPixels = (flickTime * flickSpeed) / 2000.0; + flickVector = m_flickVector.normalized() * flickPixels; + } + + if (flickTime > 0) { + startFlick(flickVector.x(), flickVector.y(), flickTime); + return true; + } + return false; +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::startFlick(int dx, int dy, int timeMs) +{ + if (!m_flick.m_animation) + return; + if (timeMs < 0) + return; + + QGeoCoordinate animationStartCoordinate = m_declarativeMap->center(); + + if (m_flick.m_animation->isRunning()) + m_flick.m_animation->stop(); + QGeoCoordinate animationEndCoordinate = m_declarativeMap->center(); + m_flick.m_animation->setDuration(timeMs); + + QPointF delta(dx, dy); + QMatrix4x4 matBearing; + matBearing.rotate(m_map->cameraData().bearing(), 0, 0, 1); + delta = matBearing * delta; + + double zoom = pow(2.0, m_declarativeMap->zoomLevel()); + double longitude = animationStartCoordinate.longitude() - (delta.x() / zoom); + double latitude = animationStartCoordinate.latitude() + (delta.y() / zoom); + + if (delta.x() > 0) + m_flick.m_animation->setDirection(QQuickGeoCoordinateAnimation::East); + else + m_flick.m_animation->setDirection(QQuickGeoCoordinateAnimation::West); + + //keep animation in correct bounds + animationEndCoordinate.setLongitude(QLocationUtils::wrapLong(longitude)); + animationEndCoordinate.setLatitude(QLocationUtils::clipLat(latitude, QLocationUtils::mercatorMaxLatitude())); + + m_flick.m_animation->setFrom(animationStartCoordinate); + m_flick.m_animation->setTo(animationEndCoordinate); + m_flick.m_animation->start(); +} + +void QQuickGeoMapGestureArea::stopPan() +{ + if (m_flickState == flickActive) { + stopFlick(); + } else if (m_flickState == panActive) { + m_flickVector = QVector2D(); + setFlickState(flickInactive); + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + emit panFinished(); + emit panActiveChanged(); + m_map->prefetchData(); + } +} + +/*! + \internal +*/ +void QQuickGeoMapGestureArea::stopFlick() +{ + if (!m_flick.m_animation) + return; + m_flickVector = QVector2D(); + if (m_flick.m_animation->isRunning()) + m_flick.m_animation->stop(); + else + handleFlickAnimationStopped(); +} + +void QQuickGeoMapGestureArea::handleFlickAnimationStopped() +{ + m_declarativeMap->setKeepMouseGrab(m_preventStealing); + if (m_flickState == flickActive) { + setFlickState(flickInactive); + emit flickFinished(); + emit panActiveChanged(); + m_map->prefetchData(); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativemaps/qquickgeomapgesturearea_p.h b/src/location/declarativemaps/qquickgeomapgesturearea_p.h new file mode 100644 index 0000000..0bc774d --- /dev/null +++ b/src/location/declarativemaps/qquickgeomapgesturearea_p.h @@ -0,0 +1,399 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKGEOMAPGESTUREAREA_P_H +#define QQUICKGEOMAPGESTUREAREA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGraphicsSceneMouseEvent; +class QQuickGeoCoordinateAnimation; +class QDeclarativeGeoMap; +class QTouchEvent; +class QWheelEvent; +class QGeoMap; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPinchEvent : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPointF center READ center) + Q_PROPERTY(qreal angle READ angle) + Q_PROPERTY(QPointF point1 READ point1) + Q_PROPERTY(QPointF point2 READ point2) + Q_PROPERTY(int pointCount READ pointCount) + Q_PROPERTY(bool accepted READ accepted WRITE setAccepted) + +public: + QGeoMapPinchEvent(const QPointF ¢er, qreal angle, + const QPointF &point1, const QPointF &point2, + int pointCount = 0, bool accepted = true) + : QObject(), m_center(center), m_angle(angle), + m_point1(point1), m_point2(point2), + m_pointCount(pointCount), m_accepted(accepted) {} + QGeoMapPinchEvent() + : QObject(), + m_angle(0.0), + m_pointCount(0), + m_accepted(true) {} + + QPointF center() const { return m_center; } + void setCenter(const QPointF ¢er) { m_center = center; } + qreal angle() const { return m_angle; } + void setAngle(qreal angle) { m_angle = angle; } + QPointF point1() const { return m_point1; } + void setPoint1(const QPointF &p) { m_point1 = p; } + QPointF point2() const { return m_point2; } + void setPoint2(const QPointF &p) { m_point2 = p; } + int pointCount() const { return m_pointCount; } + void setPointCount(int count) { m_pointCount = count; } + bool accepted() const { return m_accepted; } + void setAccepted(bool a) { m_accepted = a; } + +private: + QPointF m_center; + qreal m_angle; + QPointF m_point1; + QPointF m_point2; + int m_pointCount; + bool m_accepted; +}; + +class Q_LOCATION_PRIVATE_EXPORT QQuickGeoMapGestureArea: public QQuickItem +{ + Q_OBJECT + Q_ENUMS(GeoMapGesture) + Q_FLAGS(AcceptedGestures) + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(bool pinchActive READ isPinchActive NOTIFY pinchActiveChanged) + Q_PROPERTY(bool panActive READ isPanActive NOTIFY panActiveChanged) + Q_PROPERTY(bool rotationActive READ isRotationActive NOTIFY rotationActiveChanged) + Q_PROPERTY(bool tiltActive READ isTiltActive NOTIFY tiltActiveChanged) + Q_PROPERTY(AcceptedGestures acceptedGestures READ acceptedGestures WRITE setAcceptedGestures NOTIFY acceptedGesturesChanged) + Q_PROPERTY(qreal maximumZoomLevelChange READ maximumZoomLevelChange WRITE setMaximumZoomLevelChange NOTIFY maximumZoomLevelChangeChanged) + Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) + Q_PROPERTY(bool preventStealing READ preventStealing WRITE setPreventStealing NOTIFY preventStealingChanged REVISION 1) + +public: + QQuickGeoMapGestureArea(QDeclarativeGeoMap *map); + ~QQuickGeoMapGestureArea(); + + enum GeoMapGesture { + NoGesture = 0x0000, + PinchGesture = 0x0001, + PanGesture = 0x0002, + FlickGesture = 0x0004, + RotationGesture = 0x0008, + TiltGesture = 0x0010 + }; + + Q_DECLARE_FLAGS(AcceptedGestures, GeoMapGesture) + + AcceptedGestures acceptedGestures() const; + void setAcceptedGestures(AcceptedGestures acceptedGestures); + + bool isPinchActive() const; + bool isRotationActive() const; + bool isTiltActive() const; + bool isPanActive() const; + bool isActive() const; + + bool enabled() const; + void setEnabled(bool enabled); + + qreal maximumZoomLevelChange() const; + void setMaximumZoomLevelChange(qreal maxChange); + + qreal flickDeceleration() const; + void setFlickDeceleration(qreal deceleration); + + void handleTouchEvent(QTouchEvent *event); +#if QT_CONFIG(wheelevent) + void handleWheelEvent(QWheelEvent *event); +#endif + void handleMousePressEvent(QMouseEvent *event); + void handleMouseMoveEvent(QMouseEvent *event); + void handleMouseReleaseEvent(QMouseEvent *event); + void handleMouseUngrabEvent(); + void handleTouchUngrabEvent(); + + void setMinimumZoomLevel(qreal min); + qreal minimumZoomLevel() const; + + void setMaximumZoomLevel(qreal max); + qreal maximumZoomLevel() const; + + void setMap(QGeoMap* map); + + bool preventStealing() const; + void setPreventStealing(bool prevent); + +Q_SIGNALS: + void panActiveChanged(); + void pinchActiveChanged(); + void rotationActiveChanged(); + void tiltActiveChanged(); + void enabledChanged(); + void maximumZoomLevelChangeChanged(); + void acceptedGesturesChanged(); + void flickDecelerationChanged(); + void pinchStarted(QGeoMapPinchEvent *pinch); + void pinchUpdated(QGeoMapPinchEvent *pinch); + void pinchFinished(QGeoMapPinchEvent *pinch); + void panStarted(); + void panFinished(); + void flickStarted(); + void flickFinished(); + void rotationStarted(QGeoMapPinchEvent *pinch); + void rotationUpdated(QGeoMapPinchEvent *pinch); + void rotationFinished(QGeoMapPinchEvent *pinch); + void tiltStarted(QGeoMapPinchEvent *pinch); + void tiltUpdated(QGeoMapPinchEvent *pinch); + void tiltFinished(QGeoMapPinchEvent *pinch); + void preventStealingChanged(); +private: + void update(); + + // Create general data relating to the touch points + void touchPointStateMachine(); + void startOneTouchPoint(); + void updateOneTouchPoint(); + void startTwoTouchPoints(); + void updateTwoTouchPoints(); + + // All two fingers vertical parallel panning related code, which encompasses tilting + void tiltStateMachine(); + bool canStartTilt(); + void startTilt(); + void updateTilt(); + void endTilt(); + + // All two fingers rotation related code, which encompasses rotation + void rotationStateMachine(); + bool canStartRotation(); + void startRotation(); + void updateRotation(); + void endRotation(); + + // All pinch related code, which encompasses zoom + void pinchStateMachine(); + bool canStartPinch(); + void startPinch(); + void updatePinch(); + void endPinch(); + + // Pan related code (regardles of number of touch points), + // includes the flick based panning after letting go + void panStateMachine(); + bool canStartPan(); + void updatePan(); + bool tryStartFlick(); + void startFlick(int dx, int dy, int timeMs = 0); + void stopFlick(); + + bool pinchEnabled() const; + void setPinchEnabled(bool enabled); + bool rotationEnabled() const; + void setRotationEnabled(bool enabled); + bool tiltEnabled() const; + void setTiltEnabled(bool enabled); + bool panEnabled() const; + void setPanEnabled(bool enabled); + bool flickEnabled() const; + void setFlickEnabled(bool enabled); + +private Q_SLOTS: + void handleFlickAnimationStopped(); + + +private: + void stopPan(); + void clearTouchData(); + void updateFlickParameters(const QPointF &pos); + +private: + QGeoMap* m_map; + QDeclarativeGeoMap *m_declarativeMap; + bool m_enabled; + + // This should be intended as a "two fingers gesture" struct + struct Pinch + { + Pinch() : m_pinchEnabled(true), m_rotationEnabled(true), m_tiltEnabled(true), + m_startDist(0), m_lastAngle(0.0) {} + + QGeoMapPinchEvent m_event; + bool m_pinchEnabled; + bool m_rotationEnabled; + bool m_tiltEnabled; + struct Zoom + { + Zoom() : m_minimum(0.0), m_maximum(30.0), m_start(0.0), m_previous(0.0), + maximumChange(4.0) {} + qreal m_minimum; + qreal m_maximum; + qreal m_start; + qreal m_previous; + qreal maximumChange; + } m_zoom; + + struct Rotation + { + Rotation() : m_startBearing(0.0), m_previousTouchAngle(0.0), m_totalAngle(0.0) {} + qreal m_startBearing; + qreal m_previousTouchAngle; // needed for detecting crossing +- 180 in a safer way + qreal m_totalAngle; + } m_rotation; + + struct Tilt + { + Tilt() {} + QPointF m_startTouchCentroid; + qreal m_startTilt; + } m_tilt; + + QPointF m_lastPoint1; + QPointF m_lastPoint2; + qreal m_startDist; + qreal m_lastAngle; + } m_pinch; + + AcceptedGestures m_acceptedGestures; + + struct Pan + { + Pan() : m_maxVelocity(2500), m_deceleration(2500), m_animation(0), m_flickEnabled(true), m_panEnabled(true) {} + + qreal m_maxVelocity; + qreal m_deceleration; + QQuickGeoCoordinateAnimation *m_animation; + bool m_flickEnabled; + bool m_panEnabled; + } m_flick; + + + // these are calculated regardless of gesture or number of touch points + QVector2D m_flickVector; + QElapsedTimer m_lastPosTime; + QPointF m_lastPos; + QVector m_allPoints; + QVector m_touchPoints; + QScopedPointer m_mousePoint; + QPointF m_sceneStartPoint1; + + // only set when two points in contact + QPointF m_sceneStartPoint2; + QGeoCoordinate m_startCoord; + QGeoCoordinate m_touchCenterCoord; + qreal m_twoTouchAngle; + qreal m_twoTouchAngleStart; + qreal m_distanceBetweenTouchPoints; + qreal m_distanceBetweenTouchPointsStart; + QPointF m_twoTouchPointsCentroidStart; + QPointF m_touchPointsCentroid; + bool m_preventStealing; + bool m_panEnabled; + +private: + // prototype state machine... + enum TouchPointState + { + touchPoints0, + touchPoints1, + touchPoints2 + } m_touchPointState; + + enum PinchState + { + pinchInactive, + pinchInactiveTwoPoints, + pinchActive + } m_pinchState; + + enum RotationState + { + rotationInactive, + rotationInactiveTwoPoints, + rotationActive + } m_rotationState; + + enum TiltState + { + tiltInactive, + tiltInactiveTwoPoints, + tiltActive + } m_tiltState; + + enum FlickState + { + flickInactive, + panActive, + flickActive + } m_flickState; + + inline void setTouchPointState(const TouchPointState state); + inline void setFlickState(const FlickState state); + inline void setTiltState(const TiltState state); + inline void setRotationState(const RotationState state); + inline void setPinchState(const PinchState state); +}; + +QT_END_NAMESPACE +QML_DECLARE_TYPE(QQuickGeoMapGestureArea) + +#endif // QQUICKGEOMAPGESTUREAREA_P_H diff --git a/src/location/declarativeplaces/declarativeplaces.pri b/src/location/declarativeplaces/declarativeplaces.pri new file mode 100644 index 0000000..82f60c2 --- /dev/null +++ b/src/location/declarativeplaces/declarativeplaces.pri @@ -0,0 +1,51 @@ +INCLUDEPATH += declarativeplaces + +SOURCES += \ +#models + declarativeplaces/qdeclarativeplacecontentmodel.cpp \ + declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp \ + declarativeplaces/qdeclarativesearchsuggestionmodel.cpp \ + declarativeplaces/qdeclarativesearchresultmodel.cpp \ + declarativeplaces/qdeclarativereviewmodel.cpp \ + declarativeplaces/qdeclarativeplaceimagemodel.cpp \ + declarativeplaces/qdeclarativeplaceeditorialmodel.cpp \ +#data + declarativeplaces/qdeclarativecontactdetail.cpp \ + declarativeplaces/qdeclarativecategory.cpp \ + declarativeplaces/qdeclarativeplace.cpp \ + declarativeplaces/qdeclarativeplaceattribute.cpp \ + declarativeplaces/qdeclarativeplaceicon.cpp \ + declarativeplaces/qdeclarativeplaceuser.cpp \ + declarativeplaces/qdeclarativeratings.cpp \ + declarativeplaces/qdeclarativesupplier.cpp \ + declarativeplaces/qdeclarativesearchmodelbase.cpp + +PRIVATE_HEADERS += \ +#models + declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h \ + declarativeplaces/qdeclarativesearchsuggestionmodel_p.h \ + declarativeplaces/qdeclarativesearchresultmodel_p.h \ + declarativeplaces/qdeclarativereviewmodel_p.h \ + declarativeplaces/qdeclarativeplaceimagemodel_p.h \ +#data + declarativeplaces/qdeclarativecontactdetail_p.h \ + declarativeplaces/qdeclarativecategory_p.h \ + declarativeplaces/qdeclarativeplace_p.h \ + declarativeplaces/qdeclarativeplaceattribute_p.h \ + declarativeplaces/qdeclarativeplaceicon_p.h \ + declarativeplaces/qdeclarativeplaceuser_p.h \ + declarativeplaces/qdeclarativeratings_p.h \ + declarativeplaces/qdeclarativesupplier_p.h \ + declarativeplaces/qdeclarativesearchmodelbase_p.h \ + declarativeplaces/qdeclarativeplacecontentmodel_p.h \ + declarativeplaces/qdeclarativeplaceeditorialmodel_p.h + + + + + + + + + + diff --git a/src/location/declarativeplaces/qdeclarativecategory.cpp b/src/location/declarativeplaces/qdeclarativecategory.cpp new file mode 100644 index 0000000..1f87123 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecategory.cpp @@ -0,0 +1,458 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecategory_p.h" +#include "qdeclarativeplaceicon_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "error_messages_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Category + \instantiates QDeclarativeCategory + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + + \since QtLocation 5.5 + + \brief The Category type represents a category that a \l Place can be associated with. + + Categories are used to search for places based on the categories they are associated with. The + list of available categories can be obtained from the \l CategoryModel. The + \l PlaceSearchModel has a \l {PlaceSearchModel::categories}{categories} property that is used + to limit the search results to places with the specified categories. + + If the \l Plugin supports it, categories can be created or removed. To create a new category + construct a new Category object and set its properties, then invoke the \l save() method. + + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Category + \dots 0 + \snippet declarative/places.qml Category save + + To remove a category ensure that the \l plugin and categoryId properties are set and call the + \l remove() method. + + \sa CategoryModel +*/ + +QDeclarativeCategory::QDeclarativeCategory(QObject *parent) +: QObject(parent), m_icon(0), m_plugin(0), m_reply(0), m_complete(false), m_status(Ready) +{ +} + +QDeclarativeCategory::QDeclarativeCategory(const QPlaceCategory &category, + QDeclarativeGeoServiceProvider *plugin, + QObject *parent) +: QObject(parent), m_category(category), m_icon(0), m_plugin(plugin), m_reply(0), + m_complete(false), m_status(Ready) +{ + setCategory(category); +} + +QDeclarativeCategory::~QDeclarativeCategory() {} + +// From QQmlParserStatus +void QDeclarativeCategory::componentComplete() +{ + // delayed instantiation of QObject based properties. + if (!m_icon) { + m_icon = new QDeclarativePlaceIcon(this); + m_icon->setPlugin(m_plugin); + } + + m_complete = true; +} + +/*! + \qmlproperty Plugin Category::plugin + + This property holds the location based service to which the category belongs. +*/ +void QDeclarativeCategory::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + m_plugin = plugin; + if (m_complete) + emit pluginChanged(); + + if (m_icon && m_icon->parent() == this && !m_icon->plugin()) + m_icon->setPlugin(m_plugin); + + if (!m_plugin) + return; + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +QDeclarativeGeoServiceProvider *QDeclarativeCategory::plugin() const +{ + return m_plugin; +} + +/*! + \internal +*/ +void QDeclarativeCategory::pluginReady() +{ + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } +} + + +/*! + \qmlproperty QPlaceCategory Category::category + \keyword Category::category + + For details on how to use this property to interface between C++ and QML see + "\l {Category - QPlaceCategory} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeCategory::setCategory(const QPlaceCategory &category) +{ + QPlaceCategory previous = m_category; + m_category = category; + + if (category.name() != previous.name()) + emit nameChanged(); + + if (category.categoryId() != previous.categoryId()) + emit categoryIdChanged(); + + if (m_icon && m_icon->parent() == this) { + m_icon->setPlugin(m_plugin); + m_icon->setIcon(m_category.icon()); + } else if (!m_icon || m_icon->parent() != this) { + m_icon = new QDeclarativePlaceIcon(m_category.icon(), m_plugin, this); + emit iconChanged(); + } +} + +QPlaceCategory QDeclarativeCategory::category() +{ + m_category.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); + return m_category; +} + +/*! + \qmlproperty string Category::categoryId + + This property holds the identifier of the category. The categoryId is a string which uniquely + identifies this category within the categories \l plugin. +*/ +void QDeclarativeCategory::setCategoryId(const QString &id) +{ + if (m_category.categoryId() != id) { + m_category.setCategoryId(id); + emit categoryIdChanged(); + } +} + +QString QDeclarativeCategory::categoryId() const +{ + return m_category.categoryId(); +} + +/*! + \qmlproperty string Category::name + + This property holds string based name of the category. +*/ +void QDeclarativeCategory::setName(const QString &name) +{ + if (m_category.name() != name) { + m_category.setName(name); + emit nameChanged(); + } +} + +QString QDeclarativeCategory::name() const +{ + return m_category.name(); +} + +/*! + \qmlproperty enumeration Category::visibility + + This property holds the visibility of the category. It can be one of: + + \table + \row + \li Category.UnspecifiedVisibility + \li The visibility of the category is unspecified. If saving a category, the + plugin will automatically set a default visibility to the category saved in the backend. + This default is dependent on the plugin implementation. + \row + \li Category.DeviceVisibility + \li The category is limited to the current device. The category will not be transferred + off of the device. + \row + \li Category.PrivateVisibility + \li The category is private to the current user. The category may be transferred to an + online service but is only ever visible to the current user. + \row + \li Category.PublicVisibility + \li The category is public. + \endtable + + Note that visibility does not affect how \l{Place}s associated with + the category are displayed in the user-interface of an application + on the device. Instead, it defines the sharing semantics of the + category. +*/ +QDeclarativeCategory::Visibility QDeclarativeCategory::visibility() const +{ + return static_cast(m_category.visibility()); +} + +void QDeclarativeCategory::setVisibility(Visibility visibility) +{ + if (static_cast(m_category.visibility()) == visibility) + return; + + m_category.setVisibility(static_cast(visibility)); + emit visibilityChanged(); +} + +/*! + \qmlproperty PlaceIcon Category::icon + + This property holds the image source associated with the category. To display the icon you can use + the \l Image type. +*/ +QDeclarativePlaceIcon *QDeclarativeCategory::icon() const +{ + return m_icon; +} + +void QDeclarativeCategory::setIcon(QDeclarativePlaceIcon *icon) +{ + if (m_icon == icon) + return; + + if (m_icon && m_icon->parent() == this) + delete m_icon; + + m_icon = icon; + emit iconChanged(); +} + +/*! + \qmlmethod string Category::errorString() + + Returns a string description of the error of the last operation. + If the last operation completed successfully then the string is empty. +*/ +QString QDeclarativeCategory::errorString() const +{ + return m_errorString; +} + +void QDeclarativeCategory::setStatus(Status status, const QString &errorString) +{ + Status originalStatus = m_status; + m_status = status; + m_errorString = errorString; + + if (originalStatus != m_status) + emit statusChanged(); +} + +/*! + \qmlproperty enumeration Category::status + + This property holds the status of the category. It can be one of: + + \table + \row + \li Category.Ready + \li No error occurred during the last operation, further operations may be performed on + the category. + \row + \li Category.Saving + \li The category is currently being saved, no other operations may be performed until the + current operation completes. + \row + \li Category.Removing + \li The category is currently being removed, no other operations can be performed until + the current operation completes. + \row + \li Category.Error + \li An error occurred during the last operation, further operations can still be + performed on the category. + \endtable +*/ +QDeclarativeCategory::Status QDeclarativeCategory::status() const +{ + return m_status; +} + +/*! + \qmlmethod void Category::save() + + This method saves the category to the backend service. +*/ +void QDeclarativeCategory::save(const QString &parentId) +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->saveCategory(category(), parentId); + connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); + setStatus(QDeclarativeCategory::Saving); +} + +/*! + \qmlmethod void Category::remove() + + This method permanently removes the category from the backend service. +*/ +void QDeclarativeCategory::remove() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->removeCategory(m_category.categoryId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(replyFinished())); + setStatus(QDeclarativeCategory::Removing); +} + +/*! + \internal +*/ +void QDeclarativeCategory::replyFinished() +{ + if (!m_reply) + return; + + if (m_reply->error() == QPlaceReply::NoError) { + switch (m_reply->type()) { + case (QPlaceReply::IdReply) : { + QPlaceIdReply *idReply = qobject_cast(m_reply); + + switch (idReply->operationType()) { + case QPlaceIdReply::SaveCategory: + setCategoryId(idReply->id()); + break; + case QPlaceIdReply::RemoveCategory: + setCategoryId(QString()); + break; + default: + //Other operation types shouldn't ever be received. + break; + } + break; + } + default: + //other types of replies shouldn't ever be received. + break; + } + + m_errorString.clear(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativeCategory::Ready); + } else { + QString errorString = m_reply->errorString(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativeCategory::Error, errorString); + } +} + +/*! + \internal + Helper function to return the manager, this manager is intended to be used to perform the next + operation. Sets status to Error and an appropriate m_errorString if the manager cannot be + obtained. +*/ +QPlaceManager *QDeclarativeCategory::manager() +{ + if (m_status != QDeclarativeCategory::Ready && m_status != QDeclarativeCategory::Error) + return 0; + + if (m_reply) { + m_reply->abort(); + m_reply->deleteLater(); + m_reply = 0; + } + + if (!m_plugin) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROPERTY_NOT_SET)); + return 0; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_NOT_VALID)); + return 0; + } + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return 0; + } + + return placeManager; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativecategory_p.h b/src/location/declarativeplaces/qdeclarativecategory_p.h new file mode 100644 index 0000000..c32072f --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecategory_p.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECATEGORY_P_H +#define QDECLARATIVECATEGORY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativePlaceIcon; +class QPlaceReply; +class QPlaceManager; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeCategory : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_ENUMS(Status Visibility) + + + Q_PROPERTY(QPlaceCategory category READ category WRITE setCategory) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QString categoryId READ categoryId WRITE setCategoryId NOTIFY categoryIdChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged) + Q_PROPERTY(QDeclarativePlaceIcon *icon READ icon WRITE setIcon NOTIFY iconChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeCategory(QObject *parent = 0); + QDeclarativeCategory(const QPlaceCategory &category, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativeCategory(); + + enum Visibility { + UnspecifiedVisibility = QLocation::UnspecifiedVisibility, + DeviceVisibility = QLocation::DeviceVisibility, + PrivateVisibility = QLocation::PrivateVisibility, + PublicVisibility = QLocation::PublicVisibility + }; + enum Status {Ready, Saving, Removing, Error}; + + //From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + QPlaceCategory category(); + void setCategory(const QPlaceCategory &category); + + QString categoryId() const; + void setCategoryId(const QString &catID); + QString name() const; + void setName(const QString &name); + + Visibility visibility() const; + void setVisibility(Visibility visibility); + + QDeclarativePlaceIcon *icon() const; + void setIcon(QDeclarativePlaceIcon *icon); + + Q_INVOKABLE QString errorString() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + Q_INVOKABLE void save(const QString &parentId = QString()); + Q_INVOKABLE void remove(); + +Q_SIGNALS: + void pluginChanged(); + void categoryIdChanged(); + void nameChanged(); + void visibilityChanged(); + void iconChanged(); + void statusChanged(); + +private Q_SLOTS: + void replyFinished(); + void pluginReady(); + +private: + QPlaceManager *manager(); + + QPlaceCategory m_category; + QDeclarativePlaceIcon *m_icon; + QDeclarativeGeoServiceProvider *m_plugin; + QPlaceReply *m_reply; + bool m_complete; + Status m_status; + QString m_errorString; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeCategory) + +#endif // QDECLARATIVECATEGORY_P_H diff --git a/src/location/declarativeplaces/qdeclarativecontactdetail.cpp b/src/location/declarativeplaces/qdeclarativecontactdetail.cpp new file mode 100644 index 0000000..8ce328b --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecontactdetail.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativecontactdetail_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ContactDetails + \instantiates QDeclarativeContactDetails + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The ContactDetails type holds contact details for a \l Place. + + The ContactDetails type is a map of \l {QtLocation::ContactDetail}{ContactDetail} objects. + To access contact details in the map use the \l keys() method to get the list of keys stored in + the map and then use the \c {[]} operator to access the + \l {QtLocation::ContactDetail}{ContactDetail} items. + + The following keys are defined in the API. \l Plugin implementations are free to define + additional keys. + + \list + \li phone + \li fax + \li email + \li website + \endlist + + ContactDetails instances are only ever used in the context of \l {Place}{Places}. It is not possible + to create a ContactDetails instance directly or re-assign ContactDetails instances to \l {Place}{Places}. + Modification of ContactDetails can only be accomplished via Javascript. + + \section1 Examples + + The following example shows how to access all \l {QtLocation::ContactDetail}{ContactDetails} + and print them to the console: + + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ContactDetails read + + The returned list of contact details is an \l {QObjectList-based model}{object list} and so can be used directly as a data model. For example, the + following demonstrates how to display a list of contact phone numbers in a list view: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ContactDetails phoneList + + The following example demonstrates how to assign a single phone number to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write single + + The following demonstrates how to assign multiple phone numbers to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write multiple +*/ + +/*! + \qmlmethod variant ContactDetails::keys() + + Returns an array of contact detail keys currently stored in the map. +*/ +QDeclarativeContactDetails::QDeclarativeContactDetails(QObject *parent) + : QQmlPropertyMap(parent) +{ +} + +QVariant QDeclarativeContactDetails::updateValue(const QString &, const QVariant &input) +{ + if (input.userType() == QMetaType::QObjectStar) { + QDeclarativeContactDetail *detail = + qobject_cast(input.value()); + if (detail) { + QVariantList varList; + varList.append(input); + return varList; + } + } + + return input; +} + +/*! + \qmltype ContactDetail + \instantiates QDeclarativeContactDetail + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The ContactDetail type holds a contact detail such as a phone number or a website + address. + + The ContactDetail provides a single detail on how one could contact a \l Place. The + ContactDetail consists of a \l label, which is a localized string describing the contact + method, and a \l value representing the actual contact detail. + + \section1 Examples + + The following example demonstrates how to assign a single phone number to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write single + + The following demonstrates how to assign multiple phone numbers to a place in JavaScript: + \snippet declarative/places.qml ContactDetails write multiple + + Note, due to limitations of the QQmlPropertyMap, it is not possible + to declaratively specify the contact details in QML, it can only be accomplished + via JavaScript. +*/ +QDeclarativeContactDetail::QDeclarativeContactDetail(QObject *parent) + : QObject(parent) +{ +} + +QDeclarativeContactDetail::QDeclarativeContactDetail(const QPlaceContactDetail &src, QObject *parent) + : QObject(parent), m_contactDetail(src) +{ +} + +QDeclarativeContactDetail::~QDeclarativeContactDetail() +{ +} + +/*! + \qmlproperty QPlaceContactDetail QtLocation::ContactDetail::contactDetail + + For details on how to use this property to interface between C++ and QML see + "\l {ContactDetail - QDeclarativeContactDetail} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeContactDetail::setContactDetail(const QPlaceContactDetail &src) +{ + QPlaceContactDetail prevContactDetail = m_contactDetail; + m_contactDetail = src; + + if (m_contactDetail.label() != prevContactDetail.label()) + emit labelChanged(); + if (m_contactDetail.value() != prevContactDetail.value()) + emit valueChanged(); +} + +QPlaceContactDetail QDeclarativeContactDetail::contactDetail() const +{ + return m_contactDetail; +} + +/*! + \qmlproperty string QtLocation::ContactDetail::label + + This property holds a label describing the contact detail. + + The label can potentially be localized. The language is dependent on the entity that sets it, + typically this is the \l {Plugin}. The \l {Plugin::locales} property defines + what language is used. +*/ +QString QDeclarativeContactDetail::label() const +{ + return m_contactDetail.label(); +} + +void QDeclarativeContactDetail::setLabel(const QString &label) +{ + if (m_contactDetail.label() != label) { + m_contactDetail.setLabel(label); + emit labelChanged(); + } +} + +/*! + \qmlproperty string QtLocation::ContactDetail::value + + This property holds the value of the contact detail which may be a phone number, an email + address, a website url and so on. +*/ +QString QDeclarativeContactDetail::value() const +{ + return m_contactDetail.value(); +} + +void QDeclarativeContactDetail::setValue(const QString &value) +{ + if (m_contactDetail.value() != value) { + m_contactDetail.setValue(value); + emit valueChanged(); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativecontactdetail_p.h b/src/location/declarativeplaces/qdeclarativecontactdetail_p.h new file mode 100644 index 0000000..ad60c3b --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativecontactdetail_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVECONTACTDETAIL_P_H +#define QDECLARATIVECONTACTDETAIL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeContactDetails : public QQmlPropertyMap +{ + Q_OBJECT + +public: + explicit QDeclarativeContactDetails(QObject *parent = 0); + virtual QVariant updateValue(const QString &key, const QVariant &input); +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeContactDetail : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceContactDetail contactDetail READ contactDetail WRITE setContactDetail) + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged) + +public: + explicit QDeclarativeContactDetail(QObject *parent = 0); + explicit QDeclarativeContactDetail(const QPlaceContactDetail &src, QObject *parent = 0); + ~QDeclarativeContactDetail(); + + QPlaceContactDetail contactDetail() const; + void setContactDetail(const QPlaceContactDetail &contactDetail); + + QString label() const; + void setLabel(const QString &label); + + QString value() const; + void setValue(const QString &value); + +Q_SIGNALS: + void labelChanged(); + void valueChanged(); + +private: + QPlaceContactDetail m_contactDetail; + +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeContactDetail) +QML_DECLARE_TYPE(QDeclarativeContactDetails) + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeperiod_p.h b/src/location/declarativeplaces/qdeclarativeperiod_p.h new file mode 100644 index 0000000..3ded010 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeperiod_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPERIOD_P_H +#define QDECLARATIVEPERIOD_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativePeriod : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QDate startDate READ startDate WRITE setStartDate NOTIFY startDateChanged) + Q_PROPERTY(QTime startTime READ startTime WRITE setStartTime NOTIFY startTimeChanged) + Q_PROPERTY(QDate endDate READ endDate WRITE setEndDate NOTIFY endDateChanged) + Q_PROPERTY(QTime endTime READ endTime WRITE setEndTime NOTIFY endTimeChanged) + +public: + explicit QDeclarativePeriod(QObject *parent = 0); + explicit QDeclarativePeriod(const QPlacePeriod &period, QObject *parent = 0); + ~QDeclarativePeriod(); + + QPlacePeriod period() const; + void setPeriod(const QPlacePeriod &period); + + QDate startDate() const; + void setStartDate(const QDate &data); + QTime startTime() const; + void setStartTime(const QTime &data); + QDate endDate() const; + void setEndDate(const QDate &data); + QTime endTime() const; + void setEndTime(const QTime &data); + +Q_SIGNALS: + void startDateChanged(); + void startTimeChanged(); + void endDateChanged(); + void endTimeChanged(); + +private: + QPlacePeriod m_period; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QT_PREPEND_NAMESPACE(QDeclarativePeriod)); + +#endif // QDECLARATIVEPERIOD_P_H diff --git a/src/location/declarativeplaces/qdeclarativeplace.cpp b/src/location/declarativeplaces/qdeclarativeplace.cpp new file mode 100644 index 0000000..7ab40fe --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplace.cpp @@ -0,0 +1,1229 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplace_p.h" +#include "qdeclarativecontactdetail_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "qdeclarativeplaceattribute_p.h" +#include "qdeclarativeplaceicon_p.h" +#include "error_messages_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Place + \instantiates QDeclarativePlace + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The Place type represents a location that is a position of interest. + + The Place type represents a physical location with additional metadata describing that + location. Contrasted with \l Location, \l Address, and + \l {coordinate} type which are used to describe where a location is. + The basic properties of a Place are its \l name and \l location. + + Place objects are typically obtained from a search model and will generally only have their + basic properties set. The \l detailsFetched property can be used to test if further property + values need to be fetched from the \l Plugin. This can be done by invoking the \l getDetails() + method. Progress of the fetching operation can be monitored with the \l status property, which + will be set to Place.Fetching when the details are being fetched. + + The Place type has many properties holding information about the location. Details on how to + contact the place are available from the \l contactDetails property. Convenience properties + for obtaining the primary \l {primaryPhone}{phone}, \l {primaryFax}{fax}, + \l {primaryEmail}{email} and \l {primaryWebsite}{website} are also available. + + Each place is assigned zero or more \l categories. Categories are typically used when + searching for a particular kind of place, such as a restaurant or hotel. Some places have a + \l ratings object, which gives an indication of the quality of the place. + + Place metadata is provided by a \l supplier who may require that an \l attribution message be + displayed to the user when the place details are viewed. + + Places have an associated \l icon which can be used to represent a place on a map or to + decorate a delegate in a view. + + Places may have additional rich content associated with them. The currently supported rich + content include editorial descriptions, reviews and images. These are exposed as a set of + models for retrieving the content. Editorial descriptions of the place are available from the + \l editorialModel property. Reviews of the place are available from the \l reviewModel + property. A gallery of pictures of the place can be accessed using the \l imageModel property. + + Places may have additional attributes which are not covered in the formal API. The + \l extendedAttributes property provides access to these. The type of extended attributes + available is specific to each \l Plugin. + + A Place is almost always tied to a \l plugin. The \l plugin property must be set before it is + possible to call \l save(), \l remove() or \l getDetails(). The \l reviewModel, \l imageModel + and \l editorialModel are only valid then the \l plugin property is set. + + \section2 Saving a Place + + If the \l Plugin supports it, the Place type can be used to save a place. First create a new + Place and set its properties: + + \snippet declarative/places.qml Place savePlace def + + Then invoke the \l save() method: + + \snippet declarative/places.qml Place savePlace + + The \l status property will change to Place.Saving and then to Place.Ready if the save was + successful or to Place.Error if an error occurs. + + If the \l placeId property is set, the backend will update an existing place otherwise it will + create a new place. On success the \l placeId property will be updated with the identifier of the newly + saved place. + + \section3 Caveats + \input place-caveats.qdocinc + + \section3 Saving Between Plugins + When saving places between plugins, there are a few things to be aware of. + Some fields of a place such as the id, categories and icons are plugin specific entities. For example + the categories in one manager may not be recognised in another. + Therefore trying to save a place directly from one plugin to another is not possible. + + It is generally recommended that saving across plugins be handled as saving \l {Favorites}{favorites} + as explained in the Favorites section. However there is another approach which is to create a new place, + set its (destination) plugin and then use the \l copyFrom() method to copy the details of the original place. + Using \l copyFrom() only copies data that is supported by the destination plugin, + plugin specific data such as the place identifier is not copied over. Once the copy is done, + the place is in a suitable state to be saved. + + The following snippet provides an example of saving a place to a different plugin + using the \l copyFrom method: + + \snippet declarative/places.qml Place save to different plugin + + \section2 Removing a Place + + To remove a place, ensure that a Place object with a valid \l placeId property exists and call + its \l remove() method. The \l status property will change to Place.Removing and then to + Place.Ready if the save was successful or to Place.Error if an error occurs. + + \section2 Favorites + The Places API supports the concept of favorites. Favorites are generally implemented + by using two plugins, the first plugin is typically a read-only source of places (origin plugin) and a second + read/write plugin (destination plugin) is used to store places from the origin as favorites. + + Each Place has a favorite property which is intended to contain the corresponding place + from the destination plugin (the place itself is sourced from the origin plugin). Because both the original + place and favorite instances are available, the developer can choose which + properties to show to the user. For example the favorite may have a modified name which should + be displayed rather than the original name. + + \snippet declarative/places.qml Place favorite + + The following demonstrates how to save a new favorite instance. A call is made + to create/initialize the favorite instance and then the instance is saved. + + \snippet declarative/places.qml Place saveFavorite + + The following demonstrates favorite removal: + + \snippet declarative/places.qml Place removeFavorite 1 + \dots + \snippet declarative/places.qml Place removeFavorite 2 + + The PlaceSearchModel has a favoritesPlugin property. If the property is set, any places found + during a search are checked against the favoritesPlugin to see if there is a corresponding + favorite place. If so, the favorite property of the Place is set, otherwise the favorite + property is remains null. + + \sa PlaceSearchModel +*/ + +QDeclarativePlace::QDeclarativePlace(QObject *parent) +: QObject(parent), m_location(0), m_ratings(0), m_supplier(0), m_icon(0), + m_reviewModel(0), m_imageModel(0), m_editorialModel(0), + m_extendedAttributes(new QQmlPropertyMap(this)), + m_contactDetails(new QDeclarativeContactDetails(this)), m_reply(0), m_plugin(0), + m_complete(false), m_favorite(0), m_status(QDeclarativePlace::Ready) +{ + connect(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)), + this, SLOT(contactsModified(QString,QVariant))); + + setPlace(QPlace()); +} + +QDeclarativePlace::QDeclarativePlace(const QPlace &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent) +: QObject(parent), m_location(0), m_ratings(0), m_supplier(0), m_icon(0), + m_reviewModel(0), m_imageModel(0), m_editorialModel(0), + m_extendedAttributes(new QQmlPropertyMap(this)), + m_contactDetails(new QDeclarativeContactDetails(this)), m_reply(0), m_plugin(plugin), + m_complete(false), m_favorite(0), m_status(QDeclarativePlace::Ready) +{ + Q_ASSERT(plugin); + + connect(m_contactDetails, SIGNAL(valueChanged(QString,QVariant)), + this, SLOT(contactsModified(QString,QVariant))); + + setPlace(src); +} + +QDeclarativePlace::~QDeclarativePlace() +{ +} + +// From QQmlParserStatus +void QDeclarativePlace::componentComplete() +{ + m_complete = true; +} + +/*! + \qmlproperty Plugin Place::plugin + + This property holds the \l Plugin that provided this place which can be used to retrieve more information about the service. +*/ +void QDeclarativePlace::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + m_plugin = plugin; + if (m_complete) + emit pluginChanged(); + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +void QDeclarativePlace::pluginReady() +{ + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } +} + +QDeclarativeGeoServiceProvider *QDeclarativePlace::plugin() const +{ + return m_plugin; +} + +/*! + \qmlproperty ReviewModel Place::reviewModel + + This property holds a model which can be used to retrieve reviews about the place. +*/ +QDeclarativeReviewModel *QDeclarativePlace::reviewModel() +{ + if (!m_reviewModel) { + m_reviewModel = new QDeclarativeReviewModel(this); + m_reviewModel->setPlace(this); + } + + return m_reviewModel; +} + +/*! + \qmlproperty ImageModel Place::imageModel + + This property holds a model which can be used to retrieve images of the place. +*/ +QDeclarativePlaceImageModel *QDeclarativePlace::imageModel() +{ + if (!m_imageModel) { + m_imageModel = new QDeclarativePlaceImageModel(this); + m_imageModel->setPlace(this); + } + + return m_imageModel; +} + +/*! + \qmlproperty EditorialModel Place::editorialModel + + This property holds a model which can be used to retrieve editorial descriptions of the place. +*/ +QDeclarativePlaceEditorialModel *QDeclarativePlace::editorialModel() +{ + if (!m_editorialModel) { + m_editorialModel = new QDeclarativePlaceEditorialModel(this); + m_editorialModel->setPlace(this); + } + + return m_editorialModel; +} + +/*! + \qmlproperty QPlace Place::place + + For details on how to use this property to interface between C++ and QML see + "\l {Place - QPlace} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativePlace::setPlace(const QPlace &src) +{ + QPlace previous = m_src; + m_src = src; + + if (previous.categories() != m_src.categories()) { + synchronizeCategories(); + emit categoriesChanged(); + } + + if (m_location && m_location->parent() == this) { + m_location->setLocation(m_src.location()); + } else if (!m_location || m_location->parent() != this) { + m_location = new QDeclarativeGeoLocation(m_src.location(), this); + emit locationChanged(); + } + + if (m_ratings && m_ratings->parent() == this) { + m_ratings->setRatings(m_src.ratings()); + } else if (!m_ratings || m_ratings->parent() != this) { + m_ratings = new QDeclarativeRatings(m_src.ratings(), this); + emit ratingsChanged(); + } + + if (m_supplier && m_supplier->parent() == this) { + m_supplier->setSupplier(m_src.supplier(), m_plugin); + } else if (!m_supplier || m_supplier->parent() != this) { + m_supplier = new QDeclarativeSupplier(m_src.supplier(), m_plugin, this); + emit supplierChanged(); + } + + if (m_icon && m_icon->parent() == this) { + m_icon->setPlugin(m_plugin); + m_icon->setIcon(m_src.icon()); + } else if (!m_icon || m_icon->parent() != this) { + m_icon = new QDeclarativePlaceIcon(m_src.icon(), m_plugin, this); + emit iconChanged(); + } + + if (previous.name() != m_src.name()) { + emit nameChanged(); + } + if (previous.placeId() != m_src.placeId()) { + emit placeIdChanged(); + } + if (previous.attribution() != m_src.attribution()) { + emit attributionChanged(); + } + if (previous.detailsFetched() != m_src.detailsFetched()) { + emit detailsFetchedChanged(); + } + if (previous.primaryPhone() != m_src.primaryPhone()) { + emit primaryPhoneChanged(); + } + if (previous.primaryFax() != m_src.primaryFax()) { + emit primaryFaxChanged(); + } + if (previous.primaryEmail() != m_src.primaryEmail()) { + emit primaryEmailChanged(); + } + if (previous.primaryWebsite() != m_src.primaryWebsite()) { + emit primaryWebsiteChanged(); + } + + if (m_reviewModel && m_src.totalContentCount(QPlaceContent::ReviewType) >= 0) { + m_reviewModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ReviewType), + m_src.content(QPlaceContent::ReviewType)); + } + if (m_imageModel && m_src.totalContentCount(QPlaceContent::ImageType) >= 0) { + m_imageModel->initializeCollection(m_src.totalContentCount(QPlaceContent::ImageType), + m_src.content(QPlaceContent::ImageType)); + } + if (m_editorialModel && m_src.totalContentCount(QPlaceContent::EditorialType) >= 0) { + m_editorialModel->initializeCollection(m_src.totalContentCount(QPlaceContent::EditorialType), + m_src.content(QPlaceContent::EditorialType)); + } + + pullExtendedAttributes(); + synchronizeContacts(); +} + +QPlace QDeclarativePlace::place() +{ + // The following properties are not stored in m_src but instead stored in QDeclarative* objects + + QPlace result = m_src; + + // Categories + QList categories; + foreach (QDeclarativeCategory *value, m_categories) + categories.append(value->category()); + + result.setCategories(categories); + + // Location + result.setLocation(m_location ? m_location->location() : QGeoLocation()); + + // Rating + result.setRatings(m_ratings ? m_ratings->ratings() : QPlaceRatings()); + + // Supplier + result.setSupplier(m_supplier ? m_supplier->supplier() : QPlaceSupplier()); + + // Icon + result.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); + + //contact details + QList cppDetails; + foreach (const QString &key, m_contactDetails->keys()) { + cppDetails.clear(); + if (m_contactDetails->value(key).type() == QVariant::List) { + QVariantList detailsVarList = m_contactDetails->value(key).toList(); + foreach (const QVariant &detailVar, detailsVarList) { + QDeclarativeContactDetail *detail = qobject_cast(detailVar.value()); + if (detail) + cppDetails.append(detail->contactDetail()); + } + } else { + QDeclarativeContactDetail *detail = qobject_cast(m_contactDetails->value(key).value()); + if (detail) + cppDetails.append(detail->contactDetail()); + } + result.setContactDetails(key, cppDetails); + } + + return result; +} + +/*! + \qmlproperty QtPositioning::Location Place::location + + This property holds the location of the place which can be used to retrieve the coordinate, + address and the bounding box. +*/ +void QDeclarativePlace::setLocation(QDeclarativeGeoLocation *location) +{ + if (m_location == location) + return; + + if (m_location && m_location->parent() == this) + delete m_location; + + m_location = location; + emit locationChanged(); +} + +QDeclarativeGeoLocation *QDeclarativePlace::location() +{ + return m_location; +} + +/*! + \qmlproperty Ratings Place::ratings + + This property holds ratings of the place. The ratings provide an indication of the quality of a + place. +*/ +void QDeclarativePlace::setRatings(QDeclarativeRatings *rating) +{ + if (m_ratings == rating) + return; + + if (m_ratings && m_ratings->parent() == this) + delete m_ratings; + + m_ratings = rating; + emit ratingsChanged(); +} + +QDeclarativeRatings *QDeclarativePlace::ratings() +{ + + return m_ratings; +} + +/*! + \qmlproperty Supplier Place::supplier + + This property holds the supplier of the place data. + The supplier is typically a business or organization that collected the data about the place. +*/ +void QDeclarativePlace::setSupplier(QDeclarativeSupplier *supplier) +{ + if (m_supplier == supplier) + return; + + if (m_supplier && m_supplier->parent() == this) + delete m_supplier; + + m_supplier = supplier; + emit supplierChanged(); +} + +QDeclarativeSupplier *QDeclarativePlace::supplier() const +{ + return m_supplier; +} + +/*! + \qmlproperty Icon Place::icon + + This property holds a graphical icon which can be used to represent the place. +*/ +QDeclarativePlaceIcon *QDeclarativePlace::icon() const +{ + return m_icon; +} + +void QDeclarativePlace::setIcon(QDeclarativePlaceIcon *icon) +{ + if (m_icon == icon) + return; + + if (m_icon && m_icon->parent() == this) + delete m_icon; + + m_icon = icon; + emit iconChanged(); +} + +/*! + \qmlproperty string Place::name + + This property holds the name of the place which can be used to represent the place. +*/ +void QDeclarativePlace::setName(const QString &name) +{ + if (m_src.name() != name) { + m_src.setName(name); + emit nameChanged(); + } +} + +QString QDeclarativePlace::name() const +{ + return m_src.name(); +} + +/*! + \qmlproperty string Place::placeId + + This property holds the unique identifier of the place. The place identifier is only meaningful to the + \l Plugin that generated it and is not transferable between \l {Plugin}{Plugins}. The place id + is not guaranteed to be universally unique, but unique within the \l Plugin that generated it. + + If only the place identifier is known, all other place data can fetched from the \l Plugin. + + \snippet declarative/places.qml Place placeId +*/ +void QDeclarativePlace::setPlaceId(const QString &placeId) +{ + if (m_src.placeId() != placeId) { + m_src.setPlaceId(placeId); + emit placeIdChanged(); + } +} + +QString QDeclarativePlace::placeId() const +{ + return m_src.placeId(); +} + +/*! + \qmlproperty string Place::attribution + + This property holds a rich text attribution string for the place. + Some providers may require that the attribution be shown to the user + whenever a place is displayed. The contents of this property should + be shown to the user if it is not empty. +*/ +void QDeclarativePlace::setAttribution(const QString &attribution) +{ + if (m_src.attribution() != attribution) { + m_src.setAttribution(attribution); + emit attributionChanged(); + } +} + +QString QDeclarativePlace::attribution() const +{ + return m_src.attribution(); +} + +/*! + \qmlproperty bool Place::detailsFetched + + This property indicates whether the details of the place have been fetched. If this property + is false, the place details have not yet been fetched. Fetching can be done by invoking the + \l getDetails() method. + + \sa getDetails() +*/ +bool QDeclarativePlace::detailsFetched() const +{ + return m_src.detailsFetched(); +} + +/*! + \qmlproperty enumeration Place::status + + This property holds the status of the place. It can be one of: + + \table + \row + \li Place.Ready + \li No error occurred during the last operation, further operations may be performed on + the place. + \row + \li Place.Saving + \li The place is currently being saved, no other operation may be performed until + complete. + \row + \li Place.Fetching + \li The place details are currently being fetched, no other operations may be performed + until complete. + \row + \li Place.Removing + \li The place is currently being removed, no other operations can be performed until + complete. + \row + \li Place.Error + \li An error occurred during the last operation, further operations can still be + performed on the place. + \endtable + + The status of a place can be checked by connecting the status property + to a handler function, and then have the handler function process the change + in status. + + \snippet declarative/places.qml Place checkStatus + \dots + \snippet declarative/places.qml Place checkStatus handler + +*/ +void QDeclarativePlace::setStatus(Status status, const QString &errorString) +{ + Status originalStatus = m_status; + m_status = status; + m_errorString = errorString; + + if (originalStatus != m_status) + emit statusChanged(); +} + +QDeclarativePlace::Status QDeclarativePlace::status() const +{ + return m_status; +} + +/*! + \internal +*/ +void QDeclarativePlace::finished() +{ + if (!m_reply) + return; + + if (m_reply->error() == QPlaceReply::NoError) { + switch (m_reply->type()) { + case (QPlaceReply::IdReply) : { + QPlaceIdReply *idReply = qobject_cast(m_reply); + + switch (idReply->operationType()) { + case QPlaceIdReply::SavePlace: + setPlaceId(idReply->id()); + break; + case QPlaceIdReply::RemovePlace: + break; + default: + //Other operation types shouldn't ever be received. + break; + } + break; + } + case (QPlaceReply::DetailsReply): { + QPlaceDetailsReply *detailsReply = qobject_cast(m_reply); + setPlace(detailsReply->place()); + break; + } + default: + //other types of replies shouldn't ever be received. + break; + } + + m_errorString.clear(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativePlace::Ready); + } else { + QString errorString = m_reply->errorString(); + + m_reply->deleteLater(); + m_reply = 0; + + setStatus(QDeclarativePlace::Error, errorString); + } +} + +/*! + \internal +*/ +void QDeclarativePlace::contactsModified(const QString &key, const QVariant &) +{ + primarySignalsEmission(key); +} + +/*! + \internal +*/ +void QDeclarativePlace::cleanupDeletedCategories() +{ + foreach (QDeclarativeCategory * category, m_categoriesToBeDeleted) { + if (category->parent() == this) + delete category; + } + m_categoriesToBeDeleted.clear(); +} + +/*! + \qmlmethod void Place::getDetails() + + This method starts fetching place details. + + The \l status property will change to Place.Fetching while the fetch is in progress. On + success the object's properties will be updated, \l status will be set to Place.Ready and + \l detailsFetched will be set to true. On error \l status will be set to Place.Error. The + \l errorString() method can be used to get the details of the error. +*/ +void QDeclarativePlace::getDetails() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->getPlaceDetails(placeId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Fetching); +} + +/*! + \qmlmethod void Place::save() + + This method performs a save operation on the place. + + The \l status property will change to Place.Saving while the save operation is in progress. On + success the \l status will be set to Place.Ready. On error \l status will be set to Place.Error. + The \l errorString() method can be used to get the details of the error. + + If the \l placeId property was previously empty, it will be assigned a valid value automatically + during a successful save operation. + + Note that a \l PlaceSearchModel will call Place::getDetails on any place that it detects an update + on. A consequence of this is that whenever a Place from a \l PlaceSearchModel is successfully saved, + it will be followed by a fetch of place details, leading to a sequence of state changes + of \c Saving, \c Ready, \c Fetching, \c Ready. + +*/ +void QDeclarativePlace::save() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->savePlace(place()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Saving); +} + +/*! + \qmlmethod void Place::remove() + + This method performs a remove operation on the place. + + The \l status property will change to Place.Removing while the save operation is in progress. + On success \l status will be set to Place.Ready. On error \l status will be set to + Place.Error. The \l errorString() method can be used to get the details of the error. +*/ +void QDeclarativePlace::remove() +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + m_reply = placeManager->removePlace(place().placeId()); + connect(m_reply, SIGNAL(finished()), this, SLOT(finished())); + setStatus(QDeclarativePlace::Removing); +} + +/*! + \qmlmethod string Place::errorString() + + Returns a string description of the error of the last operation. If the last operation + completed successfully then the string is empty. +*/ +QString QDeclarativePlace::errorString() const +{ + return m_errorString; +} + +/*! + \qmlproperty string Place::primaryPhone + + This property holds the primary phone number of the place. If no "phone" contact detail is + defined for this place, this property will be an empty string. It is equivalent to: + + + \snippet declarative/places.qml Place primaryPhone +*/ +QString QDeclarativePlace::primaryPhone() const +{ + return primaryValue(QPlaceContactDetail::Phone); +} + +/*! + \qmlproperty string Place::primaryFax + + This property holds the primary fax number of the place. If no "fax" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryFax +*/ +QString QDeclarativePlace::primaryFax() const +{ + return primaryValue(QPlaceContactDetail::Fax); +} + +/*! + \qmlproperty string Place::primaryEmail + + This property holds the primary email address of the place. If no "email" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryEmail +*/ +QString QDeclarativePlace::primaryEmail() const +{ + return primaryValue(QPlaceContactDetail::Email); +} + +/*! + \qmlproperty string Place::primaryWebsite + + This property holds the primary website url of the place. If no "website" contact detail is + defined for this place this property will be an empty string. It is equivalent to + + \snippet declarative/places.qml Place primaryWebsite +*/ + +QUrl QDeclarativePlace::primaryWebsite() const +{ + return QUrl(primaryValue(QPlaceContactDetail::Website)); +} + +/*! + \qmlproperty ExtendedAttributes Place::extendedAttributes + + This property holds the extended attributes of a place. Extended attributes are additional + information about a place not covered by the place's properties. +*/ +QQmlPropertyMap *QDeclarativePlace::extendedAttributes() const +{ + return m_extendedAttributes; +} + +/*! + \qmlproperty ContactDetails Place::contactDetails + + This property holds the contact information for this place, for example a phone number or + a website URL. This property is a map of \l ContactDetail objects. +*/ +QDeclarativeContactDetails *QDeclarativePlace::contactDetails() const +{ + return m_contactDetails; +} + +/*! + \qmlproperty list Place::categories + + This property holds the list of categories this place is a member of. The categories that can + be assigned to a place are specific to each \l plugin. +*/ +QQmlListProperty QDeclarativePlace::categories() +{ + return QQmlListProperty(this, + 0, // opaque data parameter + category_append, + category_count, + category_at, + category_clear); +} + +/*! + \internal +*/ +void QDeclarativePlace::category_append(QQmlListProperty *prop, + QDeclarativeCategory *value) +{ + QDeclarativePlace *object = static_cast(prop->object); + + if (object->m_categoriesToBeDeleted.contains(value)) + object->m_categoriesToBeDeleted.removeAll(value); + + if (!object->m_categories.contains(value)) { + object->m_categories.append(value); + QList list = object->m_src.categories(); + list.append(value->category()); + object->m_src.setCategories(list); + + emit object->categoriesChanged(); + } +} + +/*! + \internal +*/ +int QDeclarativePlace::category_count(QQmlListProperty *prop) +{ + return static_cast(prop->object)->m_categories.count(); +} + +/*! + \internal +*/ +QDeclarativeCategory *QDeclarativePlace::category_at(QQmlListProperty *prop, + int index) +{ + QDeclarativePlace *object = static_cast(prop->object); + QDeclarativeCategory *res = NULL; + if (object->m_categories.count() > index && index > -1) { + res = object->m_categories[index]; + } + return res; +} + +/*! + \internal +*/ +void QDeclarativePlace::category_clear(QQmlListProperty *prop) +{ + QDeclarativePlace *object = static_cast(prop->object); + if (object->m_categories.isEmpty()) + return; + + for (int i = 0; i < object->m_categories.count(); ++i) { + if (object->m_categories.at(i)->parent() == object) + object->m_categoriesToBeDeleted.append(object->m_categories.at(i)); + } + + object->m_categories.clear(); + object->m_src.setCategories(QList()); + emit object->categoriesChanged(); + QMetaObject::invokeMethod(object, "cleanupDeletedCategories", Qt::QueuedConnection); +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeCategories() +{ + qDeleteAll(m_categories); + m_categories.clear(); + foreach (const QPlaceCategory &value, m_src.categories()) { + QDeclarativeCategory *declarativeValue = new QDeclarativeCategory(value, m_plugin, this); + m_categories.append(declarativeValue); + } +} + +/*! + \qmlproperty enumeration Place::visibility + + This property holds the visibility of the place. It can be one of: + + \table + \row + \li Place.UnspecifiedVisibility + \li The visibility of the place is unspecified, the default visibility of the \l Plugin + will be used. + \row + \li Place.DeviceVisibility + \li The place is limited to the current device. The place will not be transferred off + of the device. + \row + \li Place.PrivateVisibility + \li The place is private to the current user. The place may be transferred to an online + service but is only ever visible to the current user. + \row + \li Place.PublicVisibility + \li The place is public. + \endtable + + Note that visibility does not affect how the place is displayed + in the user-interface of an application on the device. Instead, + it defines the sharing semantics of the place. +*/ +QDeclarativePlace::Visibility QDeclarativePlace::visibility() const +{ + return static_cast(m_src.visibility()); +} + +void QDeclarativePlace::setVisibility(Visibility visibility) +{ + if (static_cast(m_src.visibility()) == visibility) + return; + + m_src.setVisibility(static_cast(visibility)); + emit visibilityChanged(); +} + +/*! + \qmlproperty Place Place::favorite + + This property holds the favorite instance of a place. +*/ +QDeclarativePlace *QDeclarativePlace::favorite() const +{ + return m_favorite; +} + +void QDeclarativePlace::setFavorite(QDeclarativePlace *favorite) +{ + + if (m_favorite == favorite) + return; + + if (m_favorite && m_favorite->parent() == this) + delete m_favorite; + + m_favorite = favorite; + emit favoriteChanged(); +} + +/*! + \qmlmethod void Place::copyFrom(Place original) + + Copies data from an \a original place into this place. Only data that is supported by this + place's plugin is copied over and plugin specific data such as place identifier is not copied over. +*/ +void QDeclarativePlace::copyFrom(QDeclarativePlace *original) +{ + QPlaceManager *placeManager = manager(); + if (!placeManager) + return; + + setPlace(placeManager->compatiblePlace(original->place())); +} + +/*! + \qmlmethod void Place::initializeFavorite(Plugin destinationPlugin) + + Creates a favorite instance for the place which is to be saved into the + \a destination plugin. This method does nothing if the favorite property is + not null. +*/ +void QDeclarativePlace::initializeFavorite(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_favorite == 0) { + QDeclarativePlace *place = new QDeclarativePlace(this); + place->setPlugin(plugin); + place->copyFrom(this); + setFavorite(place); + } +} + +/*! + \internal +*/ +void QDeclarativePlace::pullExtendedAttributes() +{ + QStringList keys = m_extendedAttributes->keys(); + foreach (const QString &key, keys) + m_extendedAttributes->clear(key); + + QStringList attributeTypes = m_src.extendedAttributeTypes(); + foreach (const QString &attributeType, attributeTypes) { + m_extendedAttributes->insert(attributeType, + qVariantFromValue(new QDeclarativePlaceAttribute(m_src.extendedAttribute(attributeType)))); + } + + emit extendedAttributesChanged(); +} + +/*! + \internal +*/ +void QDeclarativePlace::synchronizeContacts() +{ + //clear out contact data + foreach (const QString &contactType, m_contactDetails->keys()) { + QList contacts = m_contactDetails->value(contactType).toList(); + foreach (const QVariant &var, contacts) { + QObject *obj = var.value(); + if (obj->parent() == this) + delete obj; + } + m_contactDetails->insert(contactType, QVariantList()); + } + + //insert new contact data from source place + foreach (const QString &contactType, m_src.contactTypes()) { + QList sourceContacts = m_src.contactDetails(contactType); + QVariantList declContacts; + foreach (const QPlaceContactDetail &sourceContact, sourceContacts) { + QDeclarativeContactDetail *declContact = new QDeclarativeContactDetail(this); + declContact->setContactDetail(sourceContact); + declContacts.append(QVariant::fromValue(qobject_cast(declContact))); + } + m_contactDetails->insert(contactType, declContacts); + } + primarySignalsEmission(); +} + +/*! + \internal + Helper function to emit the signals for the primary___() + fields. It is expected that the values of the primary___() + functions have already been modified to new values. +*/ +void QDeclarativePlace::primarySignalsEmission(const QString &type) +{ + if (type.isEmpty() || type == QPlaceContactDetail::Phone) { + if (m_prevPrimaryPhone != primaryPhone()) { + m_prevPrimaryPhone = primaryPhone(); + emit primaryPhoneChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Email) { + if (m_prevPrimaryEmail != primaryEmail()) { + m_prevPrimaryEmail = primaryEmail(); + emit primaryEmailChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Website) { + if (m_prevPrimaryWebsite != primaryWebsite()) { + m_prevPrimaryWebsite = primaryWebsite(); + emit primaryWebsiteChanged(); + } + if (!type.isEmpty()) + return; + } + + if (type.isEmpty() || type == QPlaceContactDetail::Fax) { + if (m_prevPrimaryFax != primaryFax()) { + m_prevPrimaryFax = primaryFax(); + emit primaryFaxChanged(); + } + } +} + +/*! + \internal + Helper function to return the manager, this manager is intended to be used + to perform the next operation. If a an operation is currently underway + then return a null pointer. +*/ +QPlaceManager *QDeclarativePlace::manager() +{ + if (m_status != QDeclarativePlace::Ready && m_status != QDeclarativePlace::Error) + return 0; + + if (m_reply) { + m_reply->abort(); + m_reply->deleteLater(); + m_reply = 0; + } + + if (!m_plugin) { + qmlWarning(this) << QStringLiteral("Plugin is not assigned to place."); + return 0; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) + return 0; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + + if (!placeManager) { + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return 0; + } + + return placeManager; +} + +/*! + \internal +*/ +QString QDeclarativePlace::primaryValue(const QString &contactType) const +{ + QVariant value = m_contactDetails->value(contactType); + if (value.userType() == qMetaTypeId()) + value = value.value().toVariant(); + + if (value.userType() == QVariant::List) { + QVariantList detailList = m_contactDetails->value(contactType).toList(); + if (!detailList.isEmpty()) { + QDeclarativeContactDetail *primaryDetail = qobject_cast(detailList.at(0).value()); + if (primaryDetail) + return primaryDetail->value(); + } + } else if (value.userType() == QMetaType::QObjectStar) { + QDeclarativeContactDetail *primaryDetail = qobject_cast(m_contactDetails->value(contactType).value()); + if (primaryDetail) + return primaryDetail->value(); + } + + return QString(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplace_p.h b/src/location/declarativeplaces/qdeclarativeplace_p.h new file mode 100644 index 0000000..67d2c8d --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplace_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACE_P_H +#define QDECLARATIVEPLACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceReply; + +class QPlaceManager; +class QDeclarativePlaceIcon; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlace : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_ENUMS(Status Visibility) + + Q_PROPERTY(QPlace place READ place WRITE setPlace) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QQmlListProperty categories READ categories NOTIFY categoriesChanged) + Q_PROPERTY(QDeclarativeGeoLocation *location READ location WRITE setLocation NOTIFY locationChanged) + Q_PROPERTY(QDeclarativeRatings *ratings READ ratings WRITE setRatings NOTIFY ratingsChanged) + Q_PROPERTY(QDeclarativeSupplier *supplier READ supplier WRITE setSupplier NOTIFY supplierChanged) + Q_PROPERTY(QDeclarativePlaceIcon *icon READ icon WRITE setIcon NOTIFY iconChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString placeId READ placeId WRITE setPlaceId NOTIFY placeIdChanged) + Q_PROPERTY(QString attribution READ attribution WRITE setAttribution NOTIFY attributionChanged) + + Q_PROPERTY(QDeclarativeReviewModel *reviewModel READ reviewModel NOTIFY reviewModelChanged) + Q_PROPERTY(QDeclarativePlaceImageModel *imageModel READ imageModel NOTIFY imageModelChanged) + Q_PROPERTY(QDeclarativePlaceEditorialModel *editorialModel READ editorialModel NOTIFY editorialModelChanged) + + Q_PROPERTY(QObject *extendedAttributes READ extendedAttributes NOTIFY extendedAttributesChanged) + Q_PROPERTY(QObject *contactDetails READ contactDetails NOTIFY contactDetailsChanged) + Q_PROPERTY(bool detailsFetched READ detailsFetched NOTIFY detailsFetchedChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_PROPERTY(QString primaryPhone READ primaryPhone NOTIFY primaryPhoneChanged) + Q_PROPERTY(QString primaryFax READ primaryFax NOTIFY primaryFaxChanged) + Q_PROPERTY(QString primaryEmail READ primaryEmail NOTIFY primaryEmailChanged) + Q_PROPERTY(QUrl primaryWebsite READ primaryWebsite NOTIFY primaryWebsiteChanged) + + Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged) + Q_PROPERTY(QDeclarativePlace *favorite READ favorite WRITE setFavorite NOTIFY favoriteChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativePlace(QObject *parent = 0); + QDeclarativePlace(const QPlace &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativePlace(); + + enum Status {Ready, Saving, Fetching, Removing, Error}; + enum Visibility { + UnspecifiedVisibility = QLocation::UnspecifiedVisibility, + DeviceVisibility = QLocation::DeviceVisibility, + PrivateVisibility = QLocation::PrivateVisibility, + PublicVisibility = QLocation::PublicVisibility + }; + + //From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + QDeclarativeReviewModel *reviewModel(); + QDeclarativePlaceImageModel *imageModel(); + QDeclarativePlaceEditorialModel *editorialModel(); + + QPlace place(); + void setPlace(const QPlace &src); + + QQmlListProperty categories(); + static void category_append(QQmlListProperty *prop, + QDeclarativeCategory *value); + static int category_count(QQmlListProperty *prop); + static QDeclarativeCategory *category_at(QQmlListProperty *prop, int index); + static void category_clear(QQmlListProperty *prop); + + QDeclarativeGeoLocation *location(); + void setLocation(QDeclarativeGeoLocation *location); + QDeclarativeRatings *ratings(); + void setRatings(QDeclarativeRatings *ratings); + QDeclarativeSupplier *supplier() const; + void setSupplier(QDeclarativeSupplier *supplier); + QDeclarativePlaceIcon *icon() const; + void setIcon(QDeclarativePlaceIcon *icon); + QString name() const; + void setName(const QString &name); + QString placeId() const; + void setPlaceId(const QString &placeId); + QString attribution() const; + void setAttribution(const QString &attribution); + bool detailsFetched() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + Q_INVOKABLE void getDetails(); + Q_INVOKABLE void save(); + Q_INVOKABLE void remove(); + Q_INVOKABLE QString errorString() const; + + QString primaryPhone() const; + QString primaryFax() const; + QString primaryEmail() const; + QUrl primaryWebsite() const; + + QQmlPropertyMap *extendedAttributes() const; + + QDeclarativeContactDetails *contactDetails() const; + + Visibility visibility() const; + void setVisibility(Visibility visibility); + + QDeclarativePlace *favorite() const; + void setFavorite(QDeclarativePlace *favorite); + + Q_INVOKABLE void copyFrom(QDeclarativePlace *original); + Q_INVOKABLE void initializeFavorite(QDeclarativeGeoServiceProvider *plugin); + +Q_SIGNALS: + void pluginChanged(); + void categoriesChanged(); + void locationChanged(); + void ratingsChanged(); + void supplierChanged(); + void iconChanged(); + void nameChanged(); + void placeIdChanged(); + void attributionChanged(); + void detailsFetchedChanged(); + void reviewModelChanged(); + void imageModelChanged(); + void editorialModelChanged(); + + void primaryPhoneChanged(); + void primaryFaxChanged(); + void primaryEmailChanged(); + void primaryWebsiteChanged(); + + void extendedAttributesChanged(); + void contactDetailsChanged(); + void statusChanged(); + void visibilityChanged(); + void favoriteChanged(); + +private Q_SLOTS: + void finished(); + void contactsModified(const QString &, const QVariant &); + void pluginReady(); + void cleanupDeletedCategories(); +private: + void synchronizeCategories(); + void pullExtendedAttributes(); + void synchronizeContacts(); + void primarySignalsEmission(const QString &type = QString()); + QString primaryValue(const QString &contactType) const; + +private: + QPlaceManager *manager(); + + QList m_categories; + QDeclarativeGeoLocation *m_location; + QDeclarativeRatings *m_ratings; + QDeclarativeSupplier *m_supplier; + QDeclarativePlaceIcon *m_icon; + QDeclarativeReviewModel *m_reviewModel; + QDeclarativePlaceImageModel *m_imageModel; + QDeclarativePlaceEditorialModel *m_editorialModel; + QQmlPropertyMap *m_extendedAttributes; + QDeclarativeContactDetails *m_contactDetails; + + QPlace m_src; + + QPlaceReply *m_reply; + + QDeclarativeGeoServiceProvider *m_plugin; + bool m_complete; + + QString m_prevPrimaryPhone; + QString m_prevPrimaryEmail; + QString m_prevPrimaryFax; + QUrl m_prevPrimaryWebsite; + + QDeclarativePlace *m_favorite; + + Status m_status; + QString m_errorString; + + QListm_categoriesToBeDeleted; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePlace) + +#endif // QDECLARATIVEPLACE_P_H diff --git a/src/location/declarativeplaces/qdeclarativeplaceattribute.cpp b/src/location/declarativeplaces/qdeclarativeplaceattribute.cpp new file mode 100644 index 0000000..1466703 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceattribute.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplaceattribute_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ExtendedAttributes + \instantiates QQmlPropertyMap + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The ExtendedAttributes type holds additional data about a \l Place. + + The ExtendedAttributes type is a map of \l {PlaceAttribute}{PlaceAttributes}. To access + attributes in the map use the \l keys() method to get the list of keys stored in the map and + use the \c {[]} operator to access the \l PlaceAttribute items. + + The following are standard keys that are defined by the API. \l Plugin + implementations are free to define additional keys. Custom keys should + be qualified by a unique prefix to avoid clashes. + \table + \header + \li key + \li description + \row + \li openingHours + \li The trading hours of the place + \row + \li payment + \li The types of payment the place accepts, for example visa, mastercard. + \row + \li x_provider + \li The name of the provider that a place is sourced from + \row + \li x_id_ (for example x_id_here) + \li An alternative identifier which identifies the place from the + perspective of the specified provider. + \endtable + + Some plugins may not support attributes at all, others may only support a + certain set, others still may support a dynamically changing set of attributes + over time or even allow attributes to be arbitrarily defined by the client + application. The attributes could also vary on a place by place basis, + for example one place may have opening hours while another does not. + Consult the \l {Plugin References and Parameters}{plugin + references} for details. + + Some attributes may not be intended to be readable by end users, the label field + of such attributes is empty to indicate this fact. + + \note ExtendedAttributes instances are only ever used in the context of \l {Place}s. It is not + possible to create an ExtendedAttributes instance directly or re-assign a \l {Place}'s + ExtendedAttributes property. Modification of ExtendedAttributes can only be accomplished + via Javascript. + + The following example shows how to access all \l {PlaceAttribute}{PlaceAttributes} and print + them to the console: + + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ExtendedAttributes read + + The following example shows how to assign and modify an attribute: + \snippet declarative/places.qml ExtendedAttributes write + + \sa PlaceAttribute, QQmlPropertyMap +*/ + +/*! + \qmlmethod variant ExtendedAttributes::keys() + + Returns an array of place attribute keys currently stored in the map. +*/ + +/*! + \qmlsignal void ExtendedAttributes::valueChanged(string key, variant value) + + This signal is emitted when the set of attributes changes. \a key is the key + corresponding to the \a value that was changed. + + The corresponding handler is \c onValueChanged. +*/ + +/*! + \qmltype PlaceAttribute + \instantiates QDeclarativePlaceAttribute + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The PlaceAttribute type holds generic place attribute information. + + A place attribute stores an additional piece of information about a \l Place that is not + otherwise exposed through the \l Place type. A PlaceAttribute is a textual piece of data, + accessible through the \l text property, and a \l label. Both the \l text and \l label + properties are intended to be displayed to the user. PlaceAttributes are stored in an + \l ExtendedAttributes map with a unique key. + + The following example shows how to display all attributes in a list: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ExtendedAttributes + + The following example shows how to assign and modify an attribute: + \snippet declarative/places.qml ExtendedAttributes write +*/ + +QDeclarativePlaceAttribute::QDeclarativePlaceAttribute(QObject *parent) + : QObject(parent) +{ +} + +QDeclarativePlaceAttribute::QDeclarativePlaceAttribute(const QPlaceAttribute &src, QObject *parent) + : QObject(parent),m_attribute(src) +{ +} + +QDeclarativePlaceAttribute::~QDeclarativePlaceAttribute() +{ +} + +/*! + \qmlproperty QPlaceAttribute PlaceAttribute::attribute + + For details on how to use this property to interface between C++ and QML see + "\l {PlaceAttribute - QPlaceAttribute} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativePlaceAttribute::setAttribute(const QPlaceAttribute &src) +{ + QPlaceAttribute prevAttribute = m_attribute; + m_attribute = src; + + if (m_attribute.label() != prevAttribute.label()) + emit labelChanged(); + if (m_attribute.text() != prevAttribute.text()) + emit textChanged(); +} + +QPlaceAttribute QDeclarativePlaceAttribute::attribute() const +{ + return m_attribute; +} + +/*! + \qmlproperty string PlaceAttribute::label + + This property holds the attribute label which is a user visible string + describing the attribute. +*/ +void QDeclarativePlaceAttribute::setLabel(const QString &label) +{ + if (m_attribute.label() != label) { + m_attribute.setLabel(label); + emit labelChanged(); + } +} + +QString QDeclarativePlaceAttribute::label() const +{ + return m_attribute.label(); +} + +/*! + \qmlproperty string PlaceAttribute::text + + This property holds the attribute text which can be used to show additional information about the place. +*/ +void QDeclarativePlaceAttribute::setText(const QString &text) +{ + if (m_attribute.text() != text) { + m_attribute.setText(text); + emit textChanged(); + } +} + +QString QDeclarativePlaceAttribute::text() const +{ + return m_attribute.text(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceattribute_p.h b/src/location/declarativeplaces/qdeclarativeplaceattribute_p.h new file mode 100644 index 0000000..8079df9 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceattribute_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEATTRIBUTE_P_H +#define QDECLARATIVEPLACEATTRIBUTE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceAttribute : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceAttribute attribute READ attribute WRITE setAttribute) + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + +public: + explicit QDeclarativePlaceAttribute(QObject *parent = 0); + explicit QDeclarativePlaceAttribute(const QPlaceAttribute &src, QObject *parent = 0); + ~QDeclarativePlaceAttribute(); + + QPlaceAttribute attribute() const; + void setAttribute(const QPlaceAttribute &place); + + QString text() const; + void setText(const QString &text); + + + QString label() const; + void setLabel(const QString &label); + +Q_SIGNALS: + void labelChanged(); + void textChanged(); + +private: + QPlaceAttribute m_attribute; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePlaceAttribute) + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp b/src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp new file mode 100644 index 0000000..1920583 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplacecontentmodel.cpp @@ -0,0 +1,397 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplacecontentmodel_p.h" +#include "qdeclarativeplace_p.h" +#include "qdeclarativegeoserviceprovider_p.h" +#include "qdeclarativeplaceuser_p.h" +#include "error_messages_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativePlaceContentModel::QDeclarativePlaceContentModel(QPlaceContent::Type type, + QObject *parent) +: QAbstractListModel(parent), m_place(0), m_type(type), m_batchSize(1), m_contentCount(-1), + m_reply(0), m_complete(false) +{ +} + +QDeclarativePlaceContentModel::~QDeclarativePlaceContentModel() +{ +} + +/*! + \internal +*/ +QDeclarativePlace *QDeclarativePlaceContentModel::place() const +{ + return m_place; +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::setPlace(QDeclarativePlace *place) +{ + if (m_place != place) { + beginResetModel(); + + int initialCount = m_contentCount; + clearData(); + m_place = place; + endResetModel(); + + emit placeChanged(); + if (initialCount != -1) + emit totalCountChanged(); + + fetchMore(QModelIndex()); + } +} + +/*! + \internal +*/ +int QDeclarativePlaceContentModel::batchSize() const +{ + return m_batchSize; +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::setBatchSize(int batchSize) +{ + if (m_batchSize != batchSize) { + m_batchSize = batchSize; + emit batchSizeChanged(); + } +} + +/*! + \internal +*/ +int QDeclarativePlaceContentModel::totalCount() const +{ + return m_contentCount; +} + +/*! + \internal + Clears the model data but does not reset it. +*/ +void QDeclarativePlaceContentModel::clearData() +{ + qDeleteAll(m_users); + m_users.clear(); + + qDeleteAll(m_suppliers); + m_suppliers.clear(); + + m_content.clear(); + + m_contentCount = -1; + + if (m_reply) { + m_reply->abort(); + m_reply->deleteLater(); + m_reply = 0; + } + + m_nextRequest.clear(); +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::initializeCollection(int totalCount, const QPlaceContent::Collection &collection) +{ + beginResetModel(); + + int initialCount = m_contentCount; + clearData(); + + QMapIterator i(collection); + while (i.hasNext()) { + i.next(); + + const QPlaceContent &content = i.value(); + if (content.type() != m_type) + continue; + + m_content.insert(i.key(), content); + if (!m_suppliers.contains(content.supplier().supplierId())) { + m_suppliers.insert(content.supplier().supplierId(), + new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); + } + if (!m_users.contains(content.user().userId())) { + m_users.insert(content.user().userId(), + new QDeclarativePlaceUser(content.user(), this)); + } + } + + m_contentCount = totalCount; + + if (initialCount != totalCount) + emit totalCountChanged(); + + endResetModel(); +} + +/*! + \internal +*/ +int QDeclarativePlaceContentModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + + return m_content.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativePlaceContentModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceContent &content = m_content.value(index.row()); + + switch (role) { + case SupplierRole: + return QVariant::fromValue(static_cast(m_suppliers.value(content.supplier().supplierId()))); + case PlaceUserRole: + return QVariant::fromValue(static_cast(m_users.value(content.user().userId()))); + case AttributionRole: + return content.attribution(); + default: + return QVariant(); + } +} + +QHash QDeclarativePlaceContentModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(SupplierRole, "supplier"); + roles.insert(PlaceUserRole, "user"); + roles.insert(AttributionRole, "attribution"); + return roles; +} + +/*! + \internal +*/ +bool QDeclarativePlaceContentModel::canFetchMore(const QModelIndex &parent) const +{ + if (parent.isValid()) + return false; + + if (!m_place) + return false; + + if (m_contentCount == -1) + return true; + + return m_content.count() != m_contentCount; +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::fetchMore(const QModelIndex &parent) +{ + if (parent.isValid()) + return; + + if (!m_place) + return; + + if (m_reply) + return; + + if (!m_place->plugin()) + return; + + QDeclarativeGeoServiceProvider *plugin = m_place->plugin(); + + QGeoServiceProvider *serviceProvider = plugin->sharedGeoServiceProvider(); + if (!serviceProvider) + return; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) + return; + + if (m_nextRequest == QPlaceContentRequest()) { + QPlaceContentRequest request; + request.setContentType(m_type); + request.setPlaceId(m_place->place().placeId()); + request.setLimit(m_batchSize); + + m_reply = placeManager->getPlaceContent(request); + } else { + m_reply = placeManager->getPlaceContent(m_nextRequest); + } + + connect(m_reply, SIGNAL(finished()), this, SLOT(fetchFinished()), Qt::QueuedConnection); +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::classBegin() +{ +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::componentComplete() +{ + m_complete = true; + fetchMore(QModelIndex()); +} + +/*! + \internal +*/ +void QDeclarativePlaceContentModel::fetchFinished() +{ + if (!m_reply) + return; + + QPlaceContentReply *reply = m_reply; + m_reply = 0; + + m_nextRequest = reply->nextPageRequest(); + + if (m_contentCount != reply->totalCount()) { + m_contentCount = reply->totalCount(); + emit totalCountChanged(); + } + + if (!reply->content().isEmpty()) { + QPlaceContent::Collection contents = reply->content(); + + //find out which indexes are new and which ones have changed. + QMapIterator it(contents); + QList changedIndexes; + QList newIndexes; + while (it.hasNext()) { + it.next(); + if (!m_content.contains(it.key())) + newIndexes.append(it.key()); + else if (it.value() != m_content.value(it.key())) + changedIndexes.append(it.key()); + } + + //insert new indexes in blocks where within each + //block, the indexes are consecutive. + QListIterator newIndexesIter(newIndexes); + int startIndex = -1; + while (newIndexesIter.hasNext()) { + int currentIndex = newIndexesIter.next(); + if (startIndex == -1) + startIndex = currentIndex; + + if (!newIndexesIter.hasNext() || (newIndexesIter.hasNext() && (newIndexesIter.peekNext() > (currentIndex + 1)))) { + beginInsertRows(QModelIndex(),startIndex,currentIndex); + for (int i = startIndex; i <= currentIndex; ++i) { + const QPlaceContent &content = contents.value(i); + + m_content.insert(i, content); + if (!m_suppliers.contains(content.supplier().supplierId())) { + m_suppliers.insert(content.supplier().supplierId(), + new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); + } + if (!m_users.contains(content.user().userId())) { + m_users.insert(content.user().userId(), + new QDeclarativePlaceUser(content.user(), this)); + } + } + endInsertRows(); + startIndex = -1; + } + } + + //modify changed indexes in blocks where within each + //block, the indexes are consecutive. + startIndex = -1; + QListIterator changedIndexesIter(changedIndexes); + while (changedIndexesIter.hasNext()) { + int currentIndex = changedIndexesIter.next(); + if (startIndex == -1) + startIndex = currentIndex; + + if (!changedIndexesIter.hasNext() || (changedIndexesIter.hasNext() && changedIndexesIter.peekNext() > (currentIndex + 1))) { + for (int i = startIndex; i <= currentIndex; ++i) { + const QPlaceContent &content = contents.value(i); + m_content.insert(i, content); + if (!m_suppliers.contains(content.supplier().supplierId())) { + m_suppliers.insert(content.supplier().supplierId(), + new QDeclarativeSupplier(content.supplier(), m_place->plugin(), this)); + } + if (!m_users.contains(content.user().userId())) { + m_users.insert(content.user().userId(), + new QDeclarativePlaceUser(content.user(), this)); + } + } + emit dataChanged(index(startIndex),index(currentIndex)); + startIndex = -1; + } + } + + // The fetch didn't add any new content and we haven't fetched all content yet. This is + // likely due to the model being prepopulated by Place::getDetails(). Keep fetching more + // data until new content is available. + if (newIndexes.isEmpty() && m_content.count() != m_contentCount) + fetchMore(QModelIndex()); + } + + reply->deleteLater(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h b/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h new file mode 100644 index 0000000..05b559a --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplacecontentmodel_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACECONTENTMODEL_H +#define QDECLARATIVEPLACECONTENTMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativePlace; +class QDeclarativeGeoServiceProvider; +class QGeoServiceProvider; +class QDeclarativeSupplier; +class QDeclarativePlaceUser; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceContentModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativePlace *place READ place WRITE setPlace NOTIFY placeChanged) + Q_PROPERTY(int batchSize READ batchSize WRITE setBatchSize NOTIFY batchSizeChanged) + Q_PROPERTY(int totalCount READ totalCount NOTIFY totalCountChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativePlaceContentModel(QPlaceContent::Type type, QObject *parent = 0); + ~QDeclarativePlaceContentModel(); + + QDeclarativePlace *place() const; + void setPlace(QDeclarativePlace *place); + + int batchSize() const; + void setBatchSize(int batchSize); + + int totalCount() const; + + void clearData(); + + void initializeCollection(int totalCount, const QPlaceContent::Collection &collection); + + // from QAbstractListModel + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + enum Roles { + SupplierRole = Qt::UserRole, + PlaceUserRole, + AttributionRole, + UserRole //indicator for next conten type specific role + }; + + bool canFetchMore(const QModelIndex &parent) const; + void fetchMore(const QModelIndex &parent); + + // from QQmlParserStatus + void classBegin(); + void componentComplete(); + +Q_SIGNALS: + void placeChanged(); + void batchSizeChanged(); + void totalCountChanged(); + +private Q_SLOTS: + void fetchFinished(); + +protected: + QPlaceContent::Collection m_content; + QMap m_suppliers; + QMapm_users; + +private: + QDeclarativePlace *m_place; + QPlaceContent::Type m_type; + int m_batchSize; + int m_contentCount; + + QPlaceContentReply *m_reply; + QPlaceContentRequest m_nextRequest; + + bool m_complete; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPLACECONTENTMODEL_H diff --git a/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel.cpp b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel.cpp new file mode 100644 index 0000000..489cdf5 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplaceeditorialmodel_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype EditorialModel + \instantiates QDeclarativePlaceEditorialModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since QtLocation 5.5 + + \brief The EditorialModel type provides a model of place editorials. + + The EditorialModel is a read-only model used to fetch editorials related to a \l Place. + Binding a \l Place via \l EditorialModel::place initiates an initial fetch of editorials. + The model performs fetches incrementally and is intended to be used in conjunction + with a View such as a \l ListView. When the View reaches the last of the editorials + currently in the model, a fetch is performed to retrieve more if they are available. + The View is automatically updated as the editorials are received. The number of + editorials which are fetched at a time is specified by the \l batchSize property. + The total number of editorials available can be accessed via the \l totalCount property. + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li text + \li string + \li The editorial's textual description of the place. It can be either rich (HTML based) text or plain text + depending upon the provider. + \row + \li title + \li string + \li The title of the editorial. + \row + \li language + \li string + \li The language that the editorial is written in. + \row + \li supplier + \li \l Supplier + \li The supplier of the editorial. + \row + \li user + \li \l {QtLocation::User}{User} + \li The user who contributed the editorial. + \row + \li attribution + \li string + \li Attribution text which must be displayed when displaying the editorial. + \endtable + + \section1 Example + + The following example shows how to display editorials for a place: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml EditorialModel + +*/ + +/*! + \qmlproperty Place EditorialModel::place + + This property holds the Place that the editorials are for. +*/ + +/*! + \qmlproperty int EditorialModel::batchSize + + This property holds the batch size to use when fetching more editorials items. +*/ + +/*! + \qmlproperty int EditorialModel::totalCount + + This property holds the total number of editorial items for the place. +*/ + +QDeclarativePlaceEditorialModel::QDeclarativePlaceEditorialModel(QObject *parent) +: QDeclarativePlaceContentModel(QPlaceContent::EditorialType, parent) +{ +} + +QDeclarativePlaceEditorialModel::~QDeclarativePlaceEditorialModel() +{ +} + +/*! + \internal +*/ +QVariant QDeclarativePlaceEditorialModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceEditorial &description = m_content.value(index.row()); + + switch (role) { + case TextRole: + return description.text(); + case TitleRole: + return description.title(); + case LanguageRole: + return description.language(); + } + + return QDeclarativePlaceContentModel::data(index, role); +} + +QHash QDeclarativePlaceEditorialModel::roleNames() const +{ + QHash roleNames = QDeclarativePlaceContentModel::roleNames(); + roleNames.insert(TextRole, "text"); + roleNames.insert(TitleRole, "title"); + roleNames.insert(LanguageRole, "language"); + return roleNames; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h new file mode 100644 index 0000000..b00e2af --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceeditorialmodel_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEEDITORIALMODEL_H +#define QDECLARATIVEPLACEEDITORIALMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceEditorialModel : public QDeclarativePlaceContentModel +{ + Q_OBJECT + +public: + explicit QDeclarativePlaceEditorialModel(QObject *parent = 0); + ~QDeclarativePlaceEditorialModel(); + + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + enum Roles { + TextRole = UserRole, + TitleRole, + LanguageRole + }; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEPLACEEDITORIALMODEL_H diff --git a/src/location/declarativeplaces/qdeclarativeplaceicon.cpp b/src/location/declarativeplaces/qdeclarativeplaceicon.cpp new file mode 100644 index 0000000..1c8ae96 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceicon.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplaceicon_p.h" +#include "error_messages_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Icon + \instantiates QDeclarativePlaceIcon + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The Icon type represents an icon image source which can have multiple sizes. + + The Icon type can be used in conjunction with an \l Image type to display an icon. + The \l url() function is used to construct an icon URL of a requested size, + the icon which most closely matches the requested size is returned. + + The Icon type also has a parameters map which is a set of key value pairs. The precise + keys to use depend on the + \l {Qt Location#Plugin References and Parameters}{plugin} being used. + The parameters map is used by the \l Plugin to determine which URL to return. + + In the case where an icon can only possibly have one image URL, the + parameter key of \c "singleUrl" can be used with a QUrl value. Any Icon with this + parameter will always return the specified URL regardless of the requested icon + size and not defer to any Plugin. + + The following code shows how to display a 64x64 pixel icon: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Icon + + Alternatively, a default sized icon can be specified like so: + \snippet declarative/places.qml Icon default +*/ + +QDeclarativePlaceIcon::QDeclarativePlaceIcon(QObject *parent) +: QObject(parent), m_plugin(0), m_parameters(new QQmlPropertyMap(this)) +{ +} + +QDeclarativePlaceIcon::QDeclarativePlaceIcon(const QPlaceIcon &icon, QDeclarativeGeoServiceProvider *plugin, QObject *parent) +: QObject(parent), m_parameters(new QQmlPropertyMap(this)) +{ + if (icon.isEmpty()) + m_plugin = 0; + else + m_plugin = plugin; + + initParameters(icon.parameters()); +} + +QDeclarativePlaceIcon::~QDeclarativePlaceIcon() +{ +} + +/*! + \qmlproperty QPlaceIcon Icon::icon + + For details on how to use this property to interface between C++ and QML see + "\l {Icon - QPlaceIcon} {Interfaces between C++ and QML Code}". +*/ +QPlaceIcon QDeclarativePlaceIcon::icon() const +{ + QPlaceIcon result; + + if (m_plugin) + result.setManager(manager()); + else + result.setManager(0); + + QVariantMap params; + foreach (const QString &key, m_parameters->keys()) { + const QVariant value = m_parameters->value(key); + if (value.isValid()) { + params.insert(key, value); + } + } + + result.setParameters(params); + + return result; +} + +void QDeclarativePlaceIcon::setIcon(const QPlaceIcon &src) +{ + initParameters(src.parameters()); +} + +/*! + \qmlmethod url Icon::url(size size) + + Returns a URL for the icon image that most closely matches the given \a size. + + If no plugin has been assigned to the icon, and the parameters do not contain the 'singleUrl' key, a default constructed URL + is returned. + +*/ +QUrl QDeclarativePlaceIcon::url(const QSize &size) const +{ + return icon().url(size); +} + +/*! + \qmlproperty Object Icon::parameters + + This property holds the parameters of the icon and is a map. These parameters + are used by the plugin to return the appropriate URL when url() is called and to + specify locations to save to when saving icons. + + Consult the \l {Qt Location#Plugin References and Parameters}{plugin documentation} + for what parameters are supported and how they should be used. + + Note, due to limitations of the QQmlPropertyMap, it is not possible + to declaratively specify the parameters in QML, assignment of parameters keys + and values can only be accomplished by JavaScript. + +*/ +QQmlPropertyMap *QDeclarativePlaceIcon::parameters() const +{ + return m_parameters; +} + +/*! + \qmlproperty Plugin Icon::plugin + + The property holds the plugin that is responsible for managing this icon. +*/ +QDeclarativeGeoServiceProvider *QDeclarativePlaceIcon::plugin() const +{ + return m_plugin; +} + +void QDeclarativePlaceIcon::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + m_plugin = plugin; + emit pluginChanged(); + + if (!m_plugin) + return; + + if (m_plugin->isAttached()) { + pluginReady(); + } else { + connect(m_plugin, SIGNAL(attached()), + this, SLOT(pluginReady())); + } +} + +/*! + \internal +*/ +void QDeclarativePlaceIcon::pluginReady() +{ + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager || serviceProvider->error() != QGeoServiceProvider::NoError) { + qmlWarning(this) << QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString()); + return; + } +} + +/*! + \internal + Helper function to return the manager from the plugin +*/ +QPlaceManager *QDeclarativePlaceIcon::manager() const +{ + if (!m_plugin) { + qmlWarning(this) << QStringLiteral("Plugin is not assigned to place."); + return 0; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) + return 0; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + + if (!placeManager) + return 0; + + return placeManager; +} + +/*! + \internal +*/ +void QDeclarativePlaceIcon::initParameters(const QVariantMap ¶meterMap) +{ + //clear out old parameters + foreach (const QString &key, m_parameters->keys()) + m_parameters->clear(key); + + foreach (const QString &key, parameterMap.keys()) { + QVariant value = parameterMap.value(key); + m_parameters->insert(key, value); + } +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceicon_p.h b/src/location/declarativeplaces/qdeclarativeplaceicon_p.h new file mode 100644 index 0000000..535d98e --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceicon_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEICON_P_H +#define QDECLARATIVEPLACEICON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QQmlPropertyMap; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceIcon : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceIcon icon READ icon WRITE setIcon) + Q_PROPERTY(QObject *parameters READ parameters NOTIFY parametersChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + +public: + explicit QDeclarativePlaceIcon(QObject *parent = 0); + QDeclarativePlaceIcon(const QPlaceIcon &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativePlaceIcon(); + + QPlaceIcon icon() const; + void setIcon(const QPlaceIcon &src); + + Q_INVOKABLE QUrl url(const QSize &size = QSize()) const; + + QQmlPropertyMap *parameters() const; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + +Q_SIGNALS: + void pluginChanged(); + void parametersChanged(); //in practice is never emitted since parameters cannot be re-assigned + //the declaration is needed to avoid warnings about non-notifyable properties + +private Q_SLOTS: + void pluginReady(); + +private: + QPlaceManager *manager() const; + void initParameters(const QVariantMap ¶meterMap); + QDeclarativeGeoServiceProvider *m_plugin; + QQmlPropertyMap *m_parameters; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeplaceimagemodel.cpp b/src/location/declarativeplaces/qdeclarativeplaceimagemodel.cpp new file mode 100644 index 0000000..e7bdf3c --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceimagemodel.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplaceimagemodel_p.h" +#include "qdeclarativesupplier_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ImageModel + \instantiates QDeclarativePlaceImageModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since QtLocation 5.5 + + \brief The ImageModel type provides a model of place images. + + The ImageModel is a read-only model used to fetch images related to a \l Place. + Binding a \l Place via \l ImageModel::place initiates an initial fetch of images. + The model performs fetches incrementally and is intended to be used in conjunction + with a View such as a \l ListView. When the View reaches the last of the images + currently in the model, a fetch is performed to retrieve more if they are available. + The View is automatically updated as the images are received. The number of images + which are fetched at a time is specified by the \l batchSize property. The total number + of images available can be accessed via the \l totalCount property. + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li url + \li url + \li The URL of the image. + \row + \li imageId + \li string + \li The identifier of the image. + \row + \li mimeType + \li string + \li The MIME type of the image. + \row + \li supplier + \li \l Supplier + \li The supplier of the image. + \row + \li user + \li \l {QtLocation::User}{User} + \li The user who contributed the image. + \row + \li attribution + \li string + \li Attribution text which must be displayed when displaying the image. + \endtable + + + \section1 Example + + The following example shows how to display images for a place: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml ImageModel +*/ + +/*! + \qmlproperty Place ImageModel::place + + This property holds the Place that the images are for. +*/ + +/*! + \qmlproperty int ImageModel::batchSize + + This property holds the batch size to use when fetching more image items. +*/ + +/*! + \qmlproperty int ImageModel::totalCount + + This property holds the total number of image items for the place. +*/ + +QDeclarativePlaceImageModel::QDeclarativePlaceImageModel(QObject *parent) +: QDeclarativePlaceContentModel(QPlaceContent::ImageType, parent) +{ +} + +QDeclarativePlaceImageModel::~QDeclarativePlaceImageModel() +{ + qDeleteAll(m_suppliers); +} + +/*! + \internal +*/ +QVariant QDeclarativePlaceImageModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceImage &image = m_content.value(index.row()); + + switch (role) { + case UrlRole: + return image.url(); + case ImageIdRole: + return image.imageId(); + case MimeTypeRole: + return image.mimeType(); + } + + return QDeclarativePlaceContentModel::data(index, role); +} + +QHash QDeclarativePlaceImageModel::roleNames() const +{ + QHash roles = QDeclarativePlaceContentModel::roleNames(); + roles.insert(UrlRole, "url"); + roles.insert(ImageIdRole, "imageId"); + roles.insert(MimeTypeRole, "mimeType"); + return roles; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h b/src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h new file mode 100644 index 0000000..2c24421 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceimagemodel_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEIMAGEMODEL_P_H +#define QDECLARATIVEPLACEIMAGEMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeSupplier; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceImageModel : public QDeclarativePlaceContentModel +{ + Q_OBJECT + +public: + explicit QDeclarativePlaceImageModel(QObject *parent = 0); + ~QDeclarativePlaceImageModel(); + + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + enum Roles { + UrlRole = UserRole, + ImageIdRole, + MimeTypeRole + }; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeplaceuser.cpp b/src/location/declarativeplaces/qdeclarativeplaceuser.cpp new file mode 100644 index 0000000..8f9f00c --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceuser.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeplaceuser_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype User + \instantiates QDeclarativePlaceUser + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The User type identifies a user who contributed a particular \l Place content item. + + Each \l Place content item has an associated user who contributed the content. This type + provides information about that user. + + \sa ImageModel, ReviewModel, EditorialModel + + \section1 Example + + The following example shows how to display information about the user who + submitted an editorial: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml EditorialModel +*/ + +QDeclarativePlaceUser::QDeclarativePlaceUser(QObject *parent) + : QObject(parent) {} + +QDeclarativePlaceUser::QDeclarativePlaceUser(const QPlaceUser &user, + QObject *parent) + : QObject(parent), + m_user(user) {} + +QDeclarativePlaceUser::~QDeclarativePlaceUser() {} + +/*! + \qmlproperty QPlaceUser QtLocation::User::user + + For details on how to use this property to interface between C++ and QML see + "\l {User - QPlaceUser} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativePlaceUser::setUser(const QPlaceUser &user) +{ + QPlaceUser previousUser = m_user; + m_user = user; + + if (m_user.userId() != previousUser.userId()) + emit userIdChanged(); + + if (m_user.name() != previousUser.name()) + emit nameChanged(); +} + +QPlaceUser QDeclarativePlaceUser::user() const +{ + return m_user; +} + +/*! + \qmlproperty string QtLocation::User::userId + + This property holds the unique identifier of the user. +*/ + +void QDeclarativePlaceUser::setUserId(const QString &id) +{ + if (m_user.userId() == id) + return; + + m_user.setUserId(id); + emit userIdChanged(); +} + +QString QDeclarativePlaceUser::userId() const +{ + return m_user.userId(); +} + +/*! + \qmlproperty string QtLocation::User::name + + This property holds the name of a user. +*/ +void QDeclarativePlaceUser::setName(const QString &name) +{ + if (m_user.name() == name) + return; + + m_user.setName(name); + emit nameChanged(); +} + +QString QDeclarativePlaceUser::name() const +{ + return m_user.name(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeplaceuser_p.h b/src/location/declarativeplaces/qdeclarativeplaceuser_p.h new file mode 100644 index 0000000..8cd6449 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeplaceuser_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPLACEUSER_P_H +#define QDECLARATIVEPLACEUSER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativePlaceUser : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceUser user READ user WRITE setUser) + Q_PROPERTY(QString userId READ userId WRITE setUserId NOTIFY userIdChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + +public: + explicit QDeclarativePlaceUser(QObject *parent = 0); + explicit QDeclarativePlaceUser(const QPlaceUser &src, QObject *parent = 0); + ~QDeclarativePlaceUser(); + + QPlaceUser user() const; + void setUser(const QPlaceUser &src); + + QString userId() const; + void setUserId(const QString &id); + + QString name() const; + void setName(const QString &name); + +Q_SIGNALS: + void userIdChanged(); + void nameChanged(); + +private: + QPlaceUser m_user; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePlaceUser) + +#endif diff --git a/src/location/declarativeplaces/qdeclarativeratings.cpp b/src/location/declarativeplaces/qdeclarativeratings.cpp new file mode 100644 index 0000000..ae6876b --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeratings.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativeratings_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Ratings + \instantiates QDeclarativeRatings + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief The Ratings type holds place rating information. + + Rating information is used to describe how \e good a place is conceived to be. Typically this + information is visualized as a number of stars. The \l average property gives an aggregated + ratings value out of a possible maximum as given by the \l maximum property. + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Ratings +*/ + +QDeclarativeRatings::QDeclarativeRatings(QObject *parent) + : QObject(parent) {} + +QDeclarativeRatings::QDeclarativeRatings(const QPlaceRatings &rating, + QObject *parent) + : QObject(parent), + m_ratings(rating) {} + +QDeclarativeRatings::~QDeclarativeRatings() {} + +/*! + \qmlproperty QPlaceRatings Ratings::ratings + + For details on how to use this property to interface between C++ and QML see + "\l {Ratings - QPlaceRatings} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeRatings::setRatings(const QPlaceRatings &ratings) +{ + QPlaceRatings previous = m_ratings; + m_ratings = ratings; + + if (ratings.average() != previous.average()) { + emit averageChanged(); + } + if (ratings.count() != previous.count()) { + emit countChanged(); + } +} + +QPlaceRatings QDeclarativeRatings::ratings() const +{ + return m_ratings; +} + +/*! + \qmlproperty real Ratings::average + + This property holds the average of the individual ratings. + + \sa maximum +*/ +void QDeclarativeRatings::setAverage(qreal average) +{ + if (m_ratings.average() != average) { + m_ratings.setAverage(average); + emit averageChanged(); + } +} + +qreal QDeclarativeRatings::average() const +{ + return m_ratings.average(); +} + +/*! + \qmlproperty real Ratings::maximum + + This property holds the maximum rating value. +*/ +void QDeclarativeRatings::setMaximum(qreal max) +{ + if (m_ratings.maximum() == max) + return; + + m_ratings.setMaximum(max); + emit maximumChanged(); +} + +qreal QDeclarativeRatings::maximum() const +{ + return m_ratings.maximum(); +} + +/*! + \qmlproperty int Ratings::count + + This property holds the total number of individual user ratings + used in determining the overall ratings \l average. +*/ +void QDeclarativeRatings::setCount(int count) +{ + if (m_ratings.count() != count) { + m_ratings.setCount(count); + emit countChanged(); + } +} + +int QDeclarativeRatings::count() const +{ + return m_ratings.count(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativeratings_p.h b/src/location/declarativeplaces/qdeclarativeratings_p.h new file mode 100644 index 0000000..5ee530d --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativeratings_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVERATINGS_P_H +#define QDECLARATIVERATINGS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeRatings : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QPlaceRatings ratings READ ratings WRITE setRatings) + Q_PROPERTY(qreal average READ average WRITE setAverage NOTIFY averageChanged) + Q_PROPERTY(qreal maximum READ maximum WRITE setMaximum NOTIFY maximumChanged) + Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged) + +public: + explicit QDeclarativeRatings(QObject *parent = 0); + explicit QDeclarativeRatings(const QPlaceRatings &src, QObject *parent = 0); + ~QDeclarativeRatings(); + + QPlaceRatings ratings() const; + void setRatings(const QPlaceRatings &src); + + qreal average() const; + void setAverage(qreal average); + + qreal maximum() const; + void setMaximum(qreal max); + + int count() const; + void setCount(int count); + +Q_SIGNALS: + void averageChanged(); + void maximumChanged(); + void countChanged(); + +private: + QPlaceRatings m_ratings; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeRatings) + +#endif // QDECLARATIVERATING_P_H diff --git a/src/location/declarativeplaces/qdeclarativereviewmodel.cpp b/src/location/declarativeplaces/qdeclarativereviewmodel.cpp new file mode 100644 index 0000000..8189084 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativereviewmodel.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativereviewmodel_p.h" +#include "qdeclarativesupplier_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype ReviewModel + \instantiates QDeclarativeReviewModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since QtLocation 5.5 + + \brief Provides access to reviews of a \l Place. + + The ReviewModel is a read-only model used to fetch reviews about a \l Place. The model + incrementally fetches. The number of reviews which are fetched at a time is specified + by the \l batchSize property. The total number of reviews available can be accessed via the + \l totalCount property. + + To use the ReviewModel we need a view and a delegate. In this snippet we + see the setting up of a ListView with a ReviewModel model and a delegate. + + \snippet places/views/ReviewView.qml ReviewModel delegate + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li dateTime + \li datetime + \li The date and time that the review was posted. + \row + \li text + \li string + \li The review's textual description of the place. It can be either rich (HTML based) text or plain text + depending on the provider. + \row + \li language + \li string + \li The language that the review is written in. + \row + \li rating + \li real + \li The rating that the reviewer gave to the place. + \row + \li reviewId + \li string + \li The identifier of the review. + \row + \li title + \li string + \li The title of the review. + \row + \li supplier + \li \l Supplier + \li The supplier of the review. + \row + \li user + \li \l {QtLocation::User}{User} + \li The user who contributed the review. + \row + \li attribution + \li string + \li Attribution text which must be displayed when displaying the review. + \endtable +*/ + +/*! + \qmlproperty Place QtLocation::ReviewModel::place + + This property holds the Place that the reviews are for. +*/ + +/*! + \qmlproperty int QtLocation::ReviewModel::batchSize + + This property holds the batch size to use when fetching more reviews. +*/ + +/*! + \qmlproperty int QtLocation::ReviewModel::totalCount + + This property holds the total number of reviews for the place. +*/ + +QDeclarativeReviewModel::QDeclarativeReviewModel(QObject *parent) +: QDeclarativePlaceContentModel(QPlaceContent::ReviewType, parent) +{ +} + +QDeclarativeReviewModel::~QDeclarativeReviewModel() +{ + qDeleteAll(m_suppliers); +} + +/*! + \internal +*/ +QVariant QDeclarativeReviewModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + const QPlaceReview &review = m_content.value(index.row()); + + switch (role) { + case DateTimeRole: + return review.dateTime(); + case TextRole: + return review.text(); + case LanguageRole: + return review.language(); + case RatingRole: + return review.rating(); + case ReviewIdRole: + return review.reviewId(); + case TitleRole: + return review.title(); + } + + return QDeclarativePlaceContentModel::data(index, role); +} + +QHash QDeclarativeReviewModel::roleNames() const +{ + QHash roles = QDeclarativePlaceContentModel::roleNames(); + roles.insert(DateTimeRole, "dateTime"); + roles.insert(TextRole, "text"); + roles.insert(LanguageRole, "language"); + roles.insert(RatingRole, "rating"); + roles.insert(ReviewIdRole, "reviewId"); + roles.insert(TitleRole, "title"); + return roles; +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativereviewmodel_p.h b/src/location/declarativeplaces/qdeclarativereviewmodel_p.h new file mode 100644 index 0000000..e6d2bd9 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativereviewmodel_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEREVIEWMODEL_P_H +#define QDECLARATIVEREVIEWMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeReviewModel : public QDeclarativePlaceContentModel +{ + Q_OBJECT + +public: + explicit QDeclarativeReviewModel(QObject *parent = 0); + ~QDeclarativeReviewModel(); + + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + enum Roles { + DateTimeRole = UserRole, + TextRole, + LanguageRole, + RatingRole, + ReviewIdRole, + TitleRole + }; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEREVIEWMODEL_P_H diff --git a/src/location/declarativeplaces/qdeclarativesearchmodelbase.cpp b/src/location/declarativeplaces/qdeclarativesearchmodelbase.cpp new file mode 100644 index 0000000..92e298d --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchmodelbase.cpp @@ -0,0 +1,365 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesearchmodelbase_p.h" +#include "qdeclarativeplace_p.h" +#include "error_messages_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeSearchModelBase::QDeclarativeSearchModelBase(QObject *parent) +: QAbstractListModel(parent), m_plugin(0), m_reply(0), m_complete(false), m_status(Null) +{ +} + +QDeclarativeSearchModelBase::~QDeclarativeSearchModelBase() +{ +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider *QDeclarativeSearchModelBase::plugin() const +{ + return m_plugin; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + initializePlugin(plugin); + + if (m_complete) + emit pluginChanged(); +} + +/*! + \internal +*/ +QVariant QDeclarativeSearchModelBase::searchArea() const +{ + QGeoShape s = m_request.searchArea(); + if (s.type() == QGeoShape::RectangleType) + return QVariant::fromValue(QGeoRectangle(s)); + else if (s.type() == QGeoShape::CircleType) + return QVariant::fromValue(QGeoCircle(s)); + else if (s.type() == QGeoShape::PolygonType) + return QVariant::fromValue(QGeoPolygon(s)); + else + return QVariant::fromValue(s); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setSearchArea(const QVariant &searchArea) +{ + QGeoShape s; + + if (searchArea.userType() == qMetaTypeId()) + s = searchArea.value(); + else if (searchArea.userType() == qMetaTypeId()) + s = searchArea.value(); + else if (searchArea.userType() == qMetaTypeId()) + s = searchArea.value(); + + if (m_request.searchArea() == s) + return; + + m_request.setSearchArea(s); + emit searchAreaChanged(); +} + +/*! + \internal +*/ +int QDeclarativeSearchModelBase::limit() const +{ + return m_request.limit(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setLimit(int limit) +{ + if (m_request.limit() == limit) + return; + + m_request.setLimit(limit); + emit limitChanged(); +} + +/*! + \internal +*/ +bool QDeclarativeSearchModelBase::previousPagesAvailable() const +{ + return m_previousPageRequest != QPlaceSearchRequest(); +} + +/*! + \internal +*/ +bool QDeclarativeSearchModelBase::nextPagesAvailable() const +{ + return m_nextPageRequest != QPlaceSearchRequest(); +} + +/*! + \internal +*/ +QDeclarativeSearchModelBase::Status QDeclarativeSearchModelBase::status() const +{ + return m_status; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setStatus(Status status, const QString &errorString) +{ + Status prevStatus = m_status; + + m_status = status; + m_errorString = errorString; + + if (prevStatus != m_status) + emit statusChanged(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::update() +{ + if (m_reply) + return; + + setStatus(Loading); + + if (!m_plugin) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROPERTY_NOT_SET)); + return; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROVIDER_ERROR) + .arg(m_plugin->name())); + return; + } + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } + + m_reply = sendQuery(placeManager, m_request); + if (!m_reply) { + clearData(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, UNABLE_TO_MAKE_REQUEST)); + return; + } + + m_reply->setParent(this); + connect(m_reply, SIGNAL(finished()), this, SLOT(queryFinished())); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::cancel() +{ + if (!m_reply) + return; + + if (!m_reply->isFinished()) + m_reply->abort(); + + if (m_reply) { + m_reply->deleteLater(); + m_reply = 0; + } + + setStatus(Ready); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::reset() +{ + beginResetModel(); + clearData(); + setStatus(Null); + endResetModel(); +} + +/*! + \internal +*/ +QString QDeclarativeSearchModelBase::errorString() const +{ + return m_errorString; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::previousPage() +{ + if (m_previousPageRequest == QPlaceSearchRequest()) + return; + + m_request = m_previousPageRequest; + update(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::nextPage() +{ + if (m_nextPageRequest == QPlaceSearchRequest()) + return; + + m_request = m_nextPageRequest; + update(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::clearData(bool suppressSignal) +{ + Q_UNUSED(suppressSignal) +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::classBegin() +{ +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::componentComplete() +{ + m_complete = true; +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::initializePlugin(QDeclarativeGeoServiceProvider *plugin) +{ + beginResetModel(); + if (plugin != m_plugin) { + if (m_plugin) + disconnect(m_plugin, SIGNAL(nameChanged(QString)), this, SLOT(pluginNameChanged())); + if (plugin) + connect(plugin, SIGNAL(nameChanged(QString)), this, SLOT(pluginNameChanged())); + m_plugin = plugin; + } + + if (m_plugin) { + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (serviceProvider) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + if (placeManager->childCategoryIds().isEmpty()) { + QPlaceReply *reply = placeManager->initializeCategories(); + connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); + } + } + } + } + + endResetModel(); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::pluginNameChanged() +{ + initializePlugin(m_plugin); +} + +/*! + \internal +*/ +void QDeclarativeSearchModelBase::setPreviousPageRequest(const QPlaceSearchRequest &previous) +{ + if (m_previousPageRequest == previous) + return; + + m_previousPageRequest = previous; + emit previousPagesAvailableChanged(); +} + +void QDeclarativeSearchModelBase::setNextPageRequest(const QPlaceSearchRequest &next) +{ + if (m_nextPageRequest == next) + return; + + m_nextPageRequest = next; + emit nextPagesAvailableChanged(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h b/src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h new file mode 100644 index 0000000..2bf1aab --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchmodelbase_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESEARCHMODELBASE_H +#define QDECLARATIVESEARCHMODELBASE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManager; +class QPlaceSearchRequest; +class QPlaceSearchReply; +class QDeclarativePlace; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSearchModelBase : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QVariant searchArea READ searchArea WRITE setSearchArea NOTIFY searchAreaChanged) + Q_PROPERTY(int limit READ limit WRITE setLimit NOTIFY limitChanged) + Q_PROPERTY(bool previousPagesAvailable READ previousPagesAvailable NOTIFY previousPagesAvailableChanged) + Q_PROPERTY(bool nextPagesAvailable READ nextPagesAvailable NOTIFY nextPagesAvailableChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_ENUMS(Status) + + Q_INTERFACES(QQmlParserStatus) + +public: + enum Status { + Null, + Ready, + Loading, + Error + }; + + explicit QDeclarativeSearchModelBase(QObject *parent = 0); + ~QDeclarativeSearchModelBase(); + + QDeclarativeGeoServiceProvider *plugin() const; + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + + QVariant searchArea() const; + void setSearchArea(const QVariant &searchArea); + + int limit() const; + void setLimit(int limit); + + bool previousPagesAvailable() const; + bool nextPagesAvailable() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + Q_INVOKABLE void update(); + + Q_INVOKABLE void cancel(); + Q_INVOKABLE void reset(); + + Q_INVOKABLE QString errorString() const; + + Q_INVOKABLE void previousPage(); + Q_INVOKABLE void nextPage(); + + virtual void clearData(bool suppressSignal = false); + + // From QQmlParserStatus + virtual void classBegin(); + virtual void componentComplete(); + +Q_SIGNALS: + void pluginChanged(); + void searchAreaChanged(); + void limitChanged(); + void previousPagesAvailableChanged(); + void nextPagesAvailableChanged(); + void statusChanged(); + +protected: + virtual void initializePlugin(QDeclarativeGeoServiceProvider *plugin); + +protected Q_SLOTS: + virtual void queryFinished() = 0; + +private Q_SLOTS: + void pluginNameChanged(); + +protected: + virtual QPlaceReply *sendQuery(QPlaceManager *manager, const QPlaceSearchRequest &request) = 0; + void setPreviousPageRequest(const QPlaceSearchRequest &previous); + void setNextPageRequest(const QPlaceSearchRequest &next); + + QPlaceSearchRequest m_request; + QDeclarativeGeoServiceProvider *m_plugin; + QPlaceReply *m_reply; + +private: + bool m_complete; + Status m_status; + QString m_errorString; + QPlaceSearchRequest m_previousPageRequest; + QPlaceSearchRequest m_nextPageRequest; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESEARCHMODELBASE_H diff --git a/src/location/declarativeplaces/qdeclarativesearchresultmodel.cpp b/src/location/declarativeplaces/qdeclarativesearchresultmodel.cpp new file mode 100644 index 0000000..ed99da1 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchresultmodel.cpp @@ -0,0 +1,918 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesearchresultmodel_p.h" +#include "qdeclarativeplace_p.h" +#include "qdeclarativeplaceicon_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PlaceSearchModel + \instantiates QDeclarativeSearchResultModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since QtLocation 5.5 + + \brief Provides access to place search results. + + PlaceSearchModel provides a model of place search results within the \l searchArea. The + \l searchTerm and \l categories properties can be set to restrict the search results to + places matching those criteria. + + The PlaceSearchModel returns both sponsored and + \l {http://en.wikipedia.org/wiki/Organic_search}{organic search results}. Sponsored search + results will have the \c sponsored role set to true. + + \target PlaceSearchModel Roles + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li type + \li enum + \li The type of search result. + \row + \li title + \li string + \li A string describing the search result. + \row + \li icon + \li PlaceIcon + \li Icon representing the search result. + \row + \li distance + \li real + \li Valid only when the \c type role is \c PlaceResult, the distance to the place + from the center of the \l searchArea. If no \l searchArea + has been specified, the distance is NaN. + \row + \li place + \li \l Place + \li Valid only when the \c type role is \c PlaceResult, an object representing the + place. + \row + \li sponsored + \li bool + \li Valid only when the \c type role is \c PlaceResult, true if the search result is a + sponsored result. + \endtable + + \section2 Search Result Types + + The \c type role can take on the following values: + + \table + \row + \li PlaceSearchModel.UnknownSearchResult + \li The contents of the search result are unknown. + \row + \li PlaceSearchModel.PlaceResult + \li The search result contains a place. + \row + \li PlaceSearchModel.ProposedSearchResult + \li The search result contains a proposed search which may be relevant. + \endtable + + + It can often be helpful to use a \l Loader to create a delegate + that will choose different \l {Component}s based on the search result type. + + \snippet declarative/places_loader.qml Handle Result Types + + \section1 Detection of Updated and Removed Places + + The PlaceSearchModel listens for places that have been updated or removed from its plugin's backend. + If it detects that a place has been updated and that place is currently present in the model, then + it will call \l Place::getDetails to refresh the details. If it detects that a place has been + removed, then correspondingly the place will be removed from the model if it is currently + present. + + \section1 Example + + The following example shows how to use the PlaceSearchModel to search for Pizza restaurants in + close proximity of a given position. A \l searchTerm and \l searchArea are provided to the model + and \l update() is used to perform a lookup query. Note that the model does not incrementally + fetch search results, but rather performs a single fetch when \l update() is run. The \l count + is set to the number of search results returned during the fetch. + + \snippet places_list/places_list.qml Imports + \codeline + \snippet places_list/places_list.qml PlaceSearchModel + + \sa CategoryModel, {QPlaceManager} + + \section1 Paging + The PlaceSearchModel API has some limited support + for paging. The \l nextPage() and \l previousPage() functions as well as + the \l limit property can be used to access + paged search results. When the \l limit property is set + the search result page contains at most \l limit entries (of type place result). + For example, if the backend has 5 search results in total + [a,b,c,d,e], and assuming the first page is shown and limit of 3 has been set + then a,b,c is returned. The \l nextPage() would return d,e. The + \l nextPagesAvailable and \l previousPagesAvailable properties + can be used to check for further pages. At the moment the API does not + support the means to retrieve the total number of items available from the + backed. Note that support for \l nextPage(), previousPage() and \l limit can vary + according to the \l plugin. +*/ + +/*! + \qmlproperty Plugin PlaceSearchModel::plugin + + This property holds the \l Plugin which will be used to perform the search. +*/ + +/*! + \qmlproperty Plugin PlaceSearchModel::favoritesPlugin + + This property holds the \l Plugin which will be used to search for favorites. + Any places from the search which can be cross-referenced or matched + in the favoritesPlugin will have their \l {Place::favorite}{favorite} property + set to the corresponding \l Place from the favoritesPlugin. + + If the favoritesPlugin is not set, the \l {Place::favorite}{favorite} property + of the places in the results will always be null. + + \sa Favorites +*/ + +/*! + \qmlproperty VariantMap PlaceSearchModel::favoritesMatchParameters + + This property holds a set of parameters used to specify how search result places + are matched to favorites in the favoritesPlugin. + + By default the parameter map is empty and implies that the favorites plugin + matches by \l {Alternative Identifier Cross-Referencing}{alternative identifiers}. Generally, + an application developer will not need to set this property. + + In cases where the favorites plugin does not support matching by alternative identifiers, + then the \l {Qt Location#Plugin References and Parameters}{plugin documentation} should + be consulted to see precisely what key-value parameters to set. +*/ + +/*! + \qmlproperty variant PlaceSearchModel::searchArea + + This property holds the search area. The search result returned by the model will be within + the search area. + + If this property is set to a \l {geocircle} its + \l {geocircle}{radius} property may be left unset, in which case the \l Plugin + will choose an appropriate radius for the search. + + Support for specifying a search area can vary according to the \l plugin backend + implementation. For example, some may support a search center only while others may only + support geo rectangles. +*/ + +/*! + \qmlproperty int PlaceSearchModel::limit + + This property holds the limit of the number of items that will be returned. +*/ + +/*! + \qmlproperty bool PlaceSearchModel::previousPagesAvailable + + This property holds whether there is one or more previous pages of search results available. + + \sa previousPage() +*/ + +/*! + \qmlproperty bool PlaceSearchModel::nextPagesAvailable + + This property holds whether there is one or more additional pages of search results available. + + \sa nextPage() +*/ + +/*! + \qmlproperty enum PlaceSearchModel::status + + This property holds the status of the model. It can be one of: + + \table + \row + \li PlaceSearchModel.Null + \li No search query has been executed. The model is empty. + \row + \li PlaceSearchModel.Ready + \li The search query has completed, and the results are available. + \row + \li PlaceSearchModel.Loading + \li A search query is currently being executed. + \row + \li PlaceSearchModel.Error + \li An error occurred when executing the previous search query. + \endtable +*/ + +/*! + \qmlmethod void PlaceSearchModel::update() + + Updates the model based on the provided query parameters. The model will be populated with a + list of places matching the search parameters specified by the type's properties. Search + criteria is specified by setting properties such as the \l searchTerm, \l categories, \l searchArea and \l limit. + Support for these properties may vary according to \l plugin. \c update() then + submits the set of criteria to the \l plugin to process. + + While the model is updating the \l status of the model is set to + \c PlaceSearchModel.Loading. If the model is successfully updated the \l status is set to + \c PlaceSearchModel.Ready, while if it unsuccessfully completes, the \l status is set to + \c PlaceSearchModel.Error and the model cleared. + + \code + PlaceSearchModel { + id: model + plugin: backendPlugin + searchArea: QtPositioning.circle(QtPositioning.coordinate(10, 10)) + ... + } + + MouseArea { + ... + onClicked: { + model.searchTerm = "pizza"; + model.categories = null; //not searching by any category + model.searchArea.center.latitude = -27.5; + model.searchArea.center.longitude = 153; + model.update(); + } + } + \endcode + + \sa cancel(), status +*/ + +/*! + \qmlmethod void PlaceSearchModel::cancel() + + Cancels an ongoing search operation immediately and sets the model + status to PlaceSearchModel.Ready. The model retains any search + results it had before the operation was started. + + If an operation is not ongoing, invoking cancel() has no effect. + + \sa update(), status +*/ + +/*! + \qmlmethod void PlaceSearchModel::reset() + + Resets the model. All search results are cleared, any outstanding requests are aborted and + possible errors are cleared. Model status will be set to PlaceSearchModel.Null. +*/ + +/*! + \qmlmethod string PlaceSearchModel::errorString() const + + This read-only property holds the textual presentation of the latest place search model error. + If no error has occurred or if the model was cleared, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. +*/ + +/*! + \qmlmethod void PlaceSearchModel::previousPage() + + Updates the model to display the previous page of search results. If there is no previous page + then this method does nothing. +*/ + +/*! + \qmlmethod void PlaceSearchModel::nextPage() + + Updates the model to display the next page of search results. If there is no next page then + this method does nothing. +*/ + +QDeclarativeSearchResultModel::QDeclarativeSearchResultModel(QObject *parent) + : QDeclarativeSearchModelBase(parent), m_favoritesPlugin(0) +{ +} + +QDeclarativeSearchResultModel::~QDeclarativeSearchResultModel() +{ +} + +/*! + \qmlproperty string PlaceSearchModel::searchTerm + + This property holds search term used in query. The search term is a free-form text string. +*/ +QString QDeclarativeSearchResultModel::searchTerm() const +{ + return m_request.searchTerm(); +} + +void QDeclarativeSearchResultModel::setSearchTerm(const QString &searchTerm) +{ + m_request.setSearchContext(QVariant()); + + if (m_request.searchTerm() == searchTerm) + return; + + m_request.setSearchTerm(searchTerm); + emit searchTermChanged(); +} + +/*! + \qmlproperty list PlaceSearchModel::categories + + This property holds a list of categories to be used when searching. Returned search results + will be for places that match at least one of the categories. +*/ +QQmlListProperty QDeclarativeSearchResultModel::categories() +{ + return QQmlListProperty(this, + 0, // opaque data parameter + categories_append, + categories_count, + category_at, + categories_clear); +} + +void QDeclarativeSearchResultModel::categories_append(QQmlListProperty *list, + QDeclarativeCategory *declCategory) +{ + QDeclarativeSearchResultModel *searchModel = qobject_cast(list->object); + if (searchModel && declCategory) { + searchModel->m_request.setSearchContext(QVariant()); + searchModel->m_categories.append(declCategory); + QList categories = searchModel->m_request.categories(); + categories.append(declCategory->category()); + searchModel->m_request.setCategories(categories); + emit searchModel->categoriesChanged(); + } +} + +int QDeclarativeSearchResultModel::categories_count(QQmlListProperty *list) +{ + QDeclarativeSearchResultModel *searchModel = qobject_cast(list->object); + if (searchModel) + return searchModel->m_categories.count(); + else + return -1; +} + +QDeclarativeCategory *QDeclarativeSearchResultModel::category_at(QQmlListProperty *list, + int index) +{ + QDeclarativeSearchResultModel *searchModel = qobject_cast(list->object); + if (searchModel && (searchModel->m_categories.count() > index) && (index > -1)) + return searchModel->m_categories.at(index); + else + return 0; +} + +void QDeclarativeSearchResultModel::categories_clear(QQmlListProperty *list) +{ + QDeclarativeSearchResultModel *searchModel = qobject_cast(list->object); + if (searchModel) { + //note: we do not need to delete each of the objects in m_categories since the search model + //should never be the parent of the categories anyway. + searchModel->m_request.setSearchContext(QVariant()); + searchModel->m_categories.clear(); + searchModel->m_request.setCategories(QList()); + emit searchModel->categoriesChanged(); + } +} + +/*! + \qmlproperty string PlaceSearchModel::recommendationId + + This property holds the placeId to be used in order to find recommendations + for similar places. +*/ +QString QDeclarativeSearchResultModel::recommendationId() const +{ + return m_request.recommendationId(); +} + +void QDeclarativeSearchResultModel::setRecommendationId(const QString &placeId) +{ + if (m_request.recommendationId() == placeId) + return; + + m_request.setRecommendationId(placeId); + emit recommendationIdChanged(); +} + +/*! + \qmlproperty enumeration PlaceSearchModel::relevanceHint + + This property holds a relevance hint used in the search query. The hint is given to the + provider to help but not dictate the ranking of results. For example, the distance hint may + give closer places a higher ranking but it does not necessarily mean the results will be + strictly ordered according to distance. A provider may ignore the hint altogether. + + \table + \row + \li SearchResultModel.UnspecifiedHint + \li No relevance hint is given to the provider. + \row + \li SearchResultModel.DistanceHint + \li The distance of the place from the user's current location is important to the user. + This hint is only meaningful when a circular search area is used. + \row + \li SearchResultModel.LexicalPlaceNameHint + \li The lexical ordering of place names (in ascending alphabetical order) is relevant to + the user. This hint is useful for providers based on a local data store. + \endtable +*/ +QDeclarativeSearchResultModel::RelevanceHint QDeclarativeSearchResultModel::relevanceHint() const +{ + return static_cast(m_request.relevanceHint()); +} + +void QDeclarativeSearchResultModel::setRelevanceHint(QDeclarativeSearchResultModel::RelevanceHint hint) +{ + if (m_request.relevanceHint() != static_cast(hint)) { + m_request.setRelevanceHint(static_cast(hint)); + emit relevanceHintChanged(); + } +} + +/*! + \qmlproperty enum PlaceSearchModel::visibilityScope + + This property holds the visibility scope of the places to search. Only places with the + specified visibility will be returned in the search results. + + The visibility scope can be one of: + + \table + \row + \li Place.UnspecifiedVisibility + \li No explicit visibility scope specified, places with any visibility may be part of + search results. + \row + \li Place.DeviceVisibility + \li Only places stored on the local device will be part of the search results. + \row + \li Place.PrivateVisibility + \li Only places that are private to the current user will be part of the search results. + \row + \li Place.PublicVisibility + \li Only places that are public will be part of the search results. + \endtable +*/ +QDeclarativePlace::Visibility QDeclarativeSearchResultModel::visibilityScope() const +{ + return QDeclarativePlace::Visibility(int(m_visibilityScope)); +} + +void QDeclarativeSearchResultModel::setVisibilityScope(QDeclarativePlace::Visibility visibilityScope) +{ + QLocation::VisibilityScope scope = QLocation::VisibilityScope(visibilityScope); + + if (m_visibilityScope == scope) + return; + + m_visibilityScope = scope; + emit visibilityScopeChanged(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider *QDeclarativeSearchResultModel::favoritesPlugin() const +{ + return m_favoritesPlugin; +} + +/*! + \internal +*/ +void QDeclarativeSearchResultModel::setFavoritesPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + + if (m_favoritesPlugin == plugin) + return; + + m_favoritesPlugin = plugin; + + if (m_favoritesPlugin) { + QGeoServiceProvider *serviceProvider = m_favoritesPlugin->sharedGeoServiceProvider(); + if (serviceProvider) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + if (placeManager->childCategoryIds().isEmpty()) { + QPlaceReply *reply = placeManager->initializeCategories(); + connect(reply, SIGNAL(finished()), reply, SLOT(deleteLater())); + } + } + } + } + + emit favoritesPluginChanged(); +} + +/*! + \internal +*/ +QVariantMap QDeclarativeSearchResultModel::favoritesMatchParameters() const +{ + return m_matchParameters; +} + +/*! + \internal +*/ +void QDeclarativeSearchResultModel::setFavoritesMatchParameters(const QVariantMap ¶meters) +{ + if (m_matchParameters == parameters) + return; + + m_matchParameters = parameters; + emit favoritesMatchParametersChanged(); +} + +/*! + \internal +*/ +int QDeclarativeSearchResultModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent); + + return m_results.count(); +} + +void QDeclarativeSearchResultModel::clearData(bool suppressSignal) +{ + QDeclarativeSearchModelBase::clearData(suppressSignal); + + qDeleteAll(m_places); + m_places.clear(); + qDeleteAll(m_icons); + m_icons.clear(); + if (!m_results.isEmpty()) { + m_results.clear(); + + if (!suppressSignal) + emit rowCountChanged(); + } +} + +QVariant QDeclarativeSearchResultModel::data(const QModelIndex &index, int role) const +{ + if (index.row() > m_results.count()) + return QVariant(); + + const QPlaceSearchResult &result = m_results.at(index.row()); + + switch (role) { + case SearchResultTypeRole: + return result.type(); + case Qt::DisplayRole: + case TitleRole: + return result.title(); + case IconRole: + return QVariant::fromValue(static_cast(m_icons.at(index.row()))); + case DistanceRole: + if (result.type() == QPlaceSearchResult::PlaceResult) { + QPlaceResult placeResult = result; + return placeResult.distance(); + } + break; + case PlaceRole: + if (result.type() == QPlaceSearchResult::PlaceResult) + return QVariant::fromValue(static_cast(m_places.at(index.row()))); + break; + case SponsoredRole: + if (result.type() == QPlaceSearchResult::PlaceResult) { + QPlaceResult placeResult = result; + return placeResult.isSponsored(); + } + break; + } + return QVariant(); +} + +/*! + \internal +*/ +QVariant QDeclarativeSearchResultModel::data(int index, const QString &role) const +{ + QModelIndex modelIndex = createIndex(index, 0); + return data(modelIndex, roleNames().key(role.toLatin1())); +} + +QHash QDeclarativeSearchResultModel::roleNames() const +{ + QHash roles = QDeclarativeSearchModelBase::roleNames(); + roles.insert(SearchResultTypeRole, "type"); + roles.insert(TitleRole, "title"); + roles.insert(IconRole, "icon"); + roles.insert(DistanceRole, "distance"); + roles.insert(PlaceRole, "place"); + roles.insert(SponsoredRole, "sponsored"); + + return roles; +} + +/*! + \qmlmethod void PlaceSearchModel::updateWith(int proposedSearchIndex) + + Updates the model based on the ProposedSearchResult at index \a proposedSearchIndex. The model + will be populated with a list of places matching the proposed search. Model status will be set + to PlaceSearchModel.Loading. If the model is updated successfully status will be set to + PlaceSearchModel.Ready. If an error occurs status will be set to PlaceSearchModel.Error and the + model cleared. + + If \a proposedSearchIndex does not reference a ProposedSearchResult this method does nothing. +*/ +void QDeclarativeSearchResultModel::updateWith(int proposedSearchIndex) +{ + if (m_results.at(proposedSearchIndex).type() != QPlaceSearchResult::ProposedSearchResult) + return; + + m_request = QPlaceProposedSearchResult(m_results.at(proposedSearchIndex)).searchRequest(); + update(); +} + +QPlaceReply *QDeclarativeSearchResultModel::sendQuery(QPlaceManager *manager, + const QPlaceSearchRequest &request) +{ + Q_ASSERT(manager); + return manager->search(request); +} + +/*! + \internal +*/ +void QDeclarativeSearchResultModel::initializePlugin(QDeclarativeGeoServiceProvider *plugin) +{ + //disconnect the manager of the old plugin if we have one + if (m_plugin) { + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (serviceProvider) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + disconnect(placeManager, SIGNAL(placeUpdated(QString)), this, SLOT(placeUpdated(QString))); + disconnect(placeManager, SIGNAL(placeRemoved(QString)), this, SLOT(placeRemoved(QString))); + connect(placeManager, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + } + } + } + + //connect to the manager of the new plugin. + if (plugin) { + QGeoServiceProvider *serviceProvider = plugin->sharedGeoServiceProvider(); + if (serviceProvider) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + connect(placeManager, SIGNAL(placeUpdated(QString)), this, SLOT(placeUpdated(QString))); + connect(placeManager, SIGNAL(placeRemoved(QString)), this, SLOT(placeRemoved(QString))); + disconnect(placeManager, SIGNAL(dataChanged()), this, SIGNAL(dataChanged())); + } + } + } + QDeclarativeSearchModelBase::initializePlugin(plugin); +} + +/*! + \internal +*/ +void QDeclarativeSearchResultModel::queryFinished() +{ + if (!m_reply) + return; + QPlaceReply *reply = m_reply; + m_reply = 0; + if (reply->error() != QPlaceReply::NoError) { + m_resultsBuffer.clear(); + updateLayout(); + setStatus(Error, reply->errorString()); + reply->deleteLater(); + return; + } + + if (reply->type() == QPlaceReply::SearchReply) { + QPlaceSearchReply *searchReply = qobject_cast(reply); + Q_ASSERT(searchReply); + + m_resultsBuffer = searchReply->results(); + setPreviousPageRequest(searchReply->previousPageRequest()); + setNextPageRequest(searchReply->nextPageRequest()); + + reply->deleteLater(); + + if (!m_favoritesPlugin) { + updateLayout(); + setStatus(Ready); + } else { + QGeoServiceProvider *serviceProvider = m_favoritesPlugin->sharedGeoServiceProvider(); + if (!serviceProvider) { + updateLayout(); + setStatus(Error, QStringLiteral("Favorites plugin returns a null QGeoServiceProvider instance")); + return; + } + + QPlaceManager *favoritesManager = serviceProvider->placeManager(); + if (!favoritesManager) { + updateLayout(); + setStatus(Error, QStringLiteral("Favorites plugin returns a null QPlaceManager")); + return; + } + + QPlaceMatchRequest request; + if (m_matchParameters.isEmpty()) { + if (!m_plugin) { + reply->deleteLater(); + setStatus(Error, QStringLiteral("Plugin not assigned")); + return; + } + + QVariantMap params; + params.insert(QPlaceMatchRequest::AlternativeId, QVariant(QString::fromLatin1("x_id_") + m_plugin->name())); + request.setParameters(params); + } else { + request.setParameters(m_matchParameters); + } + + request.setResults(m_resultsBuffer); + m_reply = favoritesManager->matchingPlaces(request); + connect(m_reply, SIGNAL(finished()), this, SLOT(queryFinished())); + } + } else if (reply->type() == QPlaceReply::MatchReply) { + QPlaceMatchReply *matchReply = qobject_cast(reply); + Q_ASSERT(matchReply); + updateLayout(matchReply->places()); + setStatus(Ready); + reply->deleteLater(); + } else { + setStatus(Error, QStringLiteral("Unknown reply type")); + reply->deleteLater(); + } +} + +/*! + \qmlmethod Variant PlaceSearchModel::data(int index, string role) + + Returns the data for a given \a role at the specified row \a index. +*/ + +/*! + \qmlproperty int PlaceSearchModel::count + + This property holds the number of results the model has. + + Note that it does not refer to the total number of search results + available in the backend. The total number of search results + is not currently supported by the API. +*/ + +/*! + \internal + Note: m_results buffer should be correctly populated before + calling this function +*/ +void QDeclarativeSearchResultModel::updateLayout(const QList &favoritePlaces) +{ + int oldRowCount = rowCount(); + + beginResetModel(); + clearData(true); + m_results = m_resultsBuffer; + m_resultsBuffer.clear(); + + for (int i = 0; i < m_results.count(); ++i) { + const QPlaceSearchResult &result = m_results.at(i); + + if (result.type() == QPlaceSearchResult::PlaceResult) { + QPlaceResult placeResult = result; + QDeclarativePlace *place = new QDeclarativePlace(placeResult.place(), plugin(), this); + m_places.append(place); + + if ((favoritePlaces.count() == m_results.count()) && favoritePlaces.at(i) != QPlace()) + m_places[i]->setFavorite(new QDeclarativePlace(favoritePlaces.at(i), + m_favoritesPlugin, m_places[i])); + } else if (result.type() == QPlaceSearchResult::ProposedSearchResult) { + m_places.append(0); + } + + QDeclarativePlaceIcon *icon = 0; + if (!result.icon().isEmpty()) + icon = new QDeclarativePlaceIcon(result.icon(), plugin(), this); + m_icons.append(icon); + } + + endResetModel(); + if (m_results.count() != oldRowCount) + emit rowCountChanged(); +} + +/*! + \internal +*/ +void QDeclarativeSearchResultModel::placeUpdated(const QString &placeId) +{ + int row = getRow(placeId); + if (row < 0 || row > m_places.count()) + return; + + if (m_places.at(row)) + m_places.at(row)->getDetails(); +} + +/*! + \internal +*/ +void QDeclarativeSearchResultModel::placeRemoved(const QString &placeId) +{ + int row = getRow(placeId); + if (row < 0 || row > m_places.count()) + return; + + beginRemoveRows(QModelIndex(), row, row); + delete m_places.at(row); + m_places.removeAt(row); + m_results.removeAt(row); + endRemoveRows(); + + emit rowCountChanged(); +} + +/*! + \internal +*/ +int QDeclarativeSearchResultModel::getRow(const QString &placeId) const +{ + for (int i = 0; i < m_places.count(); ++i) { + if (!m_places.at(i)) + continue; + else if (m_places.at(i)->placeId() == placeId) + return i; + } + + return -1; +} + +/*! + \qmlsignal PlaceSearchResultModel::dataChanged() + + This signal is emitted when significant changes have been made to the underlying datastore. + + Applications should act on this signal at their own discretion. The data + provided by the model could be out of date and so the model should be reupdated + sometime, however an immediate reupdate may be disconcerting to users if the results + change without any action on their part. + + The corresponding handler is \c onDataChanged. +*/ + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h b/src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h new file mode 100644 index 0000000..77526fd --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchresultmodel_p.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESEARCHRESULTMODEL_P_H +#define QDECLARATIVESEARCHRESULTMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSearchResultModel : public QDeclarativeSearchModelBase +{ + Q_OBJECT + + Q_PROPERTY(QString searchTerm READ searchTerm WRITE setSearchTerm NOTIFY searchTermChanged) + Q_PROPERTY(QQmlListProperty categories READ categories NOTIFY categoriesChanged) + Q_PROPERTY(QString recommendationId READ recommendationId WRITE setRecommendationId NOTIFY recommendationIdChanged) + Q_PROPERTY(RelevanceHint relevanceHint READ relevanceHint WRITE setRelevanceHint NOTIFY relevanceHintChanged) + Q_PROPERTY(QDeclarativePlace::Visibility visibilityScope READ visibilityScope WRITE setVisibilityScope NOTIFY visibilityScopeChanged) + + Q_PROPERTY(int count READ rowCount NOTIFY rowCountChanged) + Q_PROPERTY(QDeclarativeGeoServiceProvider *favoritesPlugin READ favoritesPlugin WRITE setFavoritesPlugin NOTIFY favoritesPluginChanged) + Q_PROPERTY(QVariantMap favoritesMatchParameters READ favoritesMatchParameters WRITE setFavoritesMatchParameters NOTIFY favoritesMatchParametersChanged) + + Q_ENUMS(SearchResultType RelevanceHint) + +public: + enum SearchResultType { + UnknownSearchResult = QPlaceSearchResult::UnknownSearchResult, + PlaceResult = QPlaceSearchResult::PlaceResult, + ProposedSearchResult = QPlaceSearchResult::ProposedSearchResult + }; + + enum RelevanceHint { + UnspecifiedHint = QPlaceSearchRequest::UnspecifiedHint, + DistanceHint = QPlaceSearchRequest::DistanceHint, + LexicalPlaceNameHint = QPlaceSearchRequest::LexicalPlaceNameHint + }; + + explicit QDeclarativeSearchResultModel(QObject *parent = 0); + ~QDeclarativeSearchResultModel(); + + QString searchTerm() const; + void setSearchTerm(const QString &searchTerm); + + QQmlListProperty categories(); + static void categories_append(QQmlListProperty *list, + QDeclarativeCategory *category); + static int categories_count(QQmlListProperty *list); + static QDeclarativeCategory *category_at(QQmlListProperty *list, int index); + static void categories_clear(QQmlListProperty *list); + + QString recommendationId() const; + void setRecommendationId(const QString &recommendationId); + + QDeclarativeSearchResultModel::RelevanceHint relevanceHint() const; + void setRelevanceHint(QDeclarativeSearchResultModel::RelevanceHint hint); + + QDeclarativePlace::Visibility visibilityScope() const; + void setVisibilityScope(QDeclarativePlace::Visibility visibilityScope); + + QDeclarativeGeoServiceProvider *favoritesPlugin() const; + void setFavoritesPlugin(QDeclarativeGeoServiceProvider *plugin); + + QVariantMap favoritesMatchParameters() const; + void setFavoritesMatchParameters(const QVariantMap ¶meters); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + + virtual void clearData(bool suppressSignal = false); + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + Q_INVOKABLE QVariant data(int index, const QString &roleName) const; + QHash roleNames() const; + + Q_INVOKABLE void updateWith(int proposedSearchIndex); + + void updateSearchRequest(); + +Q_SIGNALS: + void searchTermChanged(); + void categoriesChanged(); + void recommendationIdChanged(); + void relevanceHintChanged(); + void visibilityScopeChanged(); + + void rowCountChanged(); + void favoritesPluginChanged(); + void favoritesMatchParametersChanged(); + void dataChanged(); + +protected: + QPlaceReply *sendQuery(QPlaceManager *manager, const QPlaceSearchRequest &request); + virtual void initializePlugin(QDeclarativeGeoServiceProvider *plugin); + +protected Q_SLOTS: + virtual void queryFinished(); + +private Q_SLOTS: + void updateLayout(const QList &favoritePlaces = QList()); + + void placeUpdated(const QString &placeId); + void placeRemoved(const QString &placeId); + +private: + enum Roles { + SearchResultTypeRole = Qt::UserRole, + TitleRole, + IconRole, + DistanceRole, + PlaceRole, + SponsoredRole + }; + + int getRow(const QString &placeId) const; + QList m_categories; + QLocation::VisibilityScope m_visibilityScope; + + QList m_results; + QList m_resultsBuffer; + QList m_places; + QList m_icons; + + QDeclarativeGeoServiceProvider *m_favoritesPlugin; + QVariantMap m_matchParameters; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVESEARCHRESULTMODEL_P_H diff --git a/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel.cpp b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel.cpp new file mode 100644 index 0000000..c416858 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesearchsuggestionmodel_p.h" +#include "qdeclarativegeoserviceprovider_p.h" + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PlaceSearchSuggestionModel + \instantiates QDeclarativeSearchSuggestionModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since QtLocation 5.5 + + \brief Provides access to search term suggestions. + + The PlaceSearchSuggestionModel can be used to provide search term suggestions as the user enters their + search term. The properties of this model should match that of the \l PlaceSearchModel, which + will be used to perform the actual search query, to ensure that the search suggestion results are + relevant. + + There are two ways of accessing the data provided by this model, either through the + \l suggestions property or through views and delegates. The latter is the preferred + method. + + The \l offset and \l limit properties can be used to access paged suggestions. When the + \l offset and \l limit properties are set the suggestions between \l offset and + (\l offset + \l limit - 1) will be returned. Support for paging may vary + from plugin to plugin. + + The model returns data for the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li suggestion + \li string + \li Suggested search term. + \endtable + + The following example shows how to use the PlaceSearchSuggestionModel to get suggested search terms + from a partial search term. The \l searchArea is set to match what would be used to perform the + actual place search with \l PlaceSearchModel. + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml SearchSuggestionModel + + \sa PlaceSearchModel, {QPlaceManager} +*/ + +/*! + \qmlproperty Plugin PlaceSearchSuggestionModel::plugin + + This property holds the provider \l Plugin which will be used to perform the search. +*/ + +/*! + \qmlproperty geoshape PlaceSearchSuggestionModel::searchArea + + This property holds the search area. Search suggestion results returned by the model will be + relevant to the given search area. + + If this property is set to a \l {geocircle} its + \l {geocircle}{radius} property may be left unset, in which case the \l Plugin + will choose an appropriate radius for the search. +*/ + +/*! + \qmlproperty int PlaceSearchSuggestionModel::offset + + This property holds the index of the first item in the model. + + \sa limit +*/ + +/*! + \qmlproperty int PlaceSearchSuggestionModel::limit + + This property holds the limit of the number of items that will be returned. + + \sa offset +*/ + +/*! + \qmlproperty enum PlaceSearchSuggestionModel::status + + This property holds the status of the model. It can be one of: + + \table + \row + \li PlaceSearchSuggestionModel.Null + \li No search query has been executed. The model is empty. + \row + \li PlaceSearchSuggestionModel.Ready + \li The search query has completed, and the results are available. + \row + \li PlaceSearchSuggestionModel.Loading + \li A search query is currently being executed. + \row + \li PlaceSearchSuggestionModel.Error + \li An error occurred when executing the previous search query. + \endtable +*/ + +/*! + \qmlmethod void PlaceSearchSuggestionModel::update() + + Updates the model based on the provided query parameters. The model will be populated with a + list of search suggestions for the partial \l searchTerm and \l searchArea. If the \l plugin + supports it, other parameters such as \l limit and \l offset may be specified. \c update() + submits the set of parameters to the \l plugin to process. + + + While the model is updating the \l status of the model is set to + \c PlaceSearchSuggestionModel.Loading. If the model is successfully updated, the \l status is + set to \c PlaceSearchSuggestionModel.Ready, while if it unsuccessfully completes, the \l status + is set to \c PlaceSearchSuggestionModel.Error and the model cleared. + + This example shows use of the model + \code + PlaceSeachSuggestionModel { + id: model + plugin: backendPlugin + searchArea: QtPositioning.circle(QtPositioning.coordinate(10, 10)) + ... + } + + MouseArea { + ... + onClicked: { + model.searchTerm = "piz" + model.searchArea.center.latitude = -27.5; + model.searchArea.cetner.longitude = 153; + model.update(); + } + } + \endcode + + A more detailed example can be found in the in + \l {Places (QML)#Presenting-Search-Suggestions}{Places (QML)} example. + + \sa cancel(), status +*/ + +/*! + \qmlmethod void PlaceSearchSuggestionModel::cancel() + + Cancels an ongoing search suggestion operation immediately and sets the model + status to PlaceSearchSuggestionModel.Ready. The model retains any search + suggestions it had before the operation was started. + + If an operation is not ongoing, invoking cancel() has no effect. + + \sa update(), status +*/ + +/*! + \qmlmethod void PlaceSearchSuggestionModel::reset() + + Resets the model. All search suggestions are cleared, any outstanding requests are aborted and + possible errors are cleared. Model status will be set to PlaceSearchSuggestionModel.Null. +*/ + + +/*! + \qmlmethod string QtLocation::PlaceSearchSuggestionModel::errorString() const + + This read-only property holds the textual presentation of the latest search suggestion model error. + If no error has occurred, or if the model was cleared, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. +*/ + +QDeclarativeSearchSuggestionModel::QDeclarativeSearchSuggestionModel(QObject *parent) +: QDeclarativeSearchModelBase(parent) +{ +} + +QDeclarativeSearchSuggestionModel::~QDeclarativeSearchSuggestionModel() +{ +} + +/*! + \qmlproperty string PlaceSearchSuggestionModel::searchTerm + + This property holds the partial search term used in query. +*/ +QString QDeclarativeSearchSuggestionModel::searchTerm() const +{ + return m_request.searchTerm(); +} + +void QDeclarativeSearchSuggestionModel::setSearchTerm(const QString &searchTerm) +{ + if (m_request.searchTerm() == searchTerm) + return; + + m_request.setSearchTerm(searchTerm); + emit searchTermChanged(); +} + +/*! + \qmlproperty stringlist PlaceSearchSuggestionModel::suggestions + + This property holds the list of predicted search terms that the model currently has. +*/ +QStringList QDeclarativeSearchSuggestionModel::suggestions() const +{ + return m_suggestions; +} + +/*! + \internal +*/ +void QDeclarativeSearchSuggestionModel::clearData(bool suppressSignal) +{ + QDeclarativeSearchModelBase::clearData(suppressSignal); + + if (!m_suggestions.isEmpty()) { + m_suggestions.clear(); + + if (!suppressSignal) + emit suggestionsChanged(); + } +} + +/*! + \internal +*/ +int QDeclarativeSearchSuggestionModel::rowCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return m_suggestions.count(); +} + +/*! + \internal +*/ +QVariant QDeclarativeSearchSuggestionModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() >= rowCount(index.parent()) || index.row() < 0) + return QVariant(); + + switch (role) { + case Qt::DisplayRole: + case SearchSuggestionRole: + return m_suggestions.at(index.row()); + } + + return QVariant(); +} + +QHash QDeclarativeSearchSuggestionModel::roleNames() const +{ + QHash roleNames = QDeclarativeSearchModelBase::roleNames(); + roleNames.insert(SearchSuggestionRole, "suggestion"); + return roleNames; +} + +/*! + \internal +*/ +void QDeclarativeSearchSuggestionModel::queryFinished() +{ + if (!m_reply) + return; + + QPlaceReply *reply = m_reply; + m_reply = 0; + + int initialCount = m_suggestions.count(); + beginResetModel(); + + clearData(true); + + QPlaceSearchSuggestionReply *suggestionReply = qobject_cast(reply); + m_suggestions = suggestionReply->suggestions(); + + if (initialCount != m_suggestions.count()) + emit suggestionsChanged(); + + endResetModel(); + + if (suggestionReply->error() != QPlaceReply::NoError) + setStatus(Error, suggestionReply->errorString()); + else + setStatus(Ready); + + + reply->deleteLater(); +} + +/*! + \internal +*/ +QPlaceReply *QDeclarativeSearchSuggestionModel::sendQuery(QPlaceManager *manager, + const QPlaceSearchRequest &request) +{ + return manager->searchSuggestions(request); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h new file mode 100644 index 0000000..5d75ee4 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesearchsuggestionmodel_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESEARCHSUGGESTIONMODEL_P_H +#define QDECLARATIVESEARCHSUGGESTIONMODEL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; +class QGeoServiceProvider; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSearchSuggestionModel : public QDeclarativeSearchModelBase +{ + Q_OBJECT + + Q_PROPERTY(QString searchTerm READ searchTerm WRITE setSearchTerm NOTIFY searchTermChanged) + Q_PROPERTY(QStringList suggestions READ suggestions NOTIFY suggestionsChanged) + +public: + explicit QDeclarativeSearchSuggestionModel(QObject *parent = 0); + ~QDeclarativeSearchSuggestionModel(); + + QString searchTerm() const; + void setSearchTerm(const QString &searchTerm); + + QStringList suggestions() const; + + void clearData(bool suppressSignal = false); + + // From QAbstractListModel + int rowCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + enum Roles { + SearchSuggestionRole = Qt::UserRole + }; + +protected Q_SLOTS: + virtual void queryFinished(); + +Q_SIGNALS: + void searchTermChanged(); + void suggestionsChanged(); + +protected: + QPlaceReply *sendQuery(QPlaceManager *manager, const QPlaceSearchRequest &request); + +private: + QStringList m_suggestions; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/declarativeplaces/qdeclarativesupplier.cpp b/src/location/declarativeplaces/qdeclarativesupplier.cpp new file mode 100644 index 0000000..b296dc6 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupplier.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesupplier_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Supplier + \instantiates QDeclarativeSupplier + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-data + \since QtLocation 5.5 + + \brief Holds data regarding the supplier of a place, a place's image, review, or editorial. + + Each instance represents a set of data about a supplier, which can include + supplier's name, url and icon. The supplier is typically a business or organization. + + Note: The Places API only supports suppliers as 'retrieve-only' objects. Submitting + suppliers to a provider is not a supported use case. + + \sa ImageModel, ReviewModel, EditorialModel + + \section1 Example + + The following example shows how to create and display a supplier in QML: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml Supplier +*/ + +QDeclarativeSupplier::QDeclarativeSupplier(QObject *parent) + : QObject(parent), m_icon(0) +{ +} + +QDeclarativeSupplier::QDeclarativeSupplier(const QPlaceSupplier &src, + QDeclarativeGeoServiceProvider *plugin, + QObject *parent) + : QObject(parent), + m_src(src), + m_icon(0) +{ + setSupplier(src, plugin); +} + +QDeclarativeSupplier::~QDeclarativeSupplier() +{ +} + +/*! + \internal +*/ +void QDeclarativeSupplier::componentComplete() +{ + // delayed instantiation of QObject based properties. + if (!m_icon) + m_icon = new QDeclarativePlaceIcon(this); +} + +/*! + \qmlproperty QPlaceSupplier Supplier::supplier + + For details on how to use this property to interface between C++ and QML see + "\l {Supplier - QPlaceSupplier} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeSupplier::setSupplier(const QPlaceSupplier &src, QDeclarativeGeoServiceProvider *plugin) +{ + QPlaceSupplier previous = m_src; + m_src = src; + + if (previous.name() != m_src.name()) + emit nameChanged(); + + if (previous.supplierId() != m_src.supplierId()) + emit supplierIdChanged(); + + if (previous.url() != m_src.url()) + emit urlChanged(); + + if (m_icon && m_icon->parent() == this) { + m_icon->setPlugin(plugin); + m_icon->setIcon(m_src.icon()); + } else if (!m_icon || m_icon->parent() != this) { + m_icon = new QDeclarativePlaceIcon(m_src.icon(), plugin, this); + emit iconChanged(); + } +} + +QPlaceSupplier QDeclarativeSupplier::supplier() +{ + m_src.setIcon(m_icon ? m_icon->icon() : QPlaceIcon()); + return m_src; +} + +/*! + \qmlproperty string Supplier::supplierId + + This property holds the identifier of the supplier. The identifier is unique + to the Plugin backend which provided the supplier and is generally + not suitable for displaying to the user. +*/ + +void QDeclarativeSupplier::setSupplierId(const QString &supplierId) +{ + if (m_src.supplierId() != supplierId) { + m_src.setSupplierId(supplierId); + emit supplierIdChanged(); + } +} + +QString QDeclarativeSupplier::supplierId() const +{ + return m_src.supplierId(); +} + +/*! + \qmlproperty string Supplier::name + + This property holds the name of the supplier which can be displayed + to the user. + + The name can potentially be localized. The language is dependent on the + entity that sets it, typically this is the \l Plugin. The \l {Plugin::locales} + property defines what language is used. +*/ + +void QDeclarativeSupplier::setName(const QString &name) +{ + if (m_src.name() != name) { + m_src.setName(name); + emit nameChanged(); + } +} + +QString QDeclarativeSupplier::name() const +{ + return m_src.name(); +} + +/*! + \qmlproperty url Supplier::url + + This property holds the URL of the supplier's website. +*/ + +void QDeclarativeSupplier::setUrl(const QUrl &url) +{ + if (m_src.url() != url) { + m_src.setUrl(url); + emit urlChanged(); + } +} + +QUrl QDeclarativeSupplier::url() const +{ + return m_src.url(); +} + +/*! + \qmlproperty PlaceIcon Supplier::icon + + This property holds the icon of the supplier. +*/ +QDeclarativePlaceIcon *QDeclarativeSupplier::icon() const +{ + return m_icon; +} + +void QDeclarativeSupplier::setIcon(QDeclarativePlaceIcon *icon) +{ + if (m_icon == icon) + return; + + if (m_icon && m_icon->parent() == this) + delete m_icon; + + m_icon = icon; + emit iconChanged(); +} + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesupplier_p.h b/src/location/declarativeplaces/qdeclarativesupplier_p.h new file mode 100644 index 0000000..b344d67 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupplier_p.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESUPPLIER_P_H +#define QDECLARATIVESUPPLIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSupplier : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(QPlaceSupplier supplier READ supplier WRITE setSupplier) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_PROPERTY(QString supplierId READ supplierId WRITE setSupplierId NOTIFY supplierIdChanged) + Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged) + Q_PROPERTY(QDeclarativePlaceIcon *icon READ icon WRITE setIcon NOTIFY iconChanged) + + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeSupplier(QObject *parent = 0); + explicit QDeclarativeSupplier(const QPlaceSupplier &src, QDeclarativeGeoServiceProvider *plugin, QObject *parent = 0); + ~QDeclarativeSupplier(); + + // From QQmlParserStatus + void classBegin() { } + void componentComplete(); + + QPlaceSupplier supplier(); + void setSupplier(const QPlaceSupplier &src, QDeclarativeGeoServiceProvider *plugin = 0); + + QString name() const; + void setName(const QString &data); + QString supplierId() const; + void setSupplierId(const QString &data); + QUrl url() const; + void setUrl(const QUrl &data); + + QDeclarativePlaceIcon *icon() const; + void setIcon(QDeclarativePlaceIcon *icon); + +Q_SIGNALS: + void nameChanged(); + void supplierIdChanged(); + void urlChanged(); + void iconChanged(); + +private: + QPlaceSupplier m_src; + QDeclarativePlaceIcon *m_icon; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSupplier) + +#endif // QDECLARATIVESUPPLIER_P_H diff --git a/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp new file mode 100644 index 0000000..b7c6e31 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel.cpp @@ -0,0 +1,689 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Aaron McCarthy +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativesupportedcategoriesmodel_p.h" +#include "qdeclarativeplaceicon_p.h" +#include "qgeoserviceprovider.h" +#include "error_messages_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype CategoryModel + \instantiates QDeclarativeSupportedCategoriesModel + \inqmlmodule QtLocation + \ingroup qml-QtLocation5-places + \ingroup qml-QtLocation5-places-models + \since QtLocation 5.5 + + \brief The CategoryModel type provides a model of the categories supported by a \l Plugin. + + The CategoryModel type provides a model of the categories that are available from the + current \l Plugin. The model supports both a flat list of categories and a hierarchical tree + representing category groupings. This can be controlled by the \l hierarchical property. + + The model supports the following roles: + + \table + \header + \li Role + \li Type + \li Description + \row + \li category + \li \l Category + \li Category object for the current item. + \row + \li parentCategory + \li \l Category + \li Parent category object for the current item. + If there is no parent, null is returned. + \endtable + + The following example displays a flat list of all available categories: + + \snippet declarative/places.qml QtQuick import + \snippet declarative/maps.qml QtLocation import + \codeline + \snippet declarative/places.qml CategoryView + + To access the hierarchical category model it is necessary to use a \l DelegateModel to access + the child items. +*/ + +/*! + \qmlproperty Plugin CategoryModel::plugin + + This property holds the provider \l Plugin used by this model. +*/ + +/*! + \qmlproperty bool CategoryModel::hierarchical + + This property holds whether the model provides a hierarchical tree of categories or a flat + list. The default is true. +*/ + +/*! + \qmlmethod string QtLocation::CategoryModel::data(ModelIndex index, int role) + \internal + + This method retrieves the model's data per \a index and \a role. +*/ + +/*! + \qmlmethod string QtLocation::CategoryModel::errorString() const + + This read-only property holds the textual presentation of the latest category model error. + If no error has occurred, an empty string is returned. + + An empty string may also be returned if an error occurred which has no associated + textual representation. +*/ + +/*! + \internal + \enum QDeclarativeSupportedCategoriesModel::Roles +*/ + +QDeclarativeSupportedCategoriesModel::QDeclarativeSupportedCategoriesModel(QObject *parent) +: QAbstractItemModel(parent), m_response(0), m_plugin(0), m_hierarchical(true), + m_complete(false), m_status(Null) +{ +} + +QDeclarativeSupportedCategoriesModel::~QDeclarativeSupportedCategoriesModel() +{ + qDeleteAll(m_categoriesTree); +} + +/*! + \internal +*/ +// From QQmlParserStatus +void QDeclarativeSupportedCategoriesModel::componentComplete() +{ + m_complete = true; +} + +/*! + \internal +*/ +int QDeclarativeSupportedCategoriesModel::rowCount(const QModelIndex &parent) const +{ + if (m_categoriesTree.keys().isEmpty()) + return 0; + + PlaceCategoryNode *node = static_cast(parent.internalPointer()); + if (!node) + node = m_categoriesTree.value(QString()); + else if (m_categoriesTree.keys(node).isEmpty()) + return 0; + + return node->childIds.count(); +} + +/*! + \internal +*/ +int QDeclarativeSupportedCategoriesModel::columnCount(const QModelIndex &parent) const +{ + Q_UNUSED(parent) + + return 1; +} + +/*! + \internal +*/ +QModelIndex QDeclarativeSupportedCategoriesModel::index(int row, int column, const QModelIndex &parent) const +{ + if (column != 0 || row < 0) + return QModelIndex(); + + PlaceCategoryNode *node = static_cast(parent.internalPointer()); + + if (!node) + node = m_categoriesTree.value(QString()); + else if (m_categoriesTree.keys(node).isEmpty()) //return root index if parent is non-existent + return QModelIndex(); + + if (row > node->childIds.count()) + return QModelIndex(); + + QString id = node->childIds.at(row); + Q_ASSERT(m_categoriesTree.contains(id)); + + return createIndex(row, 0, m_categoriesTree.value(id)); +} + +/*! + \internal +*/ +QModelIndex QDeclarativeSupportedCategoriesModel::parent(const QModelIndex &child) const +{ + PlaceCategoryNode *childNode = static_cast(child.internalPointer()); + if (m_categoriesTree.keys(childNode).isEmpty()) + return QModelIndex(); + + return index(childNode->parentId); +} + +/*! + \internal +*/ +QVariant QDeclarativeSupportedCategoriesModel::data(const QModelIndex &index, int role) const +{ + PlaceCategoryNode *node = static_cast(index.internalPointer()); + if (!node) + node = m_categoriesTree.value(QString()); + else if (m_categoriesTree.keys(node).isEmpty()) + return QVariant(); + + QDeclarativeCategory *category = node->declCategory.data(); + + switch (role) { + case Qt::DisplayRole: + return category->name(); + case CategoryRole: + return QVariant::fromValue(category); + case ParentCategoryRole: { + if (!m_categoriesTree.keys().contains(node->parentId)) + return QVariant(); + else + return QVariant::fromValue(m_categoriesTree.value(node->parentId)->declCategory.data()); + } + default: + return QVariant(); + } +} + +QHash QDeclarativeSupportedCategoriesModel::roleNames() const +{ + QHash roles = QAbstractItemModel::roleNames(); + roles.insert(CategoryRole, "category"); + roles.insert(ParentCategoryRole, "parentCategory"); + return roles; +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (m_plugin == plugin) + return; + + //disconnect the manager of the old plugin if we have one + if (m_plugin) { + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (serviceProvider) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + disconnect(placeManager, SIGNAL(categoryAdded(QPlaceCategory,QString)), + this, SLOT(addedCategory(QPlaceCategory,QString))); + disconnect(placeManager, SIGNAL(categoryUpdated(QPlaceCategory,QString)), + this, SLOT(updatedCategory(QPlaceCategory,QString))); + disconnect(placeManager, SIGNAL(categoryRemoved(QString,QString)), + this, SLOT(removedCategory(QString,QString))); + disconnect(placeManager, SIGNAL(dataChanged()), + this, SIGNAL(dataChanged())); + } + } + } + + m_plugin = plugin; + + // handle plugin name changes -> update categories + if (m_plugin) { + connect(m_plugin, SIGNAL(nameChanged(QString)), this, SLOT(connectNotificationSignals())); + connect(m_plugin, SIGNAL(nameChanged(QString)), this, SLOT(update())); + } + + connectNotificationSignals(); + + if (m_complete) + emit pluginChanged(); +} + +/*! + \internal +*/ +QDeclarativeGeoServiceProvider *QDeclarativeSupportedCategoriesModel::plugin() const +{ + return m_plugin; +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::setHierarchical(bool hierarchical) +{ + if (m_hierarchical == hierarchical) + return; + + m_hierarchical = hierarchical; + emit hierarchicalChanged(); + + updateLayout(); +} + +/*! + \internal +*/ +bool QDeclarativeSupportedCategoriesModel::hierarchical() const +{ + return m_hierarchical; +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::replyFinished() +{ + if (!m_response) + return; + + m_response->deleteLater(); + + if (m_response->error() == QPlaceReply::NoError) { + m_errorString.clear(); + + m_response = 0; + + updateLayout(); + setStatus(QDeclarativeSupportedCategoriesModel::Ready); + } else { + const QString errorString = m_response->errorString(); + + m_response = 0; + + setStatus(Error, errorString); + } +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::addedCategory(const QPlaceCategory &category, + const QString &parentId) +{ + if (m_response) + return; + + if (!m_categoriesTree.contains(parentId)) + return; + + if (category.categoryId().isEmpty()) + return; + + PlaceCategoryNode *parentNode = m_categoriesTree.value(parentId); + if (!parentNode) + return; + + int rowToBeAdded = rowToAddChild(parentNode, category); + QModelIndex parentIndex = index(parentId); + beginInsertRows(parentIndex, rowToBeAdded, rowToBeAdded); + PlaceCategoryNode *categoryNode = new PlaceCategoryNode; + categoryNode->parentId = parentId; + categoryNode->declCategory = QSharedPointer(new QDeclarativeCategory(category, m_plugin, this)); + + m_categoriesTree.insert(category.categoryId(), categoryNode); + parentNode->childIds.insert(rowToBeAdded,category.categoryId()); + endInsertRows(); + + //this is a workaround to deal with the fact that the hasModelChildren field of DelegateModel + //does not get updated when a child is added to a model + beginResetModel(); + endResetModel(); +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::updatedCategory(const QPlaceCategory &category, + const QString &parentId) +{ + if (m_response) + return; + + QString categoryId = category.categoryId(); + + if (!m_categoriesTree.contains(parentId)) + return; + + if (category.categoryId().isEmpty() || !m_categoriesTree.contains(categoryId)) + return; + + PlaceCategoryNode *newParentNode = m_categoriesTree.value(parentId); + if (!newParentNode) + return; + + PlaceCategoryNode *categoryNode = m_categoriesTree.value(categoryId); + if (!categoryNode) + return; + + categoryNode->declCategory->setCategory(category); + + if (categoryNode->parentId == parentId) { //reparenting to same parent + QModelIndex parentIndex = index(parentId); + int rowToBeAdded = rowToAddChild(newParentNode, category); + int oldRow = newParentNode->childIds.indexOf(categoryId); + + //check if we are changing the position of the category + if (qAbs(rowToBeAdded - newParentNode->childIds.indexOf(categoryId)) > 1) { + //if the position has changed we are moving rows + beginMoveRows(parentIndex, oldRow, oldRow, + parentIndex, rowToBeAdded); + + newParentNode->childIds.removeAll(categoryId); + newParentNode->childIds.insert(rowToBeAdded, categoryId); + endMoveRows(); + } else {// if the position has not changed we modifying an existing row + QModelIndex categoryIndex = index(categoryId); + emit dataChanged(categoryIndex, categoryIndex); + } + } else { //reparenting to different parents + QPlaceCategory oldCategory = categoryNode->declCategory->category(); + PlaceCategoryNode *oldParentNode = m_categoriesTree.value(categoryNode->parentId); + if (!oldParentNode) + return; + QModelIndex oldParentIndex = index(categoryNode->parentId); + QModelIndex newParentIndex = index(parentId); + + int rowToBeAdded = rowToAddChild(newParentNode, category); + beginMoveRows(oldParentIndex, oldParentNode->childIds.indexOf(categoryId), + oldParentNode->childIds.indexOf(categoryId), newParentIndex, rowToBeAdded); + oldParentNode->childIds.removeAll(oldCategory.categoryId()); + newParentNode->childIds.insert(rowToBeAdded, categoryId); + categoryNode->parentId = parentId; + endMoveRows(); + + //this is a workaround to deal with the fact that the hasModelChildren field of DelegateModel + //does not get updated when an index is updated to contain children + beginResetModel(); + endResetModel(); + } +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::removedCategory(const QString &categoryId, const QString &parentId) +{ + if (m_response) + return; + + if (!m_categoriesTree.contains(categoryId) || !m_categoriesTree.contains(parentId)) + return; + + QModelIndex parentIndex = index(parentId); + QModelIndex categoryIndex = index(categoryId); + + beginRemoveRows(parentIndex, categoryIndex.row(), categoryIndex.row()); + PlaceCategoryNode *parentNode = m_categoriesTree.value(parentId); + parentNode->childIds.removeAll(categoryId); + delete m_categoriesTree.take(categoryId); + endRemoveRows(); +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::connectNotificationSignals() +{ + if (!m_plugin) + return; + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider || serviceProvider->error() != QGeoServiceProvider::NoError) + return; + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) + return; + + // listen for any category notifications so that we can reupdate the categories + // model. + connect(placeManager, SIGNAL(categoryAdded(QPlaceCategory,QString)), + this, SLOT(addedCategory(QPlaceCategory,QString))); + connect(placeManager, SIGNAL(categoryUpdated(QPlaceCategory,QString)), + this, SLOT(updatedCategory(QPlaceCategory,QString))); + connect(placeManager, SIGNAL(categoryRemoved(QString,QString)), + this, SLOT(removedCategory(QString,QString))); + connect(placeManager, SIGNAL(dataChanged()), + this, SIGNAL(dataChanged())); +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::update() +{ + if (m_response) + return; + + setStatus(Loading); + + if (!m_plugin) { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROPERTY_NOT_SET)); + return; + } + + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (!serviceProvider || serviceProvider->error() != QGeoServiceProvider::NoError) { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_PROVIDER_ERROR) + .arg(m_plugin->name())); + return; + } + + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (!placeManager) { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, PLUGIN_ERROR) + .arg(m_plugin->name()).arg(serviceProvider->errorString())); + return; + } + + m_response = placeManager->initializeCategories(); + if (m_response) { + connect(m_response, SIGNAL(finished()), this, SLOT(replyFinished())); + } else { + updateLayout(); + setStatus(Error, QCoreApplication::translate(CONTEXT_NAME, + CATEGORIES_NOT_INITIALIZED)); + } +} + +/*! + \internal +*/ +void QDeclarativeSupportedCategoriesModel::updateLayout() +{ + beginResetModel(); + qDeleteAll(m_categoriesTree); + m_categoriesTree.clear(); + + if (m_plugin) { + QGeoServiceProvider *serviceProvider = m_plugin->sharedGeoServiceProvider(); + if (serviceProvider && serviceProvider->error() == QGeoServiceProvider::NoError) { + QPlaceManager *placeManager = serviceProvider->placeManager(); + if (placeManager) { + PlaceCategoryNode *node = new PlaceCategoryNode; + node->childIds = populateCategories(placeManager, QPlaceCategory()); + m_categoriesTree.insert(QString(), node); + node->declCategory = QSharedPointer + (new QDeclarativeCategory(QPlaceCategory(), m_plugin, this)); + } + } + } + + endResetModel(); +} + +QString QDeclarativeSupportedCategoriesModel::errorString() const +{ + return m_errorString; +} + +/*! + \qmlproperty enumeration CategoryModel::status + + This property holds the status of the model. It can be one of: + + \table + \row + \li CategoryModel.Null + \li No category fetch query has been executed. The model is empty. + \row + \li CategoryModel.Ready + \li No error occurred during the last operation, further operations may be performed on + the model. + \row + \li CategoryModel.Loading + \li The model is being updated, no other operations may be performed until complete. + \row + \li CategoryModel.Error + \li An error occurred during the last operation, further operations can still be + performed on the model. + \endtable +*/ +void QDeclarativeSupportedCategoriesModel::setStatus(Status status, const QString &errorString) +{ + Status originalStatus = m_status; + m_status = status; + m_errorString = errorString; + + if (originalStatus != m_status) + emit statusChanged(); +} + +QDeclarativeSupportedCategoriesModel::Status QDeclarativeSupportedCategoriesModel::status() const +{ + return m_status; +} + +/*! + \internal +*/ +QStringList QDeclarativeSupportedCategoriesModel::populateCategories(QPlaceManager *manager, const QPlaceCategory &parent) +{ + Q_ASSERT(manager); + + QStringList childIds; + PlaceCategoryNode *node; + + QMap sortedCategories; + foreach ( const QPlaceCategory &category, manager->childCategories(parent.categoryId())) + sortedCategories.insert(category.name(), category); + + QMapIterator iter(sortedCategories); + while (iter.hasNext()) { + iter.next(); + node = new PlaceCategoryNode; + node->parentId = parent.categoryId(); + node->declCategory = QSharedPointer(new QDeclarativeCategory(iter.value(), m_plugin ,this)); + + if (m_hierarchical) + node->childIds = populateCategories(manager, iter.value()); + + m_categoriesTree.insert(node->declCategory->categoryId(), node); + childIds.append(iter.value().categoryId()); + + if (!m_hierarchical) { + childIds.append(populateCategories(manager,node->declCategory->category())); + } + } + return childIds; +} + +/*! + \internal +*/ +QModelIndex QDeclarativeSupportedCategoriesModel::index(const QString &categoryId) const +{ + if (categoryId.isEmpty()) + return QModelIndex(); + + if (!m_categoriesTree.contains(categoryId)) + return QModelIndex(); + + PlaceCategoryNode *categoryNode = m_categoriesTree.value(categoryId); + if (!categoryNode) + return QModelIndex(); + + QString parentCategoryId = categoryNode->parentId; + + PlaceCategoryNode *parentNode = m_categoriesTree.value(parentCategoryId); + + return createIndex(parentNode->childIds.indexOf(categoryId), 0, categoryNode); +} + +/*! + \internal +*/ +int QDeclarativeSupportedCategoriesModel::rowToAddChild(PlaceCategoryNode *node, const QPlaceCategory &category) +{ + Q_ASSERT(node); + for (int i = 0; i < node->childIds.count(); ++i) { + if (category.name() < m_categoriesTree.value(node->childIds.at(i))->declCategory->name()) + return i; + } + return node->childIds.count(); +} + +/*! + \qmlsignal CategoryModel::dataChanged() + + This signal is emitted when significant changes have been made to the underlying datastore. + + Applications should act on this signal at their own discretion. The data + provided by the model could be out of date and so the model should be reupdated + sometime, however an immediate reupdate may be disconcerting to users if the categories + change without any action on their part. + + The corresponding handler is \c onDataChanged. +*/ + +QT_END_NAMESPACE diff --git a/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h new file mode 100644 index 0000000..9f17ab4 --- /dev/null +++ b/src/location/declarativeplaces/qdeclarativesupportedcategoriesmodel_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVESUPPORTEDCATEGORIESMODEL_H +#define QDECLARATIVESUPPORTEDCATEGORIESMODEL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoServiceProvider; +class QPlaceManager; +class QPlaceReply; + +class PlaceCategoryNode +{ +public: + QString parentId; + QStringList childIds; + QSharedPointer declCategory; +}; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeSupportedCategoriesModel : public QAbstractItemModel, public QQmlParserStatus +{ + Q_OBJECT + + Q_ENUMS(Status) + + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(bool hierarchical READ hierarchical WRITE setHierarchical NOTIFY hierarchicalChanged) + Q_PROPERTY(Status status READ status NOTIFY statusChanged) + + Q_INTERFACES(QQmlParserStatus) + Q_ENUMS(Roles) //The Roles enum is for internal usage only. + +public: + explicit QDeclarativeSupportedCategoriesModel(QObject *parent = 0); + virtual ~QDeclarativeSupportedCategoriesModel(); + + // From QQmlParserStatus + virtual void classBegin() {} + virtual void componentComplete(); + + // From QAbstractItemModel + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &child) const; + + Q_INVOKABLE QVariant data(const QModelIndex &index, int role) const; + QHash roleNames() const; + + enum Roles { + CategoryRole = Qt::UserRole, + ParentCategoryRole + }; //for internal usage only + + enum Status {Null, Ready, Loading, Error}; + + void setPlugin(QDeclarativeGeoServiceProvider *plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setHierarchical(bool hierarchical); + bool hierarchical() const; + + Q_INVOKABLE QString errorString() const; + + Status status() const; + void setStatus(Status status, const QString &errorString = QString()); + + using QAbstractItemModel::dataChanged; +Q_SIGNALS: + void pluginChanged(); + void hierarchicalChanged(); + void statusChanged(); + void dataChanged(); + +public Q_SLOTS: + void update(); + +private Q_SLOTS: + void replyFinished(); + void addedCategory(const QPlaceCategory &category, const QString &parentId); + void updatedCategory(const QPlaceCategory &category, const QString &parentId); + void removedCategory(const QString &categoryId, const QString &parentId); + void connectNotificationSignals(); + +private: + QStringList populateCategories(QPlaceManager *, const QPlaceCategory &parent); + QModelIndex index(const QString &categoryId) const; + int rowToAddChild(PlaceCategoryNode *, const QPlaceCategory &category); + void updateLayout(); + + QPlaceReply *m_response; + + QDeclarativeGeoServiceProvider *m_plugin; + bool m_hierarchical; + bool m_complete; + Status m_status; + QString m_errorString; + + QHash m_categoriesTree; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeSupportedCategoriesModel) + +#endif // QDECLARATIVESUPPORTEDCATEGORIESMODEL_H diff --git a/src/location/doc/images/api-mapcircle.png b/src/location/doc/images/api-mapcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..ab31eefc8e972e16a48c50c1c23e05229716d46f GIT binary patch literal 11336 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
  • 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 AUjP6A literal 0 HcmV?d00001 diff --git a/src/location/doc/images/api-mappolygon.png b/src/location/doc/images/api-mappolygon.png new file mode 100644 index 0000000000000000000000000000000000000000..46b5fc17fde18f02c3f61468c5f8d385ff5c0b07 GIT binary patch literal 42391 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 O0000-w1^_tXw|fo*|Mup$v8%NYRB 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

    D1?NBIm>hvj5l=}MzL!r>A7f$-TzL}Y6 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 literal 0 HcmV?d00001 diff --git a/src/location/doc/images/api-mapquickitem-anchor.png b/src/location/doc/images/api-mapquickitem-anchor.png new file mode 100644 index 0000000000000000000000000000000000000000..40581db50c353e114b21c74af49e4e97e4e32d87 GIT binary patch literal 61695 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 literal 0 HcmV?d00001 diff --git a/src/location/doc/images/api-mapquickitem.png b/src/location/doc/images/api-mapquickitem.png new file mode 100644 index 0000000000000000000000000000000000000000..3abde6b62cacacfeaa6f0a117272c86b00d6b159 GIT binary patch literal 23147 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 Wk%2|+Otne?0000?F1dkr=YVrW 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 KRklVN8uUNef7lTK literal 0 HcmV?d00001 diff --git a/src/location/doc/images/mapsdemo-verybasic.png b/src/location/doc/images/mapsdemo-verybasic.png new file mode 100644 index 0000000000000000000000000000000000000000..9d92a74c955769a8e39e93668be046e47b0e2c48 GIT binary patch literal 63947 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 T@#BjT1n?&<1(K{1*9ZR(sUrwt literal 0 HcmV?d00001 diff --git a/src/location/doc/qtlocation.qdocconf b/src/location/doc/qtlocation.qdocconf new file mode 100644 index 0000000..0e73539 --- /dev/null +++ b/src/location/doc/qtlocation.qdocconf @@ -0,0 +1,56 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtLocation +description = Qt Location Reference Documentation +version = $QT_VERSION + + + +qhp.projects = QtLocation + +qhp.QtLocation.file = qtlocation.qhp +qhp.QtLocation.namespace = org.qt-project.qtlocation.$QT_VERSION_TAG +qhp.QtLocation.virtualFolder = qtlocation +qhp.QtLocation.indexTitle = Qt Location +qhp.QtLocation.indexRoot = + +qhp.QtLocation.filterAttributes = qtlocation $QT_VERSION qtrefdoc +qhp.QtLocation.customFilters.Qt.name = QtLocation $QT_VERSION +qhp.QtLocation.customFilters.Qt.filterAttributes = qtlocation $QT_VERSION +qhp.QtLocation.subprojects = classes qml examples +qhp.QtLocation.subprojects.classes.title = C++ Classes +qhp.QtLocation.subprojects.classes.indexTitle = Qt Location C++ Classes +qhp.QtLocation.subprojects.classes.selectors = class fake:headerfile +qhp.QtLocation.subprojects.classes.sortPages = true +qhp.QtLocation.subprojects.qml.title = QML Types +qhp.QtLocation.subprojects.qml.indexTitle = Qt Location QML Types +qhp.QtLocation.subprojects.qml.selectors = qmlclass +qhp.QtLocation.subprojects.qml.sortPages = true +qhp.QtLocation.subprojects.examples.title = Qt Location Examples +qhp.QtLocation.subprojects.examples.indexTitle = Qt Location Examples +qhp.QtLocation.subprojects.examples.selectors = fake:example + +tagfile = ../../../doc/qtlocation/qtlocation.tags + +depends += qtcore qtdoc qtgui qtquick qtqml qtnetwork qtpositioning qtquickcontrols qtlinguist + +headerdirs += .. \ + ../../imports/location + +sourcedirs += .. \ + ../../imports/location \ + ../../plugins/geoservices/nokia + +examplesinstallpath = location + +manifestmeta.highlighted.names = "QtLocation/Map Viewer (QML)" + +exampledirs += ../../../examples/location \ + snippets/ + + +imagedirs += images + +navigation.landingpage = "Qt Location" +navigation.cppclassespage = "Qt Location C++ Classes" +navigation.qmltypespage = "Qt Location QML Types" diff --git a/src/location/doc/snippets/cpp/cpp.pro b/src/location/doc/snippets/cpp/cpp.pro new file mode 100644 index 0000000..b961b98 --- /dev/null +++ b/src/location/doc/snippets/cpp/cpp.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = cppsnippet +QT = core location + +SOURCES += \ + main.cpp \ + cppqml.cpp + diff --git a/src/location/doc/snippets/cpp/cppqml.cpp b/src/location/doc/snippets/cpp/cppqml.cpp new file mode 100644 index 0000000..43fcdf9 --- /dev/null +++ b/src/location/doc/snippets/cpp/cppqml.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void cppQmlInterface(QObject *qmlObject) +{ + //! [Category get] + QPlaceCategory category = qmlObject->property("category").value(); + //! [Category get] + + //! [Category set] + qmlObject->setProperty("category", QVariant::fromValue(category)); + //! [Category set] + + //! [ContactDetail get] + QPlaceContactDetail contactDetail = qmlObject->property("contactDetail").value(); + //! [ContactDetail get] + + //! [ContactDetail set] + qmlObject->setProperty("contactDetail", QVariant::fromValue(contactDetail)); + //! [ContactDetail set] + + //! [Place get] + QPlace place = qmlObject->property("place").value(); + //! [Place get] + + //! [Place set] + qmlObject->setProperty("place", QVariant::fromValue(place)); + //! [Place set] + + //! [PlaceAttribute get] + QPlaceAttribute placeAttribute = qmlObject->property("attribute").value(); + //! [PlaceAttribute get] + + //! [PlaceAttribute set] + qmlObject->setProperty("attribute", QVariant::fromValue(placeAttribute)); + //! [PlaceAttribute set] + + //! [Icon get] + QPlaceIcon placeIcon = qmlObject->property("icon").value(); + //! [Icon get] + + //! [Icon set] + qmlObject->setProperty("icon", QVariant::fromValue(placeIcon)); + //! [Icon set] + + //! [User get] + QPlaceUser placeUser = qmlObject->property("user").value(); + //! [User get] + + //! [User set] + qmlObject->setProperty("user", QVariant::fromValue(placeUser)); + //! [User set] + + //! [Ratings get] + QPlaceRatings placeRatings = qmlObject->property("ratings").value(); + //! [Ratings get] + + //! [Ratings set] + qmlObject->setProperty("ratings", QVariant::fromValue(placeRatings)); + //! [Ratings set] + + //! [Supplier get] + QPlaceSupplier placeSupplier = qmlObject->property("supplier").value(); + //! [Supplier get] + + //! [Supplier set] + qmlObject->setProperty("supplier", QVariant::fromValue(placeSupplier)); + //! [Supplier set] +} + diff --git a/src/location/doc/snippets/cpp/main.cpp b/src/location/doc/snippets/cpp/main.cpp new file mode 100644 index 0000000..4904db8 --- /dev/null +++ b/src/location/doc/snippets/cpp/main.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main(int /*argc*/, char ** /*argv*/) +{ + return 0; +} + diff --git a/src/location/doc/snippets/declarative/content/Cell.qml b/src/location/doc/snippets/declarative/content/Cell.qml new file mode 100644 index 0000000..7d7e07b --- /dev/null +++ b/src/location/doc/snippets/declarative/content/Cell.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import Qt 4.7 + +Item { + id: container + property string action: "no-op" + signal clicked(string action) + + width: 100; height:30 + + Rectangle { + id: rectangle + border.color: "white" + border.width: 2 + anchors.fill: parent // Fill the whole container + } + Text { + id: text + text: container.action + color: "black" + anchors.left: parent.left + anchors.leftMargin: 5 + anchors.verticalCenter: parent.verticalCenter + } + MouseArea { + anchors.fill: parent // Whole container is clickable + onClicked: container.clicked(container.action) + } +} diff --git a/src/location/doc/snippets/declarative/declarative-location.qml b/src/location/doc/snippets/declarative/declarative-location.qml new file mode 100644 index 0000000..2cdb8fe --- /dev/null +++ b/src/location/doc/snippets/declarative/declarative-location.qml @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//![0] +import QtQuick 2.0 +import QtPositioning 5.2 + +Rectangle { + id: page + width: 350 + height: 350 + PositionSource { + id: positionSource + updateInterval: 1000 + active: true + // nmeaSource: "nmealog.txt" + } + Column { + Text {text: "<==== PositionSource ====>"} + Text {text: "preferredPositioningMethods: " + printableMethod(positionSource.preferredPositioningMethods)} + Text {text: "supportedPositioningMethods: " + printableMethod(positionSource.supportedPositioningMethods)} + Text {text: "nmeaSource: " + positionSource.nmeaSource} + Text {text: "updateInterval: " + positionSource.updateInterval} + Text {text: "active: " + positionSource.active} + Text {text: "<==== Position ====>"} + Text {text: "latitude: " + positionSource.position.coordinate.latitude} + Text {text: "longitude: " + positionSource.position.coordinate.longitude} + Text {text: "altitude: " + positionSource.position.coordinate.altitude} + Text {text: "speed: " + positionSource.position.speed} + Text {text: "timestamp: " + positionSource.position.timestamp} + Text {text: "altitudeValid: " + positionSource.position.altitudeValid} + Text {text: "longitudeValid: " + positionSource.position.longitudeValid} + Text {text: "latitudeValid: " + positionSource.position.latitudeValid} + Text {text: "speedValid: " + positionSource.position.speedValid} + } + function printableMethod(method) { + if (method == PositionSource.SatellitePositioningMethods) + return "Satellite"; + else if (method == PositionSource.NoPositioningMethods) + return "Not available" + else if (method == PositionSource.NonSatellitePositioningMethods) + return "Non-satellite" + else if (method == PositionSource.AllPositioningMethods) + return "All/multiple" + return "source error"; + } + } +//![0] diff --git a/src/location/doc/snippets/declarative/declarative.pro b/src/location/doc/snippets/declarative/declarative.pro new file mode 100644 index 0000000..226010c --- /dev/null +++ b/src/location/doc/snippets/declarative/declarative.pro @@ -0,0 +1,18 @@ +TEMPLATE = aux + +content.files = \ + declarative-location.qml \ + maps.qml \ + places.qml \ + plugin.qml \ + routing.qml \ + places_loader.qml + +OTHER_FILES = \ + $${content.files} + +# Put content in INSTALLS so that content.files become part of the project tree in Qt Creator, +# but scoped with false as we don't actually want to install them. They are documentation +# snippets. +false:INSTALLS += content + diff --git a/src/location/doc/snippets/declarative/maps.qml b/src/location/doc/snippets/declarative/maps.qml new file mode 100644 index 0000000..e5770c9 --- /dev/null +++ b/src/location/doc/snippets/declarative/maps.qml @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [QtQuick import] +import QtQuick 2.0 +//! [QtQuick import] +//! [QtLocation import] +import QtPositioning 5.5 +import QtLocation 5.6 +//! [QtLocation import] + +Item { + id: page + + Plugin { + id: myPlugin + } + + //! [MapRoute] + Map { + RouteModel { + id: routeModel + } + + MapItemView { + model: routeModel + delegate: routeDelegate + } + + Component { + id: routeDelegate + + MapRoute { + route: routeData + line.color: "blue" + line.width: 5 + smooth: true + opacity: 0.8 + } + } + } + //! [MapRoute] + + //! [Map addMapItem MapCircle at current position] + PositionSource { + id: positionSource + } + + Map { + id: map + property MapCircle circle + + Component.onCompleted: { + circle = Qt.createQmlObject('import QtLocation 5.3; MapCircle {}', page) + circle.center = positionSource.position.coordinate + circle.radius = 5000.0 + circle.color = 'green' + circle.border.width = 3 + map.addMapItem(circle) + } + } + //! [Map addMapItem MapCircle at current position] +} diff --git a/src/location/doc/snippets/declarative/marker.png b/src/location/doc/snippets/declarative/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..fd727d9c5cb0fedb49863726eb2d18df993a9f1e GIT binary patch literal 1855 zcmV-F2f+A=P)bP~(>eyE6Xlw0^)oGn}?BaCVe>$~jMXaDwWebE56w(M%x4NMa z1wp~EZ+5H;f}n^)kj1J9K`g1bkg$pl#nbOR6NgPDNYpp;&AIO;dGGx0IrrRq)1${T zuIlO%kzRk@;#uGE^YiHw9UUgRd^xLEkJt6e&p$iZ$H&`Rr8+WBqbV+X_^^Q``YT>~{YHxc(~G*>AqDk2Eq?f=?1e@-1CL?9~az|b8#wvB6OsCk%s{U7oUKS8s+ zB50s}Fj?3=)*y!vnk_j@8;+c$yizUI*H_nV*%CNu?{7a^ymFA#llw(%FJ8F4{3qoYvB zt?TOaWoZSa^p?w^C!^1D4L?n(xuxwCnR$hl?n))AUz9X){UORI)KG1$E>bS{9aCJK z{qm#Z?CdnGrlu-6FgBemmxZ)~V12VSNr=YLb5}Y9I+R{SGrgmP-)@c1B)$G_nOweM zkWN=@`BFi$veL4$E|=AE3blo3DRy*@PYi8}JKr8iT}nS}OQb%_gGpUdL1)gKq=bZs z;V%{BZk3*f+wFcPL|eTtIZDdY`R#xhX1aSgJ!rg7{{9M1G4qyZtQA(izOsHAO-Zh2 z=y8)EjOu3>LVmGX?IJVXGm}WTII? zP;P0qIP8T%STkj+Rv=syzVeTw>gs!l%I3*t1!-z}NJCcc=@t-HK>XusraFI>24bHWqEwZ6M9f&GfvIYl;>qVo4mHMiZP53-`m<9u)RvadB~$3WcI) zOL3pAtu6PzI9O9t6Im_qK9IH=z2-59lao__u~_^>GMTKuo10rdFE6jYt5&V*jn8`- zfw~03VdwCh3*_UCN2#%~jzK26yStBv^AlLEuCCl31C2y`$olo`2Y7gR^fpAxbQo+) zgb8Ub5Zi5WR8@7G;P66#d=C2sTWBHL3!skx@-Bm|Teof~M+xcH%Ve1R{9Fo%PB$A! zP--^i&F-z>HuRPPI;_nOc3?CNMB?e5Jlt*p7EM z#DEncRMXJN(TH+(b`FBmL1+sW+Jp8VumP~15CbiAadDZ#sb)yC&PN45?#=UL>Gt?; z2NBLFwK{^cDube1i0`AIF!-(3N@^qSi)SbRghHF4l>qq!AY-}8@x(W&iuw8Zh|Zo( zqvDD?G{))0VfcW>19R+HLK7TK?nrQ&VG=kpXe(jmu*;yis9>WFlit$gax9ZcMSR39 zEW1S`91e6DVysgrmENi%l`2XL=TbX>Fh(TakYXH-EzmmXYeeH6*kLBm5#v=+QD7mF zcvut`<_d=*{&T@p2p)h!jkYN%DehJW5G?^>p(AiO1UAr6!MJkG;`zagrAbw*?Nwf$ z*GDRqjJ$TOkk0*mL#R5Pg&4Y8Nd=mJNvRCZ@4$IZp?EkN!vN4hXeYE0S`95`2(0*H z0Ah`{s7ujkd0n`0LWCThq0to6sY};|gVXaMIC|MvZqWJcjOvaiGgl`#cL+8Z+JUwo zM*;06TyinN-}ChJ9NiMaq-d;O?a;GrA3sDUlk^456s@+LQgg06bB$rp^ny~#yOdJ{ zCsJCZ4wu$&b{_pZA%C=`kQ3TJL5rXd05J&wBY6MSC5We8L}Y=7^}+}Z#Dm|~YRf8P zG7CD~Av56d+-sy(=iD_o&uhbB=m4}E+RV=5IlsdG%<}*ae+oMrnv6wqB+=;&yLmyUu1^L4sA6n;9d)!#%6mT<0!?%b*1m74*=@O@sfx8mMcE|+`3 zu7JKbq|b-2(|F^E_8Sbq@8BqKndJfOveU`Q(bgUH^T_ZEIA@^=c=>|rs!G}taZ31C zk%2=0%pskwk|C6at%d^D1J*$*m2&PcLVGSOm(vL-r*9$xgE@ul?d|(=)LWiKUTE}v zeWeypiweJlYdOE%#a%p!cdeRRI&#|-P4b;7q{V^=%I?2OaeHIw1sypr;Y=8_vXIT8+#E%Yt)89w8@ tpUP(p%&%b*iNuCe$9QNmuF~dk{(rgB@id" + place.extendedAttributes[modelData].label + ": " + + place.extendedAttributes[modelData].text + } + } + //! [ExtendedAttributes] + + //! [ExtendedAttributes read] + function printExtendedAttributes(extendedAttributes) { + var keys = extendedAttributes.keys(); + for (var i = 0; i < keys.length; ++i) { + var key = keys[i]; + if (extendedAttributes[key].label !== "") + console.log(extendedAttributes[key].label + ": " + extendedAttributes[key].text); + } + } + //! [ExtendedAttributes read] + + function writeExtendedAttributes() { + //! [ExtendedAttributes write] + //assign a new attribute to a place + var smokingAttrib = Qt.createQmlObject('import QtLocation 5.3; PlaceAttribute {}', place); + smokingAttrib.label = "Smoking Allowed" + smokingAttrib.text = "No" + place.extendedAttributes.smoking = smokingAttrib; + + //modify an existing attribute + place.extendedAttributes.smoking.text = "Yes" + //! [ExtendedAttributes write] + } + + Icon { + id: icon + } + //! [Icon] + Image { + source: icon.url(Qt.size(64, 64)) + } + //! [Icon] + + Image { + //! [Icon default] + source: icon.url() + //! [Icon default] + } + + //! [SearchSuggestionModel] + PlaceSearchSuggestionModel { + id: suggestionModel + + plugin: myPlugin + + // Brisbane + searchArea: QtPositioning.circle(QtPositioning.coordinate(-27.46778, 153.02778)) + + onSearchTermChanged: update() + } + + ListView { + model: suggestionModel + delegate: Text { text: suggestion } + } + //! [SearchSuggestionModel] + + //! [EditorialModel] + EditorialModel { + id: editorialModel + batchSize: 3 + place: place + } + + ListView { + model: editorialModel + delegate: Item { + anchors.fill: parent + + Column { + width: parent.width + clip: true + + Text { + text: title + width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: 24 + } + + Text { + text: text + width: parent.width + wrapMode: Text.WordWrap + font.pixelSize: 20 + } + + Row { + Image { + width: 16 + height: 16 + + source: supplier.icon.url(Qt.size(width, height), Icon.List) + } + + Text { + text: "Provided by " + supplier.name + font.pixelSize: 16 + } + } + + Text { + text: "Contributed by " + user.name + font.pixelSize: 16 + } + + Text { + text: attribution + font.pixelSize: 8 + } + } + } + } + //! [EditorialModel] + + //! [ImageModel] + ImageModel { + id: imageModel + batchSize: 3 + place: place + } + + ListView { + anchors.top: parent.top + width: parent.width + spacing: 10 + + model: imageModel + orientation: ListView.Horizontal + snapMode: ListView.SnapOneItem + + delegate: Item { + width: listView.width + height: listView.height + + Image { + anchors.fill: parent + source: url + fillMode: Image.PreserveAspectFit + } + + Text { + text: supplier.name + "\n" + supplier.url + width: parent.width + anchors.bottom: parent.bottom + } + } + } + //! [ImageModel] + + //! [Supplier] + Supplier { + id: placeSupplier + name: "Example" + url: "http://www.example.com/" + } + + Text { + text: "This place is was provided by " + placeSupplier.name + "\n" + placeSupplier.url + } + //! [Supplier] + + //! [Ratings] + Text { + text: "This place is rated " + place.ratings.average + " out of " + place.ratings.maximum + " stars." + } + //! [Ratings] + + //! [ContactDetails read] + function printContactDetails(contactDetails) { + var keys = contactDetails.keys(); + for (var i = 0; i < keys.length; ++i) { + var contactList = contactDetails[keys[i]]; + for (var j = 0; j < contactList.length; ++j) { + console.log(contactList[j].label + ": " + contactList[j].value); + } + } + } + //! [ContactDetails read] + + //! [ContactDetails write single] + function writeSingle() { + var phoneNumber = Qt.createQmlObject('import QtLocation 5.3; ContactDetail {}', place); + phoneNumber.label = "Phone"; + phoneNumber.value = "555-5555" + place.contactDetails.phone = phoneNumber; + } + //! [ContactDetails write single] + + //! [ContactDetails write multiple] + function writeMultiple() { + var bob = Qt.createQmlObject('import QtLocation 5.3; ContactDetail {}', place); + bob.label = "Bob"; + bob.value = "555-5555" + + var alice = Qt.createQmlObject('import QtLocation 5.3; ContactDetail {}', place); + alice.label = "Alice"; + alice.value = "555-8745" + + var numbers = new Array(); + numbers.push(bob); + numbers.push(alice); + + place.contactDetails.phone = numbers; + } + //! [ContactDetails write multiple] + + //! [ContactDetails phoneList] + ListView { + model: place.contactDetails.phone; + delegate: Text { text: modelData.label + ": " + modelData.value } + } + //! [ContactDetails phoneList] + + //! [Place savePlace def] + Place { + id: myPlace + plugin: myPlugin + + name: "Brisbane Technology Park" + location: Location { + address: Address { + street: "53 Brandl Street" + city: "Eight Mile Plains" + postalCode: "4113" + country: "Australia" + } + coordinate { + latitude: -27.579646 + longitude: 153.100308 + } + } + + visibility: Place.PrivateVisibility + } + //! [Place savePlace def] + + function fetchDetails() { + //! [Place fetchDetails] + if (!place.detailsFetched) + place.getDetails(); + //! [Place fetchDetails] + } + + function savePlace() { + //! [Place savePlace] + myPlace.save(); + //! [Place savePlace] + } + + function createAndSavePlace() { + //! [Place createAndSavePlace] + //creating and saving a place + var place = Qt.createQmlObject('import QtLocation 5.3; Place { }', parent); + place.plugin = myPlugin; + place.name = "New York"; + place.location.coordinate.latitude = 40.7 + place.location.coordinate.longitude = -74.0 + place.save(); + //! [Place createAndSavePlace] + } + + function removePlace() { + //! [Place removePlace] + //removing a place + place.remove(); + //! [Place removePlace] + } + + function saveToNewPlugin() { + //! [Place save to different plugin] + var place = Qt.createQmlObject('import QtLocation 5.3; Place { }', parent); + place.plugin = destinationPlugin; + place.copyFrom(originalPlace); + place.save(); + //! [Place save to different plugin] + } + + function getPlaceForId() { + //! [Place placeId] + place.plugin = myPlugin; + place.placeId = "known-place-id"; + place.getDetails(); + //! [Place placeId] + } + + function primaryContacts() { + //! [Place primaryPhone] + var primaryPhone; + if (place.contactDetails["phone"].length > 0) + primaryPhone = place.contactDetails["phone"][0].value; + //! [Place primaryPhone] + //! [Place primaryFax] + var primaryFax; + if (place.contactDetails["fax"].length > 0) + primaryFax = place.contactDetails["fax"][0].value; + //! [Place primaryFax] + //! [Place primaryEmail] + var primaryEmail; + if (place.contactDetails["email"].length > 0) + primaryEmail = place.contactDetails["email"][0].value; + //! [Place primaryEmail] + //! [Place primaryWebsite] + var primaryWebsite; + if (place.contactDetails["website"].length > 0) + primaryWebsite = place.contactDetails["website"][0].value; + //! [Place primaryWebsite] + } + + //! [Place favorite] + Text { text: place.favorite ? place.favorite.name : place.name } + //! [Place favorite] + + function saveFavorite() { + var place = Qt.createQmlObject('import QtLocation 5.3; Place { }', parent); + var destinationPlugin + //! [Place saveFavorite] + place.initializeFavorite(destinationPlugin); + //if necessary customizations to the favorite can be made here. + //... + place.favorite.save(); + //! [Place saveFavorite] + } + + function removeFavorite() { + var place; + //! [Place removeFavorite 1] + place.favorite.remove(); + //! [Place removeFavorite 1] + + //! [Place removeFavorite 2] + //check successful removal of the favorite by monitoring its status. + //once that is done we can assign null to the favorite + place.favorite = null; + //! [Place removeFavorite 2] + } + + function connectStatusChangedHandler() { + //! [Place checkStatus] + place.statusChanged.connect(statusChangedHandler); + //! [Place checkStatus] + } + + //! [Place checkStatus handler] + function statusChangedHandler() { + if (statusChangedHandler.prevStatus === Place.Saving) { + switch (place.status) { + case Place.Ready: + console.log('Save successful'); + break; + case Place.Error: + console.log('Save failed'); + break; + default: + break; + } + } + statusChangedHandler.prevStatus = place.status; + } + //! [Place checkStatus handler] +} diff --git a/src/location/doc/snippets/declarative/places_loader.qml b/src/location/doc/snippets/declarative/places_loader.qml new file mode 100644 index 0000000..8422ad5 --- /dev/null +++ b/src/location/doc/snippets/declarative/places_loader.qml @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtPositioning 5.2 +import QtLocation 5.3 + +Rectangle { + width: 360 + height: 360 + property variant startCoordinate: QtPositioning.coordinate(-27.46778, 153.02778) + + Plugin { + id: myPlugin + name: "osm" + //specify plugin parameters if necessary + //PluginParameter {...} + //PluginParameter {...} + //... + } + + PlaceSearchModel { + id: searchModel + + plugin: myPlugin + + searchTerm: "pizza" + searchArea: QtPositioning.circle(startCoordinate) + + Component.onCompleted: update() + } + + //! [Handle Result Types] + Component { + id: resultDelegate + Loader { + Component { + id: placeResult + + Column { + Text { text: title } + Text { text: place.location.address.text } + } + } + + Component { + id: otherResult + Text { text: title } + } + + sourceComponent: type == PlaceSearchModel.PlaceResult ? placeResult : + otherResult + } + } + //! [Handle Result Types] + + ListView { + anchors.fill: parent + model: searchModel + delegate: resultDelegate + spacing: 10 + } + + Connections { + target: searchModel + onStatusChanged: { + if (searchModel.status == PlaceSearchModel.Error) + console.log(searchModel.errorString()); + } + } +} diff --git a/src/location/doc/snippets/declarative/plugin.qml b/src/location/doc/snippets/declarative/plugin.qml new file mode 100644 index 0000000..8f4daca --- /dev/null +++ b/src/location/doc/snippets/declarative/plugin.qml @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +//! [Plugin import] +import QtLocation 5.3 +//! [Plugin import] + +Item { + //! [Plugin locale] + //single locale + Plugin { + locales: "en_US" + } + + //multiple locales + Plugin { + locales: ["fr_FR","en_US"] + } + //! [Plugin locale] +} diff --git a/src/location/doc/snippets/declarative/routing.qml b/src/location/doc/snippets/declarative/routing.qml new file mode 100644 index 0000000..ae56470 --- /dev/null +++ b/src/location/doc/snippets/declarative/routing.qml @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [QtQuick import] +import QtQuick 2.3 +//! [QtQuick import] +import QtPositioning 5.5 +import QtLocation 5.6 + +Item { + width: 1000 + height: 400 + + Plugin { + id: aPlugin + name: "osm" + } + + RouteQuery { + id: aQuery + waypoints: [ + { latitude: -27.575, longitude: 153.088}, + { latitude: -27.465, longitude: 153.023} + ] + travelModes: RouteQuery.CarTravel + routeOptimizations: RouteQuery.ShortestRoute + } + + //! [Route Maneuver List1] + RouteModel { + id: routeModel + // model initialization + //! [Route Maneuver List1] + plugin: aPlugin + autoUpdate: true + query: aQuery + //! [Route Maneuver List2] + } + + + ListView { + id: listview + anchors.fill: parent + spacing: 10 + model: routeModel.status == RouteModel.Ready ? routeModel.get(0).segments : null + visible: model ? true : false + delegate: Row { + width: parent.width + spacing: 10 + property bool hasManeuver : modelData.maneuver && modelData.maneuver.valid + visible: hasManeuver + Text { text: (1 + index) + "." } + Text { text: hasManeuver ? modelData.maneuver.instructionText : "" } + //! [Route Maneuver List2] + property RouteManeuver routeManeuver: modelData.maneuver + property RouteSegment routeSegment: modelData + + //! [RouteManeuver] + Text { + text: "Distance till next maneuver: " + routeManeuver.distanceToNextInstruction + + " meters, estimated time: " + routeManeuver.timeToNextInstruction + " seconds." + } + //! [RouteManeuver] + + //! [RouteSegment] + Text { + text: "Segment distance " + routeSegment.distance + " meters, " + routeSegment.path.length + " points." + } + //! [RouteSegment] + //! [Route Maneuver List3] + } + } + //! [Route Maneuver List3] +} diff --git a/src/location/doc/snippets/places/main.cpp b/src/location/doc/snippets/places/main.cpp new file mode 100644 index 0000000..ab34a3c --- /dev/null +++ b/src/location/doc/snippets/places/main.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "requesthandler.h" + +int main(int /*argc*/, char ** /*argv*/) +{ + return 0; +} + diff --git a/src/location/doc/snippets/places/places.pro b/src/location/doc/snippets/places/places.pro new file mode 100644 index 0000000..9fa4c7f --- /dev/null +++ b/src/location/doc/snippets/places/places.pro @@ -0,0 +1,5 @@ +TEMPLATE=app +TARGET=placescppsnippet +QT = core location +SOURCES+=main.cpp +HEADERS += requesthandler.h diff --git a/src/location/doc/snippets/places/requesthandler.h b/src/location/doc/snippets/places/requesthandler.h new file mode 100644 index 0000000..e5ee0d0 --- /dev/null +++ b/src/location/doc/snippets/places/requesthandler.h @@ -0,0 +1,590 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class RequestHandler : public QObject +{ +public: + void initializeManager() { + //! [Initialize Manager] + //The "provider name" is used to select a particular provider + QGeoServiceProvider *provider = new QGeoServiceProvider("provider name"); + QPlaceManager *manager = provider->placeManager(); + //! [Initialize Manager] + Q_UNUSED(provider); + Q_UNUSED(manager); + } + + void simpleSearch() + { + //! [Simple search] + //1) Make an appropriate request + QPlaceSearchRequest searchRequest; + searchRequest.setSearchTerm("ice cream"); + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(12.34, 56.78))); + + //2) Use the manager to initiate a request and retrieve a reply object + QPlaceSearchReply * searchReply = manager->search(searchRequest); + + //3) Connect the reply object to a slot which is invoked upon operation completion + connect(searchReply, SIGNAL(finished()), this, SLOT(processSearchReply())); + //! [Simple search] + } + + void search() + { + //! [Search for places cpp] + + //instantiate request and set parameters + QPlaceSearchRequest searchRequest; + searchRequest.setSearchTerm("ice cream"); + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(12.34, 56.78))); + + //send off a search request + /*QPlaceSearchReply * */ searchReply = manager->search(searchRequest); + + //connect a slot to handle the reply + connect(searchReply, SIGNAL(finished()), this, SLOT(handleSearchReply())); + + //! [Search for places cpp] + } + + void searchPaging() + { + //! [Search paging] + QPlaceSearchRequest searchRequest; + searchRequest.setLimit(15); //specify how many results are to be retrieved. + //! [Search paging] + } + + void details() + { + QPlace place; + //! [Details check] + if (!place.detailsFetched()) { + /*QPlaceDetailsReply * */ detailsReply = manager->getPlaceDetails(place.placeId()); + connect(detailsReply, SIGNAL(finished()), this, SLOT(handleDetailsReply())); + } + //! [Details check] + } + + void images() + { + QPlace place; + + //! [Image request] + QPlaceContentRequest request; + request.setContentType(QPlaceContent::ImageType); + request.setPlaceId(place.placeId()); + request.setLimit(5); + /*QPlaceContentReply * */ contentReply = manager->getPlaceContent(request); + connect(contentReply, SIGNAL(finished()), this, SLOT(handleImagesReply())); + //! [Image request] + } + + + void suggestion() + { + //! [Suggestion request] + QPlaceSearchRequest request; + request.setSearchTerm("piz"); + request.setSearchArea(QGeoCircle(QGeoCoordinate(12.34, 56.78))); + /* QPlaceSearchSuggestion * */suggestionReply = manager->searchSuggestions(request); + connect(suggestionReply, SIGNAL(finished()), this, SLOT(handleSuggestionReply())); + //! [Suggestion request] + } + + void savePlace() + { + //! [Save place pt1] + QPlace place; + place.setName( "Fred's Ice Cream Parlor" ); + + QGeoLocation location; + location.setCoordinate(QGeoCoordinate(12.34, 56.78)); + + QGeoAddress address; + address.setStreet("111 Nother Street"); + //! [Save place pt1] + + //! [Save place pt2] + location.setAddress(address); + place.setLocation(location); + + /* QPlaceIdReply * */savePlaceReply = manager->savePlace(place); + connect(savePlaceReply, SIGNAL(finished()), this, SLOT(handleSavePlaceReply())); + //! [Save place pt2] + } + + void removePlace() + { + QPlace place; + //! [Remove place] + /* QPlaceIdReply * */removePlaceReply = manager->removePlace(place.placeId()); + connect(removePlaceReply, SIGNAL(finished()), this, SLOT(handleRemovePlaceReply())); + //! [Remove place] + } + + void initializeCategories() + { + //! [Initialize categories] + /* QPlaceReply * */initCatReply = manager->initializeCategories(); + connect(initCatReply, SIGNAL(finished()), this, SLOT(handleInitCatReply())); + //! [Initialize categories] + } + + void saveCategory() + { + //! [Save category] + QPlaceCategory fastFood; + + QPlaceCategory category; + category.setName("pizza"); + /*QPlaceIdReply */ saveCategoryReply = manager->saveCategory(category); + connect(saveCategoryReply, SIGNAL(finished()), this, SLOT(handleSaveCategoryReply())); + + //we could have saved a category as a child by supplying a parent identifier. + saveCategoryReply = manager->saveCategory(category, fastFood.categoryId()); + //! [Save category] + } + + void removeCategory() + { + QPlaceCategory category; + //! [Remove category] + /* QPlaceIdReply * */removeCategoryReply = manager->removeCategory(place.placeId()); + connect(removeCategoryReply, SIGNAL(finished()), this, SLOT(handleRemoveCategoryReply())); + //! [Remove category] + } + + void searchRequest() { + QPlaceCategory diner; + QPlaceCategory restaurant; + + //! [Search request] + QPlaceSearchRequest searchRequest; + searchRequest.setSearchTerm("Fast food"); //search term for what we are interested in + + //set a search center + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(2.3, 48.87))); + + //set a distance hint as a relevancy hint. + //closer places have greater weighting in the ranking of results. + searchRequest.setRelevanceHint(QPlaceSearchRequest::DistanceHint); + + //use limit to adjust pagination. + //this limits the number of place results to 5 per page. + searchRequest.setLimit(5); + + //provide some categories to narrow down search + QList categories; + categories << diner << restaurant; + searchRequest.setCategories(categories); + //! [Search request] + } + + void content() { + QPlace place; + //! [Content request] + QPlaceContentRequest request; + request.setContentType(QPlaceContent::ImageType); + request.setPlaceId(place.placeId()); + request.setLimit(5); + + QPlaceContentReply *contentReply = manager->getPlaceContent(request); + //..connect signals..// + + //! [Content request] + Q_UNUSED(contentReply); + } + + void contentConversion() + { + //! [Content conversion] + QPlaceImage image; + image.setUrl(QUrl("www.example.com")); + + QPlaceContent content = image; + + QPlaceImage image2; + image2 = content; + qDebug() << image2.url(); //image2.url() == "www.example.com" + //! [Content conversion] + } + + void icon() { + QPlace place; + //! [icon] + QUrl iconSourceUrl = place.icon().url(QSize(32,32)); + + //A default icon may also be requested like so + iconSourceUrl = place.icon().url(); + //! [icon] + } + + void saveBetweenManagers() { + QPlaceResult result; + QPlaceIdReply *saveReply; + //! [ Save to different manager] + //result retrieved from a different manager) + QPlace place = manager->compatiblePlace(result.place()); + saveReply = manager->savePlace(place); + //! [ Save to different manager] + saveReply->abort();//needed to avoid warnings + } + + void ratings() { + //! [Ratings] + qDebug() << QString("This place rated ") + place.ratings().average() + + "out of " + place.ratings().maximum() + "stars"; + //! [Ratings] + } + + void matchPlaces() { + QList results; + //! [Match places] + QPlaceMatchRequest request; + request.setResults(results); + QVariantMap parameters; + parameters.insert(QPlaceMatchRequest::AlternativeId, "x_id_here"); + request.setParameters(parameters); + matchReply = manager->matchingPlaces(request); + //! [Match places] + } + +public slots: + // ![Simple search handler] + //4) Have the slot appropriately process the results of the operation + void processSearchReply() { + if (searchReply->error() == QPlaceReply::NoError) { + foreach (const QPlaceSearchResult &result, searchReply->results()) { + if (result.type() == QPlaceSearchResult::PlaceResult) + qDebug() << "Title:" << result.title(); + } + } + + //5) Discard the rely object when done. + searchReply->deleteLater(); + searchReply = 0; + } + // ![Simple search handler] + + //! [Search for places handler cpp] + void handleSearchReply() { + if (searchReply->error() == QPlaceReply::NoError) { + foreach (const QPlaceSearchResult &result, searchReply->results()) { + if (result.type() == QPlaceSearchResult::PlaceResult) { + QPlaceResult placeResult = result; + qDebug() << "Name: " << placeResult.place().name(); + qDebug() << "Coordinate " << placeResult.place().location().coordinate().toString(); + qDebug() << "Street: " << placeResult.place().location().address().street(); + qDebug() << "Distance: " << placeResult.distance(); + } + } + } + searchReply->deleteLater(); //discard reply + searchReply = 0; + } + //! [Search for places handler cpp] + + //! [Details handler cpp] + void handleDetailsReply() { + QPlace place; + if (detailsReply->error() == QPlaceReply::NoError) + place = detailsReply->place(); + + detailsReply->deleteLater(); //discard reply + detailsReply = 0; + } + //! [Details handler cpp] + + //! [Image handler] + void handleImagesReply() { + if (contentReply->error() == QPlaceReply::NoError) { + QMapIterator iter(contentReply->content()); + while (iter.hasNext()) { + qDebug() << "Index: " << iter.key(); + QPlaceImage image = iter.value(); + qDebug() << image.url(); + qDebug() << image.mimeType(); + } + + //alternatively if indexes are irrelevant + foreach (const QPlaceImage &image, contentReply->content()) { + qDebug() << image.url(); + qDebug() << image.mimeType(); + } + + //we can assign content to the place that it belongs to. + //the place object serves as a container where we can retrieve + //content that has already been fetched + place.insertContent(contentReply->request().contentType(), contentReply->content()); + place.setTotalContentCount(contentReply->request().contentType(), contentReply->totalCount()); + } + + contentReply->deleteLater(); + contentReply = 0; + } + //! [Image handler] + + //! [Suggestion handler] + void handleSuggestionReply() { + if (suggestionReply->error() == QPlaceReply::NoError) { + foreach (const QString &suggestion, suggestionReply->suggestions()) + qDebug() << suggestion; + } + + suggestionReply->deleteLater(); //discard reply + suggestionReply = 0; + } + + //! [Suggestion handler] + + //! [Save place handler] + void handleSavePlaceReply() { + if (savePlaceReply->error() == QPlaceReply::NoError) + qDebug() << savePlaceReply->id(); + + savePlaceReply->deleteLater(); //discard reply + savePlaceReply = 0; + } + //! [Save place handler] + + //! [Remove place handler] + void handleRemovePlaceReply() { + if (removePlaceReply->error() == QPlaceReply::NoError) + qDebug() << "Removal of place identified by" + << removePlaceReply->id() << "was successful"; + + removePlaceReply->deleteLater(); //discard reply + removePlaceReply = 0; + } + //! [Remove place handler] + + //! [Initialize categories reply] + void handleInitCatReply() { + if (initCatReply->error() == QPlaceReply::NoError) + qDebug() << "Categories initialized"; + else + qDebug() << "Failed to initialize categories"; + + initCatReply->deleteLater(); + initCatReply = 0; + } + //! [Initialize categories reply] + + void categories() { + QPlaceCategory pizza; + //! [Top level categories] + QList topLevelCategories = manager->childCategories(); + foreach (const QPlaceCategory &category, topLevelCategories) + qDebug() << category.name(); + //! [Top level categories] + + //! [Child categories] + QList childCategories = manager->childCategories(pizza.categoryId()); + //! [Child categories] + } + + //! [Save category handler] + void handleSaveCategoryReply() { + if (saveCategoryReply->error() == QPlaceReply::NoError) { + qDebug() << "Saved category id =" << saveCategoryReply->id(); + } + + saveCategoryReply->deleteLater(); + saveCategoryReply = 0; + } + //! [Save category handler] + + //! [Remove category handler] + void handleRemoveCategoryReply() { + if (removeCategoryReply->error() == QPlaceReply::NoError) + qDebug() << "Removal of category identified by" + << removeCategoryReply->id() << "was successful"; + + removeCategoryReply->deleteLater(); //discard reply + removeCategoryReply = 0; + } + //! [Remove category handler] + + //! [Content handler] + void contentHandler() { + if (contentReply->error() == QPlaceReply::NoError) { + place.insertContent(contentReply->request().contentType(), + contentReply->content()); + } + } + //! [Content handler] + + void phoneNumbers() { + //! [Phone numbers] + if (place.contactTypes().contains(QPlaceContactDetail::Phone)) { + foreach (const QPlaceContactDetail &number, place.contactDetails(QPlaceContactDetail::Phone)) + qDebug() << number.label() << ":" << number.value(); + } + //! [Phone numbers] + } + + + void openingHours() { + //! [Opening hours] + if (place.extendedAttributeTypes().contains(QPlaceAttribute::OpeningHours)) + qDebug() << place.extendedAttribute(QPlaceAttribute::OpeningHours).text(); + //! [Opening hours] + } + + //! [Match places handler] + void matchHandler() { + if (matchReply->error() == QPlaceReply::NoError) { + foreach (const QPlace place, matchReply->places()) { + if (place != QPlace()) + qDebug() << "Place is a favorite with name" << place.name(); + else + qDebug() << "Place is not a favorite"; + } + } + + matchReply->deleteLater(); + matchReply = 0; + } + //! [Match places handler] + + void convertSearchResult() { + QPlaceSearchResult result; + //! [Convert search result] + if (result.type() == QPlaceSearchResult::PlaceResult) { + QPlaceResult placeResult = result; + qDebug() << placeResult.place().name(); + qDebug() << placeResult.place().location().coordinate(); + qDebug() << placeResult.distance(); + } + //! [Convert search result] + } + +QPlaceSearchReply *searchReply; +QPlaceManager *manager; +QPlaceDetailsReply *detailsReply; +QPlaceContentReply *contentReply; +QPlaceSearchSuggestionReply *suggestionReply; +QPlaceIdReply *savePlaceReply; +QPlaceIdReply *removePlaceReply; +QPlaceIdReply *saveCategoryReply; +QPlaceIdReply *removeCategoryReply; +QPlaceReply *initCatReply; +QPlaceMatchReply *matchReply; +QPlace place; +}; + +class ManagerEngine : public QObject +{ +}; + +//! [Implement reply pt1] +class SearchReply : public QPlaceSearchReply +{ +public: + explicit SearchReply(ManagerEngine *engine) + : QPlaceSearchReply(engine), m_engine(engine){} + + ~SearchReply(); + void setResults(const QList &results); + void setRequest(const QPlaceSearchRequest &request); +//! [Implement reply pt1] + +//! [Implement reply pt2] + void triggerDone(QPlaceReply::Error error = QPlaceReply::NoError, + const QString &errorString = QString()); + + ManagerEngine *m_engine; +}; +//! [Implement reply pt2] + +class SearchSuggestionReply : public QPlaceSearchSuggestionReply +{ +public: + void triggerDone(QPlaceReply::Error error = QPlaceReply::NoError, + const QString &errorString = QString()); + + ManagerEngine *m_engine; + +}; + +//! [Trigger done] +void SearchSuggestionReply::triggerDone(QPlaceReply::Error error, + const QString &errorString) +{ + if (error != QPlaceReply::NoError) { + this->setError(error,errorString); + QMetaObject::invokeMethod(m_engine, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *,this), + Q_ARG(QPlaceReply::Error, error), + Q_ARG(QString, errorString)); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error), + Q_ARG(QString, errorString)); + } + + this->setFinished(true); + QMetaObject::invokeMethod(m_engine, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *,this)); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); +} +//! [Trigger done] diff --git a/src/location/doc/snippets/snippets.pro b/src/location/doc/snippets/snippets.pro new file mode 100644 index 0000000..e4946c8 --- /dev/null +++ b/src/location/doc/snippets/snippets.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += places declarative cpp diff --git a/src/location/doc/src/cpp-qml.qdoc b/src/location/doc/src/cpp-qml.qdoc new file mode 100644 index 0000000..b8a49fa --- /dev/null +++ b/src/location/doc/src/cpp-qml.qdoc @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-cpp-qml.html +\title Interfaces between C++ and QML Code in Qt Location + +\brief Some of the location QML types providing interfaces to access and modify properties in C++. + +\section2 Category - QPlaceCategory +The \l {Category::category} {Category.category} property is used to provide an interface between C++ and QML code. First a pointer to a +Category object must be obtained from C++, then use the \l {QObject::property()}{property()} and +\l {QObject::setProperty()}{setProperty()} functions to get and set the \c category property. +The following gets the QPlaceCategory representing this object from C++: +\snippet cpp/cppqml.cpp Category get +The following sets the properties of this object based on a QPlaceCategory object from C++: +\snippet cpp/cppqml.cpp Category set + + +\section2 ContactDetail - QDeclarativeContactDetail +The \l {ContactDetail::contactDetail} {ContactDetail.contactDetail} property is used to provide an interface between C++ and QML code. First a pointer to a ContactDetail object must be obtained from C++, then use the +\l {QObject::property()}{property()} and \l {QObject::setProperty()}{setProperty()} functions +to get and set the \c contactDetail property. +The following gets the QPlaceContactDetail representing this object from C++: +\snippet cpp/cppqml.cpp ContactDetail get +The following sets the properties of this object based on a QPlaceContactDetail object from +C++: +\snippet cpp/cppqml.cpp ContactDetail set + + +\section2 Place - QPlace +The \l {Place::place} {Place.place} property is used to provide an interface between C++ and QML code. First a pointer to a Place object must be obtained from C++, then use the +\l {QObject::property()}{property()} and \l {QObject::setProperty()}{setProperty()} functions +to get and set the \c place property. +The following gets the QPlace representing this object from C++: +\snippet cpp/cppqml.cpp Place get +The following sets the properties of this object based on a QPlace object from C++: +\snippet cpp/cppqml.cpp Place set + + + +\section2 PlaceAttribute - QPlaceAttribute +The \l {PlaceAttribute::attribute} {PlaceAttribute.attribute} property is used to provide an interface between C++ and QML code. First a pointer to a +PlaceAttribute object must be obtained from C++, then use the +\l {QObject::property()}{property()} and \l {QObject::setProperty()}{setProperty()} functions +to get and set the \c attribute property. +The following gets the QPlaceAttribute representing this object from C++: +\snippet cpp/cppqml.cpp PlaceAttribute get +The following sets the properties of this object based on a QPlaceAttribute object from C++: +\snippet cpp/cppqml.cpp PlaceAttribute set + + +\section2 Icon - QPlaceIcon +The \l {Icon::icon} {Icon.icon} property is used to provide an interface between C++ and QML code. First a pointer to a Icon object must be obtained from C++, then use the \l {QObject::property()}{property()} and +\l {QObject::setProperty()}{setProperty()} functions to get and set the \c icon property. +The following gets the QPlaceIcon representing this object from C++: +\snippet cpp/cppqml.cpp Icon get +The following sets the properties of this object based on a QPlaceIcon object from C++: +\snippet cpp/cppqml.cpp Icon set + + +\section2 User - QPlaceUser +The \l {User::user} {User.user} property is used to provide an interface between C++ and QML code. First a pointer to a +User object must be obtained from C++, then use the \l {QObject::property()}{property()} and +\l {QObject::setProperty()}{setProperty()} functions to get and set the \c user property. +The following gets the QPlaceUser representing this object from C++: +\snippet cpp/cppqml.cpp User get +The following sets the properties of this object based on a QPlaceUser object from C++: +\snippet cpp/cppqml.cpp User set + + +\section2 Ratings - QPlaceRatings +The \l {Ratings::ratings} {Ratings.ratings} property is used to provide an interface between C++ and QML code. First a pointer to a +Ratings object must be obtained from C++, then use the \l {QObject::property()}{property()} and +\l {QObject::setProperty()}{setProperty()} functions to get and set the \c ratings property. +The following gets the QPlaceRating representing this object from C++: +\snippet cpp/cppqml.cpp Ratings get +The following sets the properties of this object based on a QPlaceRatings object from C++: +\snippet cpp/cppqml.cpp Ratings set + + +\section2 Supplier - QPlaceSupplier +The \l {Supplier::supplier} {Supplier.supplier} property is used to provide an interface between C++ and QML code. First a pointer to a +Supplier object must be obtained from C++, then use the \l {QObject::property()}{property()} and +\l {QObject::setProperty()}{setProperty()} functions to get and set the \c supplier property. +The following gets the QPlaceSupplier representing this object from C++: +\snippet cpp/cppqml.cpp Supplier get +The following sets the properties of this object based on a QPlaceSupplier object from C++: +\snippet cpp/cppqml.cpp Supplier set + +*/ diff --git a/src/location/doc/src/example-parameters.qdocinc b/src/location/doc/src/example-parameters.qdocinc new file mode 100644 index 0000000..2ae351b --- /dev/null +++ b/src/location/doc/src/example-parameters.qdocinc @@ -0,0 +1,12 @@ +The example can work with any of the available geo services plugins. However, some +plugins may require additional \l {QtLocation::PluginParameter}{plugin parameters} in order to +function correctly. \l {QtLocation::PluginParameter}{Plugin parameters} can be passed on the +command line using the \c {--plugin} argument, which takes the form: + +\badcode + --plugin. +\endcode + +Refer to the documentation for each of the geo services plugins for details on what plugin +parameters they support. The default plugin used by this example is +\l {Qt Location Open Street Map Plugin}, which does not require any parameters. diff --git a/src/location/doc/src/maps.qdoc b/src/location/doc/src/maps.qdoc new file mode 100644 index 0000000..d929b12 --- /dev/null +++ b/src/location/doc/src/maps.qdoc @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +/*! +\page location-maps-qml.html +\title Maps and Navigation (QML) + +\brief Provides QtQuick user interfaces for displaying, navigating and + interacting with maps, as well as geocoding and navigation. + +\b{Maps and Navigation} provides QtQuick user interface types for +displaying geographic information on a map, as well as allowing user +interaction with map overlay objects and the display itself. It also +contains utilities for geocoding (finding a geographic coordinate from a +street address) and navigation (including driving and walking directions). + +It builds upon the API concepts and types in the \l{Positioning (QML)}{QML Positioning API}. +A more hands-on introduction of the Maps and Navigation types can be found in the +\l {QML Maps}{Maps and Navigation tutorial}. + +\section1 Maps + +\section2 Displaying Maps + +Displaying a map is done using the \l{QtLocation::Map}{Map} QML types. The Map type supports +user interaction through the \l{QtLocation::MapGestureArea}{MapGestureArea} QML type. The Map +object draws the map on-screen using OpenGL (ES), allowing for hardware-accelerated rendering +where available. + +\b{Key Types} +\table + \row + \li \l{QtLocation::Plugin}{Plugin} + \li A location-based services plugin provides data including map data which is then displayed in a Map object. + \row + \li \l{QtLocation::Map}{Map} + \li QtQuick item to display a map on-screen. + \row + \li \l{QtLocation::MapGestureArea}{MapGestureArea} + \li Interaction helper for panning, flicking and pinch-to-zoom gesture on a Map. +\endtable + +Note that the client must create a \l{QtLocation::Plugin}{Plugin} object +prior to using a \l{QtLocation::Map}{Map} type in order to have access +to map data to display. + +\section2 Putting Objects on a Map (Map Overlay Objects) + +Maps can also contain map overlay objects, which are used to display information +on its surface. There is a set of basic pre-defined map overlay objects, as well +as the ability to implement custom map overlay objects using the +\l{QtLocation::MapQuickItem}{MapQuickItem} type, which can contain any +standard QtQuick item. + +\b{Key Types} +\table + \row + \li \l{QtLocation::MapCircle}{MapCircle} + \li A geographic circle (all points at a set distance from a center), optionally with a border. + \row + \li \l{QtLocation::MapRectangle}{MapRectangle} + \li A rectangle whose top left and bottom right points are specified as + \l {coordinate} types, optionally with a border. + \row + \li \l{QtLocation::MapPolygon}{MapPolygon} + \li A polygon made of an arbitrary list of \l {coordinate}{coordinates}. + \row + \li \l{QtLocation::MapPolyline}{MapPolyline} + \li A polyline made of an arbitrary list of \l {coordinate}{coordinates}. + \row + \li \l{QtLocation::MapQuickItem}{MapQuickItem} + \li Turns any arbitrary QtQuick Item into a map overlay object. MapQuickItem is an enabler for specifying custom map overlay objects. +\endtable + +\section2 Model-View Design with Map Overlay Objects + +To automatically generate map overlay objects based on the contents of a QtQuick +model (for example a ListModel item), the \l{QtLocation::MapItemView}{MapItemView} +type is available. It accepts any map overlay object as its delegate, and can +only be created within a \l{QtLocation::Map}{Map}. + +\b{Key Types} +\table + \row + \li \l{QtLocation::MapItemView}{MapItemView} + \li Populates a Map with map overlay objects based on the data provided by a model. +\endtable + +\section2 Interaction with Map Overlay Objects + +Properties of map overlay objects that influence their appearance on the display can +be changed at any time, and many can also be used in animations. Animating +coordinate-based map overlay objects, such as MapPolygon and MapPolyline, is not yet +available. + +\section1 Geocoding -- Address to Coordinate and Vice Versa + +Geocoding is the translation of geographic coordinates into addresses, or vice +versa. Such a translation usually involves sending the source data to a server +which then performs the translation and returns the results, although some +location-based service provider \l{QtLocation::Plugin}{plugins} may be able to +provide some geocoding functionality without sending data to a remote server. +The availability and accuracy of the translation usually depends on the location +or address being translated, as different areas of the Earth are mapped to +varying degrees of accuracy. + +A geocoding query in QML is performed using the +\l{QtLocation::GeocodeModel}{GeocodeModel} type. For an address-to-coordinate +query, its \c{query} property may be set to either an +\l [QtPositioning]{Address} object or a string containing the textual +form of the address to search for. To perform the reverse, the same property +can be set to a \l {coordinate} instead. Results are made available in the +contents of the model. + +\b{Key Types} +\table + \row + \li \l{QtLocation::Plugin}{Plugin} + \li A location-based services plugin provides data including geocoding translation results which are exposed to clients via a GeocodeModel. + \row + \li \l{QtLocation::GeocodeModel}{GeocodeModel} + \li Queries the Plugin for geocoding translations and provides access to results via indexes in the model. + \row + \li \l[QtPositioning]{Address} + \li Structured address for use in queries and results of geocoding. +\endtable + +Note that the client must create a \l{QtLocation::Plugin}{Plugin} object +prior to using a \l{QtLocation::GeocodeModel}{GeocodeModel} object. This +will enable access to geocoding translation services and thus data to display. + +\section1 Routing and Navigation + +Routing is the determination of a navigable path from one point to another on +a map. Given a map that is aware of features that aid or hinder navigation, such as +bridges, waterways and so on, a series of segments that make +up the journey can be constructed. If these \l {RouteSegment}s are simple then we can +add navigation information at the connecting points, \l {RouteManeuver}s, +between the segments. + +\b{Key Types} +\table + \row + \li \l{QtLocation::Route}{Route} + \li The entire path to be navigated. + \row + \li \l{QtLocation::RouteSegment}{RouteSegment} + \li The individual components of a route. + \row + \li \l{QtLocation::RouteManeuver}{RouteManeuver} + \li The navigation information that joins segments. + \row + \li \l{QtLocation::RouteModel}{RouteModel} + \li The means of making requests on the backend to supply route + information. +\endtable + + + + + + +*/ + + +/*! +\page location-maps-cpp.html +\title Maps and Navigation (C++) + +\brief Provides C++ classes for Geocoding and Navigation. + +\b{Maps and Navigation} provides C++ utilities for geocoding (finding a +geographic coordinate from a street address) and navigation (including driving +and walking directions). + +Currently it is not possible to interact with maps via C++. Mapping applications +must use the \l {Maps and Navigation (QML)} API. + + +\section1 Geocoding + +In C++, an address-to-coordinate query is performed using the +\l{QGeoCodingManager::geocode()}{geocode()} method of the QGeoCodingManager +class. For coordinate-to-address queries, the +\l{QGeoCodingManager::reverseGeocode()}{reverseGeocode()} method is available +on the same class. Instances of QGeoCodingManager are available via +\l{QGeoServiceProvider}. + +\b{Key Classes} +\table + \row + \li \l{QGeoServiceProvider} + \li Provides a QGeoCodingManager instance ready for use. + \row + \li \l{QGeoCodingManager} + \li Accepts queries and produces QGeoCodeReply objects. + \row + \li \l{QGeoCodeReply} + \li Contains the results of a geocoding query. +\endtable + +\section1 Navigation + +In C++, a route query is performed using the \l{QGeoRoutingManager::calculateRoute()}{calculate()} +method of the QGeoRoutingManager class. The returned route reply can contain +multiple routes to the same destination. + +\b{Key Classes} +\table + \row + \li \l{QGeoServiceProvider} + \li Provides a QGeoCodingManager instance ready for use. + \row + \li \l{QGeoRoutingManager} + \li Accepts queries and produces QGeoRouteReply objects. + \row + \li \l{QGeoRouteReply} + \li Contains the results of a routing query. + \row + \li \l{QGeoRoute} + \li Contains information about a route. +\endtable + + +*/ diff --git a/src/location/doc/src/place-caveats.qdocinc b/src/location/doc/src/place-caveats.qdocinc new file mode 100644 index 0000000..d3d9bd0 --- /dev/null +++ b/src/location/doc/src/place-caveats.qdocinc @@ -0,0 +1,21 @@ + The Places API is currently designed for only saving \a {core} details. Saving rich content like + images and reviews or details like supplier and rating is not a supported use case. Typically a manager + will generally ignore these fields upon save and may produce a warning message if they are populated. + + The Places API only supports saving of the following \e {core details}: + \list + \li name + \li place id + \li location + \li contact details + \li icon + \li categories (tag-like names to describe a place) + \li visibility scope + \endlist + + It is possible that providers may only support a subset of these. + See the \l {Qt Location#Plugin References and Parameters}{plugin documentation} for more + details. + + Saving of properties such as the rating, extended attributes, + images, reviews, editorials and supplier is explicitly not supported by the Places API. diff --git a/src/location/doc/src/place-crossref.qdocinc b/src/location/doc/src/place-crossref.qdocinc new file mode 100644 index 0000000..f0502f4 --- /dev/null +++ b/src/location/doc/src/place-crossref.qdocinc @@ -0,0 +1,7 @@ + \code + origin R/O manager(here) destination R/W manager (places_jsondb) + Save + Place id: ae246 ---> Place id: 0001 + Attribute type: x_provider Attribute type: x_id_here + Attribute value: here Attribute text value: ae246 + \endcode diff --git a/src/location/doc/src/place-definition.qdocinc b/src/location/doc/src/place-definition.qdocinc new file mode 100644 index 0000000..2398775 --- /dev/null +++ b/src/location/doc/src/place-definition.qdocinc @@ -0,0 +1,27 @@ +A place is a point of interest, it could be a favorite restaurant, a park or someone's home. +A QPlace object represents a place by acting as a container for various information about that place. + +This information can be divided into 2 broad classifications + +\list +\li Details +\li Rich content +\endlist + +The place details consist of properties of the place, such as the name, +location, contact information and so on. When a place is returned during a +search, these details are filled in. Sometimes in order to save bandwidth, +there are further details about the place that can be retrieved on an +individual place by place basis, if the user is interested. The +QPlace::detailsFetched() function can be queried to see if all available +details have been fetched, and if not, QPlaceManager::getPlaceDetails() can +be used to retrieve them. Precisely which details are populated during a +search and which need to be fetched individually may vary from provider to +provider. See \l {Qt Location#Plugin References and Parameters}{plugin documentation} for +more details. + +The rich content of a place consists of items such as images, reviews and +editorials. Potentially there may be many rich content items, so they are +treated separately from the place details. They can be retrieved in a paged +fashion via QPlaceManager::getPlaceContent(). If necessary, the content may +be assigned to a place so it can act as a convenient container. diff --git a/src/location/doc/src/places.qdoc b/src/location/doc/src/places.qdoc new file mode 100644 index 0000000..9c01fba --- /dev/null +++ b/src/location/doc/src/places.qdoc @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \page location-places-qml.html + \title QML Places API + + \section1 Overview + + The Places API allows users to discover places of interest and view + details about them, such as address and contact information. Some places may have + additional content associated with them, such as images and reviews. + The Places API also facilitates management of places and + categories, allowing users to save and remove them. + + \section1 Introductory Concepts + + \section2 Plugin + A \l Plugin is an abstraction for a backend. One \l Plugin might access places from a + REST server while another may access places from a local database. The following + instantiates a \l Plugin object by providing a name of "osm". The \l Plugin name + identifies which backend to choose from. Plugins may also be provided with a set of + \l {PluginParameter} {parameters}, which essentially takes the form of a set of + key-value pairs. The \l {PluginParameter} {parameters} that can be specified vary + among the different \l Plugin backends. For documentation on the possible \l + {PluginParameter} {parameters} and nuances of each \l Plugin, see the \l {Plugin + references and parameters}{Plugin References}. + + \snippet places_list/places_list.qml Initialize Plugin + + \note The HERE plugin must be supplied with some mandatory parameters as outlined + in the \l {Mandatory Parameters} {HERE Plugin} documentation. + + \section2 Models, Views and Delegates + The QML Places API is built around the notion of models, views and delegates. + + \table + \row + \li \b Model + \li A model holds data items and maintains their structure. + The model is also responsible for retrieving the items from a data source. + \row + \li \b View + \li A view is a visual container that displays the data and manages how visual + items are shown such as in a list or a grid. The view may also + be responsible for navigating the data, for example, scrolling through + the visual items during a flicking motion. + \row + \li \b Delegate + \li A delegate defines how individual data elements should appear + as visual items in the view. The models expose a set of data roles + and the delegate uses them to construct a visual item. The delegate + may also define behaviour such as an operation to invoke when a visual + item is clicked. + \endtable + + The Common Use Cases section below demonstrates concrete examples of how + these concepts fit together. + + \section1 Common Use Cases + + \section2 Searching for Places + Searching is accomplished via the \l PlaceSearchModel. The \l + {PlaceSearchModel::plugin} {plugin} property specifies which backend to + perform search operations against. Search parameters may be provided + through properties such as the \l {PlaceSearchModel::searchTerm} + {searchTerm} and \l {PlaceSearchModel::searchArea} {searchArea}. A search + operation can then be started by invoking the \l {PlaceSearchModel::update} + {update()} method. For simplicity, the snippet below invokes \l + {PlaceSearchModel::update} {update()} once construction of the model as + been completed, typically \l {PlaceSearchModel::update} {update()} would be + invoked in response to a user action such as a button click. While the + search operation is underway the \l {PlaceSearchModel::status} property + transitions into the \c Loading state and when successfully completed moves + into the \c Ready state. + + \snippet places_list/places_list.qml PlaceSearchModel + + \section2 Display Search Results using a ListView + A \l ListView can be used to show the search results found by the model. + It defines the visual region for where the results are shown, and in the + case below fills the entirety of its parent. The \l ListView has built in + behavior that enables the region to respond to flicking events and to + scroll appropriately. + + In the snippet below, the search model has been assigned to the ListView's + \l {ListView::model} {model} property. When the model is updated with new + results, the \l ListView is automatically updated to reflect the model's new + data items. + + A simple delegate has been bound to the \l {ListView}'s \l + {ListView::delegate} {delegate} property. The \l PlaceSearchModel exposes + a set of \l {PlaceSearchModel Roles} {roles} of which the \e title and \e + place roles have been used below, these are of type string and \l Place + respectively. Essentially for each data item that should be visible in the + view, the view invokes the delegate to create a visual representation of + the item. + + \table + \row + \li + \snippet places_list/places_list.qml Places ListView + \li + \inlineimage places_list.png + \endtable + + \note For simplicty's sake we have assumed that every search result is of + \l {Search Result Types} {type} \c PlaceSearchResult and so always have + access to the \e place role, other search result types may not have a + \e place role. + + See the \l {Places List(QML)} {Places List} example for full source code. + + \section2 Display Search Results using a MapItemView + Instead of a \l ListView, the \l PlaceSearchModel can be used in + conjunction with a \l MapItemView to display markers on a map. Firstly a + \l Map is used to define the visual region occupied by the map, in this + case it fills the entirety of its parent. Other properties are specified + such as the \l {Map::plugin} {plugin} providing the maps, and the map's \l + {Map::center} {center} and \l {Map::zoomLevel} {zoomLevel}. + + Inside the \l Map, a \l MapItemView is declared, where the \l + {MapItemView::model} {model} property has been set to the search model and + a \l {MapItemView::delegate} {delegate} consisting of a \l MapQuickItem is + used to display a marker image. A marker is shown for every place that + was found by the search model. The delegate uses the \e place role + to position the marker. + + \table + \row + \li + \snippet places_map/places_map.qml Places MapItemView + \li + \inlineimage places_map.png + \endtable + + \note For simplicty's sake we have assumed that every search result is of + \l {Search Result Types} {type} \c PlaceSearchResult and so always have + access to the \e place role, other search result types may not have a + \e place role. + + See the \l {Places Map(QML)} {Places Map} example for full source code. + + \section2 Fetching Place Details + In order to save bandwidth, sometimes a backend will only return places which + are partially populated with details. This can be checked with the + Place::detailsFetched property which indicates whether all availalable details + have been fetched or not. If not, the Place::getDetails() method can be invoked + to fetch the remaining details. + + \snippet declarative/places.qml Place fetchDetails + + \section2 Saving and Removing Places + Some backends may support saving and removing places. This can be done by + calling the Place::save() and Place::remove() methods respectively. Note + that in order to save a \l Place, a \l Plugin must be assigned to specify + which backend we are saving to. The \l {Place::status} {status} property will + transition into the \c Saving state while the save operation is happening and on + successful completion will move to the \c Ready state. The following + snippet shows how to save and remove a place using javascript. + + \snippet declarative/places.qml Place createAndSavePlace + \codeline + \snippet declarative/places.qml Place removePlace + + \section2 Learn More + The above snippets only exhibit a small subset of Places functionality. + Refer to the \l {Places Types} shown below for richer content such as \l {ImageModel} {images}, \l {ReviewModel} {reviews} etc, as well as more indepth descriptions and explanations. + + See also the \l {Places (QML)}{Places (QML)} example for a more comprehensive demonstration on + how to use the API. + + \section1 Places Types + \section2 Data Types + \annotatedlist qml-QtLocation5-places-data + + \section2 Models + \annotatedlist qml-QtLocation5-places-models +*/ + +/*! + \page location-places-cpp.html + \title Places (C++) + + \section1 Overview + + The Places API allows users to discover places/points of interest + and view details about them such as address and contact information; + some places may even have rich content such as images and reviews. + The Places API also facilitates management of places and + categories, allowing users to save and remove them. + + \section1 Place Definition + \include place-definition.qdocinc + + \section1 Common Operations + + \section2 Initializing a Manager + All places functionality is facilitated by a QPlaceManager instance. One must specify + a QGeoServiceProvider in order to create the QPlaceManager + + \snippet places/requesthandler.h Initialize Manager + + \section2 Discovery/Search + + In order to perform a search operation we simply create a QPlaceSearchRequest + and set the desired search parameters, such as a search term and search center. + + \snippet places/requesthandler.h Search for places cpp + + The request is an asynchronous operation so we need a slot to handle the + completion of the request. In the handler we check that there are no errors and that our search result + type is a place. If so we can then retrieve some of the core details of the + place. At the end of the slot, we delete the reply since they are for single use only. + + \snippet places/requesthandler.h Search for places handler cpp + + \b {Note:} Depending upon the plugin backend that was chosen, the search results may contain places + which have further details that can be fetched on a place by place basis. To fetch these other details + see \l {Fetching Place Details}. + + \section3 Recommendations + Recommendations can be retrieved by supplying a place id via QPlaceSearchRequest::setRecommendationId(). + Any places similar to the given place are retrieved. + + \section3 Paging + If the plugin supports paging, the limit parameter may be provided to the search request. + \snippet places/requesthandler.h Search paging + + \section2 Fetching Place Details + A place that has been returned from a search request may have more details + that can be fetched. The following demonstrates how to check if there + are further details and if so how to request them. + + \snippet places/requesthandler.h Details check + \dots + \dots + \snippet places/requesthandler.h Details handler cpp + + \section2 Fetching Rich Content + Rich content such as images and reviews is retrieved through the manager and then if required assigned to a place. + \snippet places/requesthandler.h Image request + + We can handle the content request as shown below. + \snippet places/requesthandler.h Image handler + + It is important to note that the results in the QPlaceContentReply, + is a QPlaceContent::Collection which is essentially a QMap. The key \c {int} in this case is the + index of the content, and the value is the content itself. Due to the way Content is implemented + it is possible to convert a content type as follows + \code + QPlaceImage image = content; //provided that 'content' has a type QPlace::ImageType + \endcode + + The usage of the QPlaceContent::Collection and the conversion between content and its subtypes means + that code for handling the mechanics of paging reviews, images and editorials can be easily shared. + + \section2 Search Suggestions + The retrieval of search term suggestions is very similar to performing a place search. A QPlaceSearchRequest + is used just like a place search, the only difference being that the search term is set to a + partially completed string. + + \snippet places/requesthandler.h Suggestion request + And when the request is done, we can use the reply to show the suggestions. + \snippet places/requesthandler.h Suggestion handler + + \target Saving a place cpp + \section2 Saving a Place + The saving of a new place is performed as follows, we create a QPlace instance + and populate it with information such as a name, address and coordinate. Once + done we can invoke QPlaceManager::savePlace() to begin a save operation. + \snippet places/requesthandler.h Save place pt1 + \dots + \snippet places/requesthandler.h Save place pt2 + + Once a place is saved the reply contains the new identifier for that place. + \snippet places/requesthandler.h Save place handler + + Note that to save an already \e existing place, the QPlace::placeId() must + be filled in with the correct identifier. Otherwise a new place will be created if empty or the + wrong place overwritten if the identifier is incorrect. + + When a place is saved, the QPlaceManager may emit QPlaceManager::placedAdded() or QPlaceManager::placeUpdated() + signals. However whether a manager does so or not is provider specific, managers accessing places + from a web service will typically not emit these signals while managers accessing places locally stored generally will. + + \section3 Caveats + \input place-caveats.qdocinc + + \section3 Saving Between Managers + When saving places between managers, there are a few things to be aware of. + Some fields of a place such as the id, categories and icons are manager specific entities + for example the categories in one manager may not be recognized in another. + Therefore trying to save a place directly from one manager to another is not possible. + + The typical approach is to use the QPlaceManager::compatiblePlace() function, + it creates a copy of a place, but only copies data that the manager supports. + Manager specific data such as the place identifier is not copied over. The new + copy is now suitable for saving into the manager. If the manager supports matching by alternative + identifiers, an alternative identifier attribute is assigned to the copy (see \l {Matching places between managers}) + + \snippet places/requesthandler.h Save to different manager + + \target Removing a place cpp + \section2 Removing a Place + The removal of a place is performed as follows: + \snippet places/requesthandler.h Remove place + \dots + \dots + \snippet places/requesthandler.h Remove place handler + + When a place is removed, the QPlaceManager may emit the QPlaceManager::placeRemoved() signal. Whether a + manager does so is provider specific. Managers accessing places from a web service will typically not emit + these signals, while managers accessing places stored locally generally will. + + \section2 Using Categories + + Categories are keywords that can describe a place. For example, 'park', 'theater', + 'restaurant'. A place could be described by many categories, it could be a park and a music venue and a ferry or bus stop. + + To use categories they must first be initialized. + \snippet places/requesthandler.h Initialize categories + \dots + \dots + \snippet places/requesthandler.h Initialize categories reply + + After the categories have been initialized we can then use these category functions. + \list + \li QPlaceManager::childCategories() + \li QPlaceManager::category() + \li QPlaceManager::parentCategoryId() + \li QPlaceManager::childCategoryIds(); + \endlist + + To retrieve the top level categories + we use the QPlaceManager::childCategories() function but do not provide + a category identifier. + + \snippet places/requesthandler.h Top level categories + + If we did provide an identifier then we could retrieve a category's children. + + \snippet places/requesthandler.h Child categories + + \section2 Saving a Category + The following shows how to save a category + \snippet places/requesthandler.h Save category + \dots + \dots + \snippet places/requesthandler.h Save category handler + + When a category is saved, the QPlaceManager may emit QPlaceManager::categoryAdded() or QPlaceManager::categoryUpdated() + signals. However whether a manager does so or not is provider specific, managers accessing places + from a web service will typically not emit these signals while managers accessing places locally stored generally will. + + + \section2 Removing a Category + Category removal is very similar to removing a place + \snippet places/requesthandler.h Remove category + \dots + \dots + \snippet places/requesthandler.h Remove category handler + + When a category is removed, the QPlaceManager may emit the QPlaceManager::categoryRemoved() signal. Whether a + manager does so is provider specific. Managers accessing places from a web service will typically not emit + these signals, while managers accessing places stored locally generally will. + + \section2 Matching Places Between Managers + Sometimes you may want to cross reference whether places from one manager match those from another manager. + Such a situation may arise where one manager provides read-only access to places (origin manager) while another second r/w + manager (destination manager) is used to save selected favorites from the first. During a search + of the origin manager we may want to know which ones have been 'favorited' into the destination manager and perhaps display + a customized favorite name rather than the original name. + + The matching mechanism can vary between managers, but is typically accomplished through an alternative identifier. + As part of the save process, the place identifier from the origin manager is saved as an alternative identifier attribute in the destination manager + (which can have its own place identifier scheme). In the following example, the origin manager is from the 'here' QGeoServiceProider, therefore + as part of the saving process an alternative identifier attribute, x_id_here, is set for the place saved into the destination manager + (when QPlaceManager::compatiblePlace() is called) + + \input place-crossref.qdocinc + + In order to perform the matching, we create a QPlaceMatchRequest and assign it the search results from the origin manager. + The QPlaceMatchRequest will be used on the destination manager to return corresponding places. We also specify + matching parameters which are key value pairs. As mentioned previously, this can vary depending on the manager but typically + the key is QPlaceMatchRequest::AlternativeId to indicate we are matching by alternative id, the value in this case would be + x_id_here which specifies which alternative identifier attribute we are using to do the matching. + + \snippet places/requesthandler.h Match places + \dots + \dots + \snippet places/requesthandler.h Match places handler + + \section1 Classes in Places + + \section2 Data Classes + \annotatedlist QtLocation-places-data + + \section2 Request Classes + \annotatedlist QtLocation-places-requests + + \target Places Reply Classes + \section2 Reply classes + \annotatedlist QtLocation-places-replies + + \section2 Manager Classes + \annotatedlist QtLocation-places-manager +*/ + diff --git a/src/location/doc/src/plugins/esri.qdoc b/src/location/doc/src/plugins/esri.qdoc new file mode 100644 index 0000000..2707ca5 --- /dev/null +++ b/src/location/doc/src/plugins/esri.qdoc @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-plugin-esri.html +\title Qt Location Esri Plugin +\ingroup QtLocation-plugins + +\brief Uses Esri for location services. + +\section1 Overview + +This geo services plugin allows applications to access +\l {http://www.esri.com}{Esri} location based services using the Qt Location API. +The use of these services is governed by the \l {http://www.esri.com/legal/terms-use}{Esri terms of use}. + +Data is provided by \l {http://www.esri.com/data/find-data}{many different content providers}. + +The Esri geoservices plugin can be loaded by using the plugin key "esri". + +To use the services provided by the Esri platform, a subscription is required. There are two kinds of subscriptions: + +\list + \li \l {https://developers.arcgis.com/plans/}{Developer Subscription} + \li \l {http://www.arcgis.com/features/plans/pricing.html}{ArcGIS Online Subscription} +\endlist + +The Developer subscription offers a free-of-charge option for developing and testing your applications. +With this, your applications can go to production under the following conditions: + +\list + \li The app can request up to one million maps per month, and one million geocodes per month. + \li The app does not directly generate revenue. That is, end users must be able to obtain it and to use it for free. +\endlist + +If the above conditions cannot be met, the ArcGIS Online subscription is the correct choice, as it +gives applications full access to the ArcGIS platform. + +\section1 Parameters + +\section2 Mandatory parameters + +\table +\header + \li Parameter + \li Description +\row + \li esri.token + \li Security token for using routing services only. Mapping and geocoding services do not require a token. +\endtable + +To use the routing services hosted on ArcGIS Online with the Esri plugin, a token is required. +You can \l{https://developers.arcgis.com/authentication/accessing-arcgis-online-services/#registering-your-application}{obtain a token for testing purposes}, +or you can sign up for an \l {http://www.arcgis.com/features/plans/pricing.html}{ArcGIS Online subscription}. + +\section2 Optional parameters + +\table +\header + \li Parameter + \li Description +\row + \li esri.useragent + \li User agent string sent when making network requests +\row + \li esri.mapping.minimumZoomLevel + \li The minimum zoom level [double] at which the map is displayed +\row + \li esri.mapping.maximumZoomLevel + \li The maximum level [double] at which the map is displayed +\row + \li esri.mapping.cache.directory + \li Absolute path to map tile cache directory used as network disk cache. + + The default place for the cache is the \c{QtLocation/esri} subdirectory in the location returned by + QStandardPaths::writableLocation(), called with QStandardPaths::GenericCacheLocation as a parameter. + On systems that have no concept of a shared cache, the application-specific \l{QStandardPaths::CacheLocation} is used instead. +\row + \li esri.mapping.cache.disk.cost_strategy + \li The cost strategy to use to cache map tiles on disk. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b esri.mapping.cache.disk.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li esri.mapping.cache.disk.size + \li Disk cache size for map tiles. The default size of the cache is 50 MiB when \b bytesize is the cost + strategy for this cache, or 1000 tiles, when \b unitary is the cost strategy. +\row + \li esri.mapping.cache.memory.cost_strategy + \li The cost strategy to use to cache map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b esri.mapping.cache.memory.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li esri.mapping.cache.memory.size + \li Memory cache size for map tiles. The default size of the cache is 3 MiB when \b bytesize is the cost + strategy for this cache, or 100 tiles, when \b unitary is the cost strategy. +\row + \li esri.mapping.cache.texture.cost_strategy + \li The cost strategy to use to cache decompressed map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b esri.mapping.cache.texture.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li esri.mapping.cache.texture.size + \li Texture cache size for map tiles. The default size of the cache is 6 MiB when \b bytesize is the cost + strategy for this cache, or 30 tiles, when \b unitary is the cost strategy. + Note that the texture cache has a hard minimum size which depends on the size of the map viewport + (it must contain enough data to display the tiles currently visible on the display). + This value is the amount of cache to be used in addition to the bare minimum. +\row + \li esri.mapping.prefetching_style + \li This parameter allows to provide a hint how tile prefetching is to be performed by the engine. The default value, + \tt{TwoNeighbourLayers}, makes the engine prefetch tiles for the layer above and the one below the current tile + layer, providing ready tiles when zooming in or out from the current zoom level. + \tt{OneNeighbourLayer} only prefetches the one layer closest to the current zoom level. + Finally, \tt{NoPrefetching} allows to disable the prefetching, so only tiles that are visible will be fetched. + Note that, depending on the active map type, this hint might be ignored. +\endtable + +\section2 Directions language + +The service supports generating directions in the following languages: + +\table +\header + \li Language + \li Description +\row + \li ar + \li Generate directions in Arabic +\row + \li cs + \li Generate directions in Czech +\row + \li de + \li Generate directions in German +\row + \li el + \li Generate directions in Greek +\row + \li en + \li (default) Generate directions in English +\row + \li es + \li Generate directions in Spanish +\row + \li et + \li Generate directions in Estonian +\row + \li fr + \li Generate directions in French +\row + \li he + \li Generate directions in Hebrew +\row + \li it + \li Generate directions in Italian +\row + \li ja + \li Generate directions in Japanese +\row + \li ko + \li Generate directions in Korean +\row + \li lt + \li Generate directions in Lithuanian +\row + \li lv + \li Generate directions in Latvian +\row + \li nl + \li Generate directions in Dutch +\row + \li pl + \li Generate directions in Polish +\row + \li pt-BR + \li Generate directions in Brazilian Portuguese +\row + \li pt-PT + \li Generate directions in Portuguese (Portugal) +\row + \li ru + \li Generate directions in Russian +\row + \li sv + \li Generate directions in Swedish +\row + \li tr + \li Generate directions in Turkish +\row + \li zh-CN + \li Simplified Chinese +\endtable + +If only unsupported language codes are specified via the \l{http://doc.qt.io/qt-5/qml-qtlocation-plugin.html#locales-prop} +{locales} property, the service returns the directions using the default language, English. + +\section2 Directions length units + +QLocale::MetricSystem (default) use meters, other values (QLocale::ImperialUSSystem, QLocale::ImperialUSSystem) use feet. + +*/ diff --git a/src/location/doc/src/plugins/itemsoverlay.qdoc b/src/location/doc/src/plugins/itemsoverlay.qdoc new file mode 100644 index 0000000..ddb4de9 --- /dev/null +++ b/src/location/doc/src/plugins/itemsoverlay.qdoc @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-plugin-itemsoverlay.html +\title Qt Location Items Overlay Plugin +\ingroup QtLocation-plugins + +\brief Provides an empty map intended to be used as background for an overlay layers for map items. + +\section1 Overview + +This geo services plugin is a special plugin providing only an empty map. +This plugin provides no services, and is intended to be used in those cases where a \l Map element should +only show the added map items. + +The Items Overlay geo services plugin can be loaded by using the plugin key "itemsoverlay". + +\section1 Example usage + +The following snippet shows how a Map using this plugin can be added as an overlay to display +anti-aliased map items. +Note that for such an overlay Map to be transparent, it is also necessary to set its color to +a transparent one, such as \b transparent, like in the example. + + \qml + Window { + id: win + visible: true + width: 640 + height: 640 + + Map { + id: mapBase + gesture.enabled: true + anchors.fill: parent + plugin: Plugin { name: "osm" } + center: QtPositioning.coordinate(45,10) + zoomLevel: 4 + z: parent.z + 1 + } + + Map { + id: mapOverlay + anchors.fill: parent + plugin: Plugin { name: "itemsoverlay" } + gesture.enabled: false + center: mapBase.center + color: 'transparent' // Necessary to make this map transparent + minimumFieldOfView: mapBase.minimumFieldOfView + maximumFieldOfView: mapBase.maximumFieldOfView + minimumTilt: mapBase.minimumTilt + maximumTilt: mapBase.maximumTilt + minimumZoomLevel: mapBase.minimumZoomLevel + maximumZoomLevel: mapBase.maximumZoomLevel + zoomLevel: mapBase.zoomLevel + tilt: mapBase.tilt; + bearing: mapBase.bearing + fieldOfView: mapBase.fieldOfView + z: mapBase.z + 1 + + MapCircle { + id: circle + center: QtPositioning.coordinate(44, 10) + radius: 200000 + border.width: 5 + + MouseArea { + anchors.fill: parent + drag.target: parent + } + } + + // The code below enables SSAA + layer.enabled: true + layer.smooth: true + property int w : mapOverlay.width + property int h : mapOverlay.height + property int pr: Screen.devicePixelRatio + layer.textureSize: Qt.size(w * 2 * pr, h * 2 * pr) + } + } + \endqml + +*/ diff --git a/src/location/doc/src/plugins/mapbox.qdoc b/src/location/doc/src/plugins/mapbox.qdoc new file mode 100644 index 0000000..3933b10 --- /dev/null +++ b/src/location/doc/src/plugins/mapbox.qdoc @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-plugin-mapbox.html +\title Qt Location Mapbox Plugin +\ingroup QtLocation-plugins + +\brief Uses Mapbox for location services. + +\section1 Overview + +This geo services plugin allows applications to access +\l {http://mapbox.com}{Mapbox} location based services using the Qt Location API. +The use of these services is governed by the \l {https://www.mapbox.com/tos}{Mapbox terms of service}. +An access token is required to use these services. +Data is provided by \l {https://www.mapbox.com/about/maps}{OpenStreetMap and others}. + +The Mapbox geo services plugin can be loaded by using the plugin key "mapbox". + +\section1 Parameters + +\section2 Mandatory parameters +The following table lists mandatory parameters that \e must be passed to the Mapbox plugin. +\table +\header + \li Parameter + \li Description +\row + \li mapbox.access_token + \li \l{https://www.mapbox.com/help/define-access-token/}{Access token} provided by Mapbox. +\endtable + +The Mapbox geo services plugin requires an access token and map ID to use the +Mapbox services. To create a Mapbox account visit +\l{https://www.mapbox.com/#signup}. + +\section2 Optional parameters +The following table lists optional parameters that can be passed to the Mapbox plugin. +\table +\header + \li Parameter + \li Description +\row + \li mapbox.enterprise + \li Boolean representing whether the access token comes from a + \l{https://www.mapbox.com/enterprise}{Mapbox Enterprise} account. +\row + \li mapbox.mapping.map_id, mapbox.map_id (\b deprecated) + \li \l{https://www.mapbox.com/help/define-map-id/}{ID} of the Mapbox map to show. An example ID is "examples.map-zr0njcqy". + If this parameter is present, the specified map type will be used by default, unless another is selected. + If not present, the default Mapbox map ID is "mapbox.streets". + \b{Note:} neither in this parameter nor in \b{mapbox.mapping.additional_map_ids} it is allowed to use repeated map_ids. + This includes the map_ids bundled in the plugin by default (documented \l{https://www.mapbox.com/api-documentation/#maps}{here} under + \b{Mapbox classic map IDs}). Failing to do so will cause tile cache corruption. +\row + \li mapbox.mapping.additional_map_ids + \li Additional, comma separated, Mapbox map IDs to be added to the available map types. +\row + \li mapbox.mapping.format, mapbox.format (\b deprecated) + \li Data format to download tiles in, available values are "png", "png32", + "png64", "png128", "png256", (PNG with full, 32, 64, 128 and 256 color palette) + "jpg70", "jpg80", "jpg90" (JPEG with 70%, 80% and 90% compression). + Defaults to "png". +\row + \li mapbox.mapping.highdpi_tiles + \li Whether or not to request high dpi tiles. Valid values are \b true and \b false. The default value is \b false. +\row + \li useragent + \li User agent string set when making network requests. +\row + \li mapbox.mapping.cache.directory + \li Absolute path to map tile cache directory used as network disk cache. + + The default place for the cache is the \c{QtLocation/mapbox} subdirectory in the location returned by + QStandardPaths::writableLocation(), called with QStandardPaths::GenericCacheLocation as a parameter. + On systems that have no concept of a shared cache, the application-specific \l{QStandardPaths::CacheLocation} + is used instead. +\row + \li mapbox.mapping.cache.disk.cost_strategy + \li The cost strategy to use to cache map tiles on disk. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b mapbox.mapping.cache.disk.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b unitary. +\row + \li mapbox.mapping.cache.disk.size + \li Disk cache size for map tiles. + The default size of this cache is 6000 if \b unitary is used as cost strategy, + or 50 MiB, if \b bytesize is used as cost strategy. + Note that 6000 is the maximum amount of tiles that the Mapbox free plan allows to cache. + Make sure to comply with Mapbox Terms of Service before increasing this value. +\row + \li mapbox.mapping.cache.memory.cost_strategy + \li The cost strategy to use to cache map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b mapbox.mapping.cache.memory.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li mapbox.mapping.cache.memory.size + \li Memory cache size for map tiles. + The Default size of this cache is 100 if \b unitary is used as cost strategy, or + 3 MiB, if \b bytesize is used as cost strategy. +\row + \li mapbox.mapping.cache.texture.cost_strategy + \li The cost strategy to use to cache decompressed map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b mapbox.mapping.cache.texture.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li mapbox.mapping.cache.texture.size + \li Texture cache size for map tiles. + The Default size of this cache is 30 if \b unitary is used as cost strategy, or + 6 MiB, if \b bytesize is used as cost strategy. + Note that the texture cache has a hard minimum size which depends on the size of the map + viewport (it must contain enough data to display the tiles currently visible on the + display). + This value is the amount of tiles to be cached in addition to the bare minimum. +\row + \li mapbox.mapping.prefetching_style + \li This parameter allows to provide a hint how tile prefetching is to be performed by the engine. The default value, + \tt{TwoNeighbourLayers}, makes the engine prefetch tiles for the layer above and the one below the current tile + layer, providing ready tiles when zooming in or out from the current zoom level. + \tt{OneNeighbourLayer} only prefetches the one layer closest to the current zoom level. + Finally, \tt{NoPrefetching} allows to disable the prefetching, so only tiles that are visible will be fetched. + Note that, depending on the active map type, this hint might be ignored. +\row + \li mapbox.routing.use_mapbox_text_instructions + \li Whether to use the instruction text that came with the response from the server (true) or the + text generated by the plugin. The default value is true. + Note that if instructions in a language that is not directly supported by Mapbox are needed (see + \l{https://www.mapbox.com/api-documentation/#instructions-languages}{here} for the supported languages), + it is possible to use the \l{Qt Linguist} to translate QtLocation to the desired language, and set this parameter to + false in order to use the translated built-in instructions. +\endtable + +\section1 Extra routing attributes + +When using this plugin, the RouteManeuver objects in the returned route may contain additional extended attributes (see \l RouteManeuver::extendedAttributes), +where available. +These attributes are described in detail in the official \l{https://www.mapbox.com/api-documentation/#stepmaneuver-object}{Mapbox direction API documentation}. +*/ diff --git a/src/location/doc/src/plugins/mapboxgl.qdoc b/src/location/doc/src/plugins/mapboxgl.qdoc new file mode 100644 index 0000000..51b49f9 --- /dev/null +++ b/src/location/doc/src/plugins/mapboxgl.qdoc @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Copyright (C) 2014 Canonical Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-plugin-mapboxgl.html +\title Qt Location Mapbox GL Plugin +\ingroup QtLocation-plugins + +\brief Uses Mapbox GL for location services. + +\section1 Overview + +This geo services plugin allows applications to access +\l {http://mapbox.com}{Mapbox} mapping services using the Qt Location API. +The use of these services is governed by the \l {https://www.mapbox.com/tos}{Mapbox terms of service}. + +This plugin differs from the \l {http://doc.qt.io/qt-5/location-plugin-mapbox.html}{Mapbox} plugin because it uses +the \l {https://github.com/mapbox/mapbox-gl-native/} {Mapbox GL map engine} for rendering both raster tiles and \l +{https://www.mapbox.com/vector-tiles} {vector tiles} in real-time. The benefits are: text staying upright, font +antialiasing, labels flowing between zoom levels, smooth pan, tilt, rotation and continuous zoom. + +The appearance and behavior of vector maps can be customized by creating custom map styles. This can be +done with tools like \l {https://www.mapbox.com/studio} {Mapbox Studio}. + +The Mapbox GL geo services plugin can be loaded by using the plugin key "mapboxgl". + +Both Mapbox geo services plugins require an access token to access map styles +and tiles hosted by Mapbox. To create a Mapbox account visit \l{https://www.mapbox.com/pricing}. + +\b {Note:} the API for this plugin is introduced in Qt 5.9 as \e {technology preview}. + +\section1 Platform Support + +Qt Location Mapbox GL Plugin has the following support for platforms: + +\list + \li Microsoft Windows (win32) - Supported, requires MinGW 5.0+ + \li Linux X11 - Supported, requires GCC 4.9+ + \li macOS - Supported + \li Android - Supported + \li Embedded Linux - Supported, requires GCC 4.9+ + \li iOS - Supported + \li WinRT - Not supported +\endlist + +\section1 Parameters + +\section2 Optional plugin parameters + +The following table lists optional parameters that can be passed to the Mapbox plugin. + +\table +\header + \li Parameter + \li Description +\row + \li mapboxgl.access_token + \li \l{https://www.mapbox.com/help/define-access-token}{Access token} provided by Mapbox. + The token can also be specified using the environment variable \c{MAPBOX_ACCESS_TOKEN}, but if also + set using a plugin parameter, then this last one will have the precedence over the environment variable. + When not set, a development token will be used by default. The development token is subject to the Mapbox + \l{https://www.mapbox.com/tos}{Terms of Services} and must not be used in production. This property has + no effect on styles hosted outside the Mapbox servers. +\row + \li mapboxgl.mapping.additional_style_urls + \li Additional, comma separated, Mapbox \l{https://www.mapbox.com/help/define-style-url} + {style URLs} to be added to the available style URLs. Additional styles will be prepended to + the \l[QML]{QtLocation::Map::}{supportedMapTypes} property of the \l{QtLocation::Map}{Map} item. +\row + \li mapboxgl.mapping.cache.directory + \li Absolute path to map tile cache directory used as network disk cache. + + The default place for the cache is the \c{QtLocation/mapboxgl} subdirectory in the location returned by + QStandardPaths::writableLocation(), called with QStandardPaths::GenericCacheLocation as a parameter. + On systems that have no concept of a shared cache, the application-specific \l{QStandardPaths::CacheLocation} + is used instead. + + This is an \l {https://www.mapbox.com/help/mobile-offline/#ambient-caching} {ambient cache}, meaning it will + get populated on the fly until it reaches the size limit, and when that happens, it will evict the least used + tiles. + + This cache can also be used for storing \l {https://www.mapbox.com/help/mobile-offline}{offline tiles}, + but the offline database must be populated using the \l {https://github.com/mapbox/mapbox-gl-native/blob/master/bin/offline.cpp} + {offline tool}. The offline database will work alongside with the ambient cache in the same file. + Make sure to comply with Mapbox Terms of Service before creating an offline database. + + \b {Note:} The map tile cache file name must be "mapboxgl.db". When using the offline tool, the default + output is "offline.db". For using the generated output from the offline tool, you must move that to the + proper directory, and rename it as "mapboxgl.db". The offline tool also provides the "--output" + parameter for specifying the name of the generated output. + +\row + \li mapboxgl.mapping.cache.memory + \li Whether or not the cache should be in-memory only. Valid values are \b true and \b false. The default + value is \b false. When set to \b true, the disk cache is never created. The ambient cache will work in-memory, + but the offline database cannot be used with this option enabled. +\row + \li mapboxgl.mapping.cache.size + \li Cache size for map resources in bytes. + The default size of this cache is 50 MiB. + Make sure to comply with Mapbox Terms of Service before increasing this value. +\row + \li mapboxgl.mapping.use_fbo + \li Sets whether to use a framebuffer object to render Mapbox GL Native. + Valid values are \b true and \b false. The default value is \b true. When + set to \b false, the map is rendered issuing OpenGL commands directly, + through a QSGRenderNode, to improve the rendering performance. This mode is + experimental, and it does not support QQuickItem transformations nor stencil + clipping. It might be also produce rendering artifacts e.g. when adding it + inside a \l{QtQuick::Flipable}{Flipable} item. +\row + \li mapboxgl.mapping.items.insert_before + \li Some map items such as \l{QtLocation::MapPolyline}{MapPolyline}, + \l{QtLocation::MapPolygon}{MapPolygon} and \l{QtLocation::MapRectangle}{MapRectangle} + will be rendered after the topmost \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#layers}{layer} + of the style. With this parameter set, the map items will be rendered \b before the layer ID + specified, unless the layer is not present on the current style, which will fallback + to the default behavior. This parameter can be used to display route lines under labels. +\endtable + +\section2 Optional map parameters + +The \l{QtLocation::Map}{Map} item using this plugin, can also be customized using \l{QtLocation::MapParameter}{MapParameters}, +allowing runtime changes to the map style and data. + +Examples of what can be currently controlled using \l{QtLocation::MapParameter}{MapParameter} are: + +\list + \li Hide and show parts of the map, like roads and buildings. + \li Change paint properties like color and opacity of various parts of the map. + \li Change layout properties like thickness and line joints. + \li Add data to the map. + \li Add sprites to the map. +\endlist + +With the exception of extrusion and data driven style properties, every property described at the +\l {https://www.mapbox.com/mapbox-gl-js/style-spec/}{Mapbox Style Specification} can be changed at runtime. + +The \l{QtLocation::MapParameter}{MapParameters}, used to control the style of the map at runtime, always +have a \b type property, followed by user defined properties that try to match the +\l {https://www.mapbox.com/mapbox-gl-js/style-spec/}{Mapbox Style Specification} naming as much as possible, +replacing the dash with camel case for technical reasons (i.e. \b line-cap will be translated to \b lineCap). + +\table +\header + \li Parameter type + \li Description +\row + \li source + \li A style data \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#sources}{source}. When using a source + of \b sourceType \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#sources-geojson}{geojson}, the + \b data property can be both inlined or sourced from qrc. + \li Supported source types are: \b vector, \b raster, \b raster-dem and \b geojson. +'geojson'. +\row + \li layer + \li Adds a new \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#layers}{style layer} to the map. On a Mapbox GL map, + layers are used in styles for adding styling rules to specific subsets of data. A layer will contain a reference to the + data for which they are defining a style. Use the \b before attribute to insert a layer before an existing layer. +\row + \li paint + \li Defines how a layer will be painted. \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#layer-paint}{Paint} properties + can be used for changing the color and opacity of roads in the map or the land color, among other things. +\row + \li layout + \li Defines how a layer will be layouted. \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#layer-layout}{Layout} properties + can be used for setting a layer's visibility, and for defining the spacing between dashes in a dashed line, among other things. +\row + \li image + \li Adds a \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#root-sprite}{sprite} to the map to be used by a style layer. + This property is required if any style layer uses the properties such as \b backgroundPattern, \b fillPattern, \b linePattern, + or \b iconImage. +\row + \li filter + \li A \l {https://www.mapbox.com/mapbox-gl-js/style-spec/#types-filter}{filter} selects specific features from a layer. This can + be used for adding a layer from a GeoJSON source based on specific parts of the data source, like by using only the points + in the GeoJSON. +\endtable + +\section1 Example usage + +This example adds inline GeoJSON data to the map. Simply adding a \b source is not enough to get the data +visible. It is also necessary to create a \b layer based on this source. After the layer is added, we also need +to style its \b paint and \b layout properties. In this case we are changing the line color to blue, and the line +width to 8 pixels, as well as also setting round line joints and caps. + +\code +Map { + plugin: Plugin { name: "mapboxgl" } + + center: QtPositioning.coordinate(60.170448, 24.942046) // Helsinki + zoomLevel: 12 + + MapParameter { + type: "source" + + property var name: "routeSource" + property var sourceType: "geojson" + property var data: '{ "type": "FeatureCollection", "features": \ + [{ "type": "Feature", "properties": {}, "geometry": { \ + "type": "LineString", "coordinates": [[ 24.934938848018646, \ + 60.16830257086771 ], [ 24.943315386772156, 60.16227776476442 ]]}}]}' + } + + MapParameter { + type: "layer" + + property var name: "route" + property var layerType: "line" + property var source: "routeSource" + + // Draw under the first road label layer + // of the mapbox-streets style. + property var before: "road-label-small" + } + + MapParameter { + type: "paint" + + property var layer: "route" + property var lineColor: "blue" + property var lineWidth: 8.0 + } + + MapParameter { + type: "layout" + + property var layer: "route" + property var lineJoin: "round" + property var lineCap: "round" + } +} +\endcode + +Note that the order we add the parameters is important, because there is dependency between the +parameters. Adding a layer before adding a source will create an invalid layer, same goes for +adding a paint property for a layer that doesn't exist. + +Paint and layout properties can also be used for styling existing layers of the current style, and +not only layers created at runtime. \l {https://www.mapbox.com/studio/}{Mapbox Studio} can be used +for inspecting layers of a given style. +*/ diff --git a/src/location/doc/src/plugins/nokia.qdoc b/src/location/doc/src/plugins/nokia.qdoc new file mode 100644 index 0000000..79bd728 --- /dev/null +++ b/src/location/doc/src/plugins/nokia.qdoc @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-plugin-here.html +\title Qt Location HERE Plugin +\ingroup QtLocation-plugins + +\brief Uses the relevant services provided by HERE. + +\section1 Overview + +Included with Qt Location is a geo services plugin which accesses the relevant HERE services +provided by HERE/Nokia. The use of these services is governed by the terms and conditions +available at \l {https://developer.here.com/terms-conditions}. + +Note that accepting the terms and conditions only applies those terms and conditions to the use of +the HERE geo services plugin and does not limit the use of the other geo services plugins that may +be included with Qt. + +The HERE geo services plugin can be loaded by using the plugin key "here". + +The online plugin uses the tiled map classes, which caches tile data in heap memory and texture +memory. + +\section1 Parameters + +\section2 Mandatory parameters +The following table lists mandatory parameters that \e must be passed to the HERE plugin. +\table +\header + \li Parameter + \li Description +\row + \li here.app_id + \li Client \e app_id part of the app_id/token pair used for authentication by all managers. +\row + \li here.token + \li Client \e token part of the app_id/token pair for the service used for authentication by all managers. +\endtable + +The HERE geo services plugin requires an application id and token pair to authenticate the +application with the HERE services. To obtain an application id and token pair visit +\l{https://developer.here.com/} + +\section2 Optional parameters +The following table lists optional parameters that can be passed to the HERE plugin. + +\note Since Qt 5.5 all parameters below must be prefixed with \c here. Previous versions did not require +a prefix. + +\table +\header + \li Parameter + \li Description +\row + \li here.proxy + \li Proxy server URL used by all managers. For usage of the system proxy just pass "system" as value. + + \note See the notes in \l{QNetworkProxyFactory::systemProxyForQuery()} for further information. +\row + \li here.mapping.host + \li Base map tile service URL used by mapping manager. +\row + \li here.mapping.host.aerial + \li Aerial map tile service URL used by mapping manager. For all satellite, hybrid and terrain schemes. +\row + \li here.mapping.cache.directory + \li Absolute path to map tile cache directory used as network disk cache. + + The default place for the cache is the \c{QtLocation/here} subdirectory in the location returned by + QStandardPaths::writableLocation(), called with QStandardPaths::GenericCacheLocation as a parameter. + On systems that have no concept of a shared cache, the application-specific \l{QStandardPaths::CacheLocation} is used instead. + +\row + \li here.mapping.cache.disk.cost_strategy + \li The cost strategy to use to cache map tiles on disk. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b here.mapping.cache.disk.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li here.mapping.cache.disk.size + \li Disk cache size for map tiles. The default size of the cache is 50 MiB when \b bytesize is the cost + strategy for this cache, or 1000 tiles, when \b unitary is the cost strategy. +\row + \li here.mapping.cache.memory.cost_strategy + \li The cost strategy to use to cache map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b here.mapping.cache.memory.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li here.mapping.cache.memory.size + \li Memory cache size for map tiles. The default size of the cache is 3 MiB when \b bytesize is the cost + strategy for this cache, or 100 tiles, when \b unitary is the cost strategy. +\row + \li here.mapping.cache.texture.cost_strategy + \li The cost strategy to use to cache decompressed map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b here.mapping.cache.texture.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li here.mapping.cache.texture.size + \li Texture cache size for map tiles. The default size of the cache is 6 MiB when \b bytesize is the cost + strategy for this cache, or 30 tiles, when \b unitary is the cost strategy. + Note that the texture cache has a hard minimum size which depends on the size of the map viewport + (it must contain enough data to display the tiles currently visible on the display). + This value is the amount of cache to be used in addition to the bare minimum. +\row + \li here.mapping.prefetching_style + \li This parameter allows to provide a hint how tile prefetching is to be performed by the engine. The default value, + \tt{TwoNeighbourLayers}, makes the engine prefetch tiles for the layer above and the one below the current tile + layer, providing ready tiles when zooming in or out from the current zoom level. + \tt{OneNeighbourLayer} only prefetches the one layer closest to the current zoom level. + Finally, \tt{NoPrefetching} allows to disable the prefetching, so only tiles that are visible will be fetched. + Note that, depending on the active map type, this hint might be ignored. +\row + \li here.mapping.highdpi_tiles + \li Whether or not to request high dpi tiles. Valid values are \b true and \b false. The default value is \b false. +\row + \li here.geocoding.host + \li Geocoding service URL used by geocoding manager. +\row + \li here.routing.host + \li Routing service URL used by routing manager. +\row + \li here.places.host + \li Search service URL used by search manager. +\row + \li here.places.api_version + \li Version of the REST API used by the places manager. Currently versions 1 and 2 are + supported. The version 1 is deprecated and will not be part of the final Qt release. The default is version 2. +\endtable + +\section1 Parameter Usage Example + +The following two examples show how to create a HERE plugin instance with +parameters supplied for an application id and token, which is required for +authentication. + +\section2 QML + +\code +Plugin { + name: "here" + PluginParameter { name: "here.app_id"; value: "myapp" } + PluginParameter { name: "here.token"; value: "abcdefg12345" } +} +\endcode + +\section2 C++ + +\code +QMap params; +params["here.app_id"] = "myapp"; +params["here.token"] = "abcdefg12345"; + +QGeoServiceProvider *gsp = new QGeoServiceProvider("here", params); +\endcode + +\section1 Places +The HERE provider remotely accesses places (read-only) from a REST based server. The specific capabilities +and behaviours are outlined below: + +\section2 Capabilities +\table + \row + \li Storage + \li remote + \row + \li Read/Write + \li read-only + \row + \li Icons + \li yes + \row + \li Search term suggestions + \li yes + \row + \li Recommendations + \li yes + \row + \li Category structure + \li Hierarchical + \row + \li (Rich) Content images + \li yes + \row + \li (Rich) Content reviews + \li yes + \row + \li (Rich) Content editorials + \li yes + \row + \li All details fetched during search + \li no + \row + \li Paging offset index + \li no + \row + \li Paging limit + \li yes + \row + \li Distance relevance hint + \li no + \row + \li Lexical name relevance hint + \li no + \row + \li Extended Attributes + \li yes + \row + \li Notifications for added/removed places/categories + \li no + \row + \li visibility scopes + \li public + \row + \li favorites matching/(usable as favoritesPlugin) + \li no +\endtable + +\section2 Plugin Specific Behaviors and Limitations. +\section3 Search +The following list shows what core place data is returned during a place search: +\list +\li name +\li location +\li contact information +\li attribution +\li categories +\li rating +\li visibility +\endlist + +The following list shows further details that may be retrieved +via QPlaceManager::getDetails() +\list +\li supplier +\li extended attributes +\endlist + +\section3 Searching for Places +\section4 Search Term and Categories +The HERE plugin supports searching with a \e {search term} and \e {category or categories}, however +both are not supported simultaneously. + +\list + \li Valid usage: \e {search term} + \e {search center} + \li Valid usage: \e {category} + \e {search center} + \li Invalid usage: \e {search term} + \e {category} + \e {search center} +\endlist + +This limitation applies when using the HERE plugin with \l PlaceSearchModel and QPlaceManager::search(). + +\section4 Search Area +The HERE plugin only supports provision of a \e {search center} when searching for places via \l PlaceSearchModel +and QPlaceManager::search(). A search center can be provided via a bounding circle, however the +radius should be kept at the default value of -1. Typically a developer should not have to set the radius at all. +If a developer sets a radius, it is ignored by the plugin and the boundaries are not honored. + +In a similar manner only the center of a bounding box is taken into consideration when searching. The boundaries +of the box are not honored. + +A search center \e {must} be provided for all searches. + +\section4 Relevancy Hints +The HERE plugin does not support relevancy hints. Any relevancy hints supplied to +a search request are consequently ignored. + +\section3 Search Term suggestions +Only a partial \e {search term} and \e {search center} is supported when retrieving suggestions. +This limitation applies when using the HERE plugin with the \l PlaceSearchSuggestionModel and QPlaceManager::searchSuggestions(). + +Both search term and search center \e {must} be provided when retrieving search term suggestions. + +\section3 Recommendations +Only a given \e {place identifier} is supported as a parameter for a recommendations. No other parameters +such as limit, offset, and search area are supported. This limitation applies when using the +HERE plugin with \l PlaceSearchModel and QPlaceManager::search(). + +\section3 Extended Attributes +The supported set of attributes provided by the HERE plugin are not fixed and +may grow over time. Also the attributes provided may vary according to a place +by place basis, e.g one place may provide opening hours while another does not. +At the time of writing, it is known that some places provide \c openingHours +(QPlaceAttribute::OpeningHours) and \c payment (QPlaceAttribute::Payment) +methods but other attributes may be made available by the backend server. All +places provided by the plugin will have the \c x_provider +(QPlaceAttribute::Provider) attribute set to \c here. + +\section3 Restrictions of Usage - ExtendedAttributes and Content +The extended attributes and rich content of places are not permitted +to be saved. For QML this is related to \l Place::extendedAttributes, \l ImageModel, +\l ReviewModel, and \l EditorialModel. For C++ this relates to QPlace::extendedAttribute(), +QPlace::content() and QPlaceManager::getPlaceContent(). + +(Note that the HERE plugin is a read-only source of places and +does not support saving functionality at all.) +*/ diff --git a/src/location/doc/src/plugins/osm.qdoc b/src/location/doc/src/plugins/osm.qdoc new file mode 100644 index 0000000..649d3be --- /dev/null +++ b/src/location/doc/src/plugins/osm.qdoc @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-plugin-osm.html +\title Qt Location Open Street Map Plugin +\ingroup QtLocation-plugins + +\brief Uses Open Street Map and related services. + +\section1 Overview + +This geo services plugin allows applications to access +\l {http://openstreetmap.org}{Open Street Map} location based services using the Qt Location API. + +Data, imagery and map information provided by \l {http://korona.geog.uni-heidelberg.de/}{OpenMapSurfer}, +\l {http://www.thunderforest.com/}{ThunderForest}, OpenStreetMap and contributors. The data is +available under the \l {http://www.opendatacommons.org/licenses/odbl}{Open Database License}. + +The Open Street Map geo services plugin can be loaded by using the plugin key "osm". + +\note Since Qt 5.6.2, the available map types offered by this plugin may change (or be removed) +without notice depending on the actual availability of a viable openly accessible provider for each type. +This also implies that providers serving tiles over HTTPS may be used. +This becomes relevant when using the OSM plugin on platforms, such as Android, for which SSL support is not built into Qt by default. +To prevent these changes, either a different geo service plugin should be used, or the plugin +parameter \e osm.mapping.providersrepository.address should be set to a user-specified repository, in order to take full control over +(and accept \b responsibility for) selecting the provider that is used for each map type. +Since Qt 5.9.6 the default nominatim endpoint, used for geocoding and places, has also changed to HTTPS-only. + +\section1 Parameters + +\section2 Optional parameters +The following table lists optional parameters that can be passed to the Open Street Map plugin. + +\note Since Qt 5.5 all parameters below must be prefixed with \c osm. Previous versions did not require +a prefix. + +\table +\header + \li Parameter + \li Description +\row + \li osm.geocoding.host + \li Url string set when making network requests to the geocoding server. This parameter should be set to a + valid server url with the correct osm API. If not specified the default \l {http://nominatim.openstreetmap.org/}{url} will be used. + \note The API documentation is available at \l {https://wiki.openstreetmap.org/wiki/Nominatim}{Project OSM Nominatim}. +\row + \li osm.mapping.cache.directory + \li Absolute path to map tile cache directory used as network disk cache. + + The default place for the cache is the \c{QtLocation/osm} subdirectory in the location returned by + QStandardPaths::writableLocation(), called with QStandardPaths::GenericCacheLocation as a parameter. + On systems that have no concept of a shared cache, the application-specific \l{QStandardPaths::CacheLocation} is used instead. +\row + \li osm.mapping.cache.disk.cost_strategy + \li The cost strategy to use to cache map tiles on disk. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b osm.mapping.cache.disk.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li osm.mapping.cache.disk.size + \li Disk cache size for map tiles. The default size of the cache is 50 MiB when \b bytesize is the cost + strategy for this cache, or 1000 tiles, when \b unitary is the cost strategy. +\row + \li osm.mapping.cache.memory.cost_strategy + \li The cost strategy to use to cache map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b osm.mapping.cache.memory.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li osm.mapping.cache.memory.size + \li Memory cache size for map tiles. The default size of the cache is 3 MiB when \b bytesize is the cost + strategy for this cache, or 100 tiles, when \b unitary is the cost strategy. +\row + \li osm.mapping.cache.texture.cost_strategy + \li The cost strategy to use to cache decompressed map tiles in memory. + Valid values are \b bytesize and \b unitary. + Using \b bytesize, the related size parameter (\b osm.mapping.cache.texture.size) will + be interpreted as bytes. + Using \b unitary, they will be interpreted as number of tiles. + The default value for this parameter is \b bytesize. +\row + \li osm.mapping.cache.texture.size + \li Texture cache size for map tiles. The default size of the cache is 6 MiB when \b bytesize is the cost + strategy for this cache, or 30 tiles, when \b unitary is the cost strategy. + Note that the texture cache has a hard minimum size which depends on the size of the map viewport + (it must contain enough data to display the tiles currently visible on the display). + This value is the amount of cache to be used in addition to the bare minimum. +\row + \li osm.mapping.custom.datacopyright + \li Custom data copryright string is used when setting the \l{Map::activeMapType} to \l{MapType}.CustomMap via urlprefix parameter. + This copyright will only be used when using the CustomMap from above. If empty no data copyright will be displayed for the custom map. +\row + \li osm.mapping.custom.host + \li Url string of a custom tile server. This parameter should be set to a valid server url offering the correct osm API. + To use this server, the \l{Map::activeMapType} parameter of the \l Map should be set to the supported map type + whose type is \l{MapType}.CustomMap. + This map type is only be available if this plugin parameter is set, in which case it is + always \l{Map::supportedMapTypes}[supportedMapTypes.length - 1]. + \note Setting the mapping.custom.host parameter to a new server renders the map tile cache useless for the old custommap style. +\row + \li osm.mapping.custom.mapcopyright + \li Custom map copryright string is used when setting the \l{Map::activeMapType} to \l{MapType}.CustomMap via urlprefix parameter. + This copyright will only be used when using the CustomMap from above. If empty no map copyright will be displayed for the custom map. +\row + \li osm.mapping.highdpi_tiles + \li Whether or not to request high dpi tiles. Valid values are \b true and \b false. The default value is \b false. + Please note that not all map types are available in high dpi. Setting this parameter to true might even have no effect if + no map type is available in high dpi at the moment. Provider information files for high dpi tiles are named + \tt{street-hires}, \tt{satellite-hires}, \tt{cycle-hires}, \tt{transit-hires}, \tt{night-transit-hires}, \tt{terrain-hires} and \tt{hiking-hires}. + These are fetched from the same location used for the low dpi counterparts. +\row + \li osm.mapping.offline.directory + \li Absolute path to a directory containing map tiles used as an offline storage. If specified, it will work together with the network disk cache, but tiles won't get automatically + inserted, removed or updated. The format of the tiles is the same used by the network disk cache. + There is no default value, and if this property is not set, no directory will be indexed and only the network disk cache will be used + to reduce network usage or to act as an offline storage for the currently cached tiles. +\row + \li osm.mapping.prefetching_style + \li This parameter allows to provide a hint how tile prefetching is to be performed by the engine. The default value, + \tt{TwoNeighbourLayers}, makes the engine prefetch tiles for the layer above and the one below the current tile + layer, providing ready tiles when zooming in or out from the current zoom level. + \tt{OneNeighbourLayer} only prefetches the one layer closest to the current zoom level. + Finally, \tt{NoPrefetching} allows to disable the prefetching, so only tiles that are visible will be fetched. + Note that, depending on the active map type, this hint might be ignored. +\row + \li osm.mapping.providersrepository.address + \li The OpenStreetMap plugin retrieves the provider's information from a remote repository. This is done to prevent using hardcoded + servers by default, which may become unavailable. By default this information is fetched from \l {http://maps-redirect.qt.io} {maps-redirect.qt.io}. + Setting this parameter changes the provider repository address to a user-specified one, which must contain the files + \tt{street}, \tt{satellite}, \tt{cycle}, \tt{transit}, \tt{night-transit}, \tt{terrain} and \tt{hiking}, each of which must contain valid provider information. +\row + \li osm.mapping.providersrepository.disabled + \li By default, the OpenStreetMap plugin retrieves the provider's information from a remote repository to avoid a loss of service due to unavailability of hardcoded services. + The plugin, however, still contains fallback hardcoded provider data, in case the provider repository becomes unreachable. + Setting this parameter to \b true makes the plugin use the hardcoded urls only and therefore prevents the plugin from fetching provider data from the remote repository. + +\row + \li osm.places.debug_query + \li Set this parameter to true to have an extended attribute in each result named "requestUrl", and containing the + url used for the query. Default is \b false. +\row + \li osm.places.host + \li Url string set when making network requests to the places server. + This parameter should be set to a valid server url with the correct osm API. + If not specified the default \l {http://nominatim.openstreetmap.org/search}{url} + will be used. + \note The API documentation is available at \l {https://wiki.openstreetmap.org/wiki/Nominatim}{Project OSM Nominatim}. +\row + \li osm.places.page_size + \li The amount of results in a page. Note that this value might be clamped server side. The typical maximum in standard + nominatim instances is 50. + +\row + \li osm.routing.apiversion + \li String defining the api version of the (custom) OSRM server. Valid values are \b{v4} and \b{v5}. The default is \b{v5}. + This parameter should be set only if \tt{osm.routing.host} is set, and is an OSRM v4 server. +\row + \li osm.routing.host + \li Url string set when making network requests to the routing server. This parameter should be set to a + valid server url with the correct osrm API. If not specified the default \l {http://router.project-osrm.org/route/v1/driving/}{url} will be used. + \note The API documentation and sources are available at \l {http://project-osrm.org/}{Project OSRM}. + +\row + \li osm.useragent + \li User agent string set when making network requests. This parameter should be set to a + value that uniquely identifies the application. Note that providers might block applications not setting this + parameter, leaving it to the stock plugin user agent (e.g., \l {http://wiki.openstreetmap.org/wiki/Nominatim_usage_policy}{Nominatim} + for geocoding) +\endtable + +\section1 Parameter Usage Example + +The following example shows how to create an OSM plugin instance with +parameters supplied for an useragent, and if necessary, a custom server url plus the corresponding copyright information for the tile provider. +Additionally, it is possible to choose another routing server than the public osrm one. + +\section2 QML + +\code +Plugin { + name: "osm" + PluginParameter { name: "osm.useragent"; value: "My great Qt OSM application" } + PluginParameter { name: "osm.mapping.host"; value: "http://osm.tile.server.address/" } + PluginParameter { name: "osm.mapping.copyright"; value: "All mine" } + PluginParameter { name: "osm.routing.host"; value: "http://osrm.server.address/viaroute" } + PluginParameter { name: "osm.geocoding.host"; value: "http://geocoding.server.address" } +} +\endcode + +\section1 Other Plugin-specific Information + +\section2 Tile cache + +The tiles are cached in a \c{QtLocation/osm} directory in \l {QStandardPaths::writableLocation()}{QStandardPaths::writableLocation} +(\l{QStandardPaths::GenericCacheLocation}). On systems that have no concept of a shared cache, the application-specific +\l{QStandardPaths::CacheLocation} is used instead. +*/ diff --git a/src/location/doc/src/plugins/places-backend.qdoc b/src/location/doc/src/plugins/places-backend.qdoc new file mode 100644 index 0000000..7b63ea9 --- /dev/null +++ b/src/location/doc/src/plugins/places-backend.qdoc @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-places-backend.html +\title Places Backend + +\brief The Places backend is responsible for managing a places datastore whether + it is located remotely or locally + +\section1 Overview + +The QPlaceManager interface, provided to clients to allow access to place information, +depends directly on an implementation of QPlaceManagerEngine. The engine provides +the backend function implementations which are called by the manager. + +A places backend implementer needs to derive from QPlaceManagerEngine and provide implementations +for the virtual functions relevant for their backend. Most of these functions are asynchronous and +so implementers will also need to derive the appropriate \l {Places Reply Classes}{reply classes}. +The reply objects are responsible for managing an asynchronous request; they are used to notify +when a request is complete and hold the results of that request. QPlaceManagerEngine provides a +default implementation for all virtual functions. The default implementations for the asynchronous +functions return a reply that will emit the error() and finished() signals at the next iteration +through the event loop. + +\section1 Implementing/Inheriting Reply Objects +A reply object would be inherited as follows: +\snippet places/requesthandler.h Implement reply pt1 +\dots +\snippet places/requesthandler.h Implement reply pt2 + +The implementation of a QPlaceManagerEngine must ensure that any signals emitted by the reply +objects are delayed until the request functions have returned and the application code has a chance +to connect those signals to slots. The typical approach is to use \l {QMetaObject::invokeMethod()} +with a \l {Qt::QueuedConnection} to emit the signals. + +\snippet places/requesthandler.h Trigger done + +Note that the \c finished signals should always be emitted when a reply is complete, even if +an error has been encountered, that is, if there is an error, both the \c error and \c finished signals +should be emitted while if there is no error, only the \c finished signals are emitted. + +The protected functions of QPlaceSearchReply::setResults() and QPlaceSearchReply::setRequest() +are made publicly accessible so the plugin can assign results and requests. Because +these functions are not publicly exported, accessibility is not so much of an issue. +An alternative would have been to declare a friend class in SearchReply. + +Typically the engine instance would be made the \c parent of the reply. If the developer +fails to discard the replies when finished, the engine can clean those upon destruction. +Commonly, the reply also has a pointer reference back to the engine, which may be used +to emit the QPlaceManagerEngine::finished() and QPlaceManagerEngine::error() signals. This is +just one of many ways the reply could be implemented. + +\section1 Icon URLs +Icon URLs are provided through the QPlaceManagerEngine::constructIconUrl() function. +The expected behaviour is that the engine will use the QPlaceIcon::parameters() +in order to construct an appropriate URL. When a QPlace object is returned +from the manager either from a search or a query to get place details, +it is expected the engine will correctly populate the parameters as necessary. + +The backend is free to choose what the parameter key and values are, however +if a backend only ever has one URL per icon it is recommended that the QPlaceIcon::SingleUrl +be used as the key. + +\section1 Categories +The categories of a manager engine are relatively static entities; for engines accessing +remote place datastores it may be desirable to cache the category structure rather than +querying a server every time QPlaceManagerEngine::initializeCategories() is called. +Depending on how dynamic the categories are, always downloading the freshest +set of categories may be more appropriate. + +\section1 Saving Places to the Manager +A place generally cannot be saved directly between managers as is because it contains manager specific data such as icons +and categories. In order to facilitate saving to one's own manager, engine implementers should implement +the QPlaceManagerEngine::compatiblePlace() function. This function returns a copy of the input place +with properties pruned or modified as necessary such that the copy can be saved into manager. + +Construction of a compatible place may involve ignoring certain properties from the +original place, for example if contact details are not supported, these are left out of the +compatible place. Other times it may involve modifying certain properties, for example +modifying the icon parameters to facilitate copying or downloading of the original +place's icon to a location that the backend can access. + +\section1 Cross-Referencing Places Between Managers +Sometimes a situation may arise where we wish to cross-reference and match places between managers. +Such a situation may arise where one manager provides read-only access to places (origin manager), while another second r/w +manager (destination manager) is used to save selected favorites from the first. During a search of the origin manager, we may want to +know which ones have been 'favorited' into the destination manager and perhaps display the customized favorite name +rather than the original name. + +\section2 Alternative Identifier Cross-Referencing +In order to accomplish cross-referencing, there needs to be a link between the original place and the favorited place +and this is typically handled via an alternative identifier attribute. The favorited place contains an alternative identifier attribute which has the identifier of the original place. + +\include place-crossref.qdocinc + +There are 3 prerequisites for implementing cross-referencing by alternative identifier. The first is that the origin manager must provide the x_provider attribute +with the value being the name of the manager's QGeoServiceProvider. The attribute label should be kept empty, indicating the attribute should not +be displayed to users. \note It is generally expected that all managers should set the \c x_provider attribute. + +The second is that QPlaceManager::compatiblePlace() of the destination manager use the \c x_provider attribute of the initial place +and set an alternative identifier attribute of the place to be saved. The key of the alternative identifier attribute is \c x_id_ and +the text value is the identifier of the initial place. The \c x_provider attribute should not be passed to the compatible place. When +it is saved, the x_provider of the saved place is considered to be the destination manager. + +The third is that QPlaceManager::matchingPlaces() of the destination manager accept the QPlaceMatchRequest::AlternativeId as a parameter key +and the alternative identifier attribute key as the value, in this case \c x_id_ would be the expected value. +This indicates that the identifiers of places in the QPlaceMatchRequest should be matched against the \c x_id_ alternative identifier attributes. + +Note that if the destination manager is to facilitate saving and cross-referencing from any arbitrary manager, it internally must +accommodate saving of arbitrary key value pairs since we cannot know the provider names before hand, nor can we know what structure the +ids will be. + +\section3 Other Methods of Linking +If an origin manager does not supply a place id, it may be necessary to provide some other means of cross-referencing/matching. +One approach might be to do so via the place coordinates, if the coordinate of a place in the origin manager is identical or close +to a place in the destination manager, there is a high likelihood that they are the same place. +In this case, the manager might implement QPlaceManager::matchingPlaces() to accept a QPlaceMatchRequest with a parameter key of 'proximity' +and a parameter value of the distance two places must be in order to detect a match. for example if an origin place and destination place are within 50m +of each other, they can be considered the same place. + +Generally however it is recommended that cross referencing be implemented via alternative identifiers as mentioned above. + +\section3 User Readable vs Non-User Readable Extended Attributes +If an attribute is not intended to be readable by end users, the label field should be kept +empty as an indicator of this fact. +*/ diff --git a/src/location/doc/src/qml-maps.qdoc b/src/location/doc/src/qml-maps.qdoc new file mode 100644 index 0000000..87dd241 --- /dev/null +++ b/src/location/doc/src/qml-maps.qdoc @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group qml-QtLocation5-maps + \title QML Maps Plugin + QML Support for the Qt Location API. +*/ + + +/*! +\page qml-location5-maps.html +\title QML Maps + +\brief Maps deals with maps, their contents and navigation. + +\section1 Overview + +The \l Map type allows the display of a map and placing objects within the map. +Various points of interest can be defined and added to the map for display. +Also the \l Map has features to control how the map is displayed. With the +Map item you can center the map, zoom, pinch and make the item flickable. + +The places to be added to the map are +\l {Maps and Navigation (QML)#Putting Objects on a Map (Map Overlay Objects)}{MapItems}. The item's +position is defined by a \l {coordinate} which includes latitude, +longitude and altitude. The item is then displayed automatically after it is added to the \l Map. + +\section2 Position on map + +All position APIs are part of the \l {QtPositioning} module. +The basic piece of position information is the \l {coordinate}. A +coordinate encapsulates data for the latitude, longitude and altitude of the location. Altitude is +in meters. It also has a method to determine distance to another +\l {coordinate}. The \l {coordinate} type may +also be held within a \l [QtPositioning]{Location} element, this will also have information +on a bounding box size to determine sufficient proximity to the location and a location address. + + +Here is an example of a client that uses a \l{PositionSource}{position source} +to center a \l{Map}{map} on the current position: + +\code + Rectangle { + + import QtPositioning 5.2 + import QtLocation 5.3 + ... + + Map { + id: map + // initialize map + ... + } + + PositionSource { + onPositionChanged: { + // center the map on the current position + map.center = position.coordinate + } + } + } +\endcode + +\section2 Geocoding + +\l {http://en.wikipedia.org/wiki/Geocoding}{Geocoding} is the derivation of +geographical coordinates (latitude and longitude) from other geographical references +to the locations. For example, this can be a street address. Reverse geocoding is also possible with +a street address being used to determine a geographical coordinate. Geocoding +is performed by using the \l [QML]{GeocodeModel} type. + +The following code examples are a small part of the \c map component in the +\l {Map Viewer (QML)}{Map Viewer (QML)} example. The snippets +demonstrate the declaration of the \l GeocodeModel component. + +In the snippet we see that the [QML]{GeocodeModel} contains the plugin +and two signal handlers. One for changes in status \l [QML]{GeocodeModel::status}{\c onStatusChanged} and +the other to update the centering of the Map object \l [QML]{GeocodeModel::locationsChanged}{\c onLocationsChanged}. + +\snippet mapviewer/map/MapComponent.qml geocodemodel0 +\codeline +\snippet mapviewer/map/MapComponent.qml geocodeview + +The geocoding features are called from a higher level piece of code. In this +snippet we see an \l [QML]{Address} object filled with the desired parameters. + +\snippet mapviewer/mapviewer.qml geocode0 + +The \l [QML]{Address} is later used in a query for the \l GeocodeModel to +process and determine the geographical \l [QML]{coordinate}{coordinates}. + +\snippet mapviewer/map/MapComponent.qml geocode1 + + +\section2 Navigation + +A very important function of the \l Map type is navigation +from one place to a destination with possible waypoints along the route. The +route will be divided up into a series of segments. At the end of each segment +is a vertex called a \e maneuver. The \e segments contain information about +the time and distance to the end of the segment. The \e maneuvers contain information +about what to do next, how to get onto the next segment, if there is one. So +a \e maneuver contains navigational information, for example "turn right now". + +To find a suitable route we will need to use a \l RouteQuery to define the +selection criteria and adding any required waypoints. +The \l RouteModel should return a list of \l {RouteSegment}s that defines the +route to the destination complete with navigation advice at the joins between +segments, called \l {RouteManeuver}s + +There are many options that you can add to the query to narrow the criteria. +The \l RouteQuery properties can include + +\table 60% + \row + \li \l {RouteQuery::}{numberAlternativeRoutes} + \li The number of alternative routes + \row + \li \l {RouteQuery::}{travelModes} + \li Travel modes + \row + \li \l {RouteQuery::}{routeOptimizations} + \li Required route optimizations + \row + \li \l {RouteQuery::}{segmentDetail} + \li Level of detail in segments + \row + \li \l {RouteQuery::}{maneuverDetail} + \li Level of detail in maneuvers between segments + \row + \li \l {RouteQuery::}{waypoints} + \li A list of waypoints + \row + \li \l {RouteQuery::}{excludedAreas} + \li A list of excluded areas that the route must not cross + \row + \li \l {RouteQuery::}{featureTypes} + \li Relevant map features, for example highway, ferry +\endtable + + +In the following example a default \l [QML]{RouteQuery} is declared within \l [QML]{RouteModel}. + +\snippet mapviewer/map/MapComponent.qml routemodel0 + +The user enters some information such as the starting point +of the route, some waypoints and the destination. All of these locations are +waypoints so the locations from start to finish will be entered as a sequence +of waypoints. Then other query properties can be set that may be specific to +this trip. + +\snippet mapviewer/map/MapComponent.qml routerequest0 +\snippet mapviewer/map/MapComponent.qml routerequest1 + +The \c routeInfoModel \l {Models and Views in Qt Quick#Models}{ListModel} is used to grab the +results of the query and construct a suitable list for display. +\snippet mapviewer/forms/RouteList.qml routeinfomodel0 +\snippet mapviewer/forms/RouteList.qml routeinfomodel1 +\snippet mapviewer/forms/RouteList.qml routeinfomodel3 + +The \l {Models and Views in Qt Quick#Models}{ListModel} \c routeInfoModel can be filled +with values using a code, that loops through the segments extracting the segment length, +instruction text and distance to the next instruction. The extracted data is formatted +for display as it is retrieved. + +\snippet mapviewer/forms/RouteList.qml routeinfomodel2 + +For more information on the example see the \l {Map Viewer (QML)}{Map Viewer (QML)} example. + + +\section2 Zoom, Pinch and Flickable + +The \l Map item also supports user interface interactions with the map using +tactile and mouse gestures. That is features such as swiping to pan, +pinching to zoom. + +Enabling and configuring pinch and flickable is easy within the \l Map type. + +\snippet mapviewer/map/MapComponent.qml top +\snippet mapviewer/map/MapComponent.qml mapnavigation +\snippet mapviewer/map/MapComponent.qml end + +Zoom can also be controlled by other objects like sliders, with binding +to the Map \l {QtLocation::Map::}{zoomLevel}. + +\section1 QML Types + +\section3 Maps +\annotatedlist qml-QtLocation5-maps + +\section3 Geocoding +\annotatedlist qml-QtLocation5-geocoding + +\section3 Routing +\annotatedlist qml-QtLocation5-routing + + + +\section1 Example + +The above snippets are taken from the \l {Map Viewer (QML)}{Map Viewer (QML)} example. + +*/ + diff --git a/src/location/doc/src/qtlocation-changes.qdoc b/src/location/doc/src/qtlocation-changes.qdoc new file mode 100644 index 0000000..458c9e9 --- /dev/null +++ b/src/location/doc/src/qtlocation-changes.qdoc @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtlocation-changes.html +\title Qt Location QML API changes since 5.4 +\brief Information about the Qt Location QML API changes since 5.4 + +This page lists the QtLocation QML API changes since the first Qt Location Technology Preview +in Qt 5.4. Since Qt 5.6 this API is considered to be final and subsequent releases will +not break the given API anymore. + +\note The public C++ API remained binary compatible since Qt 5.4. + +\b{\l{QtLocation::Map}{Map} Component} +\list + \li removed wheelAngleChanged() signal + \li added \l[QML]{QtLocation::Map::}{error} property + \li added \l[QML]{QtLocation::Map::}{errorString} property + \li added \l[QML]{QtLocation::Map::}{copyrightLinkActivated} signal + \li removed toScreenPosition() method + \li added \l[QML]{QtLocation::Map::}{fromCoordinate}() method + \li replaced cameraStopped() method with \l[QML]{QtLocation::Map::}{prefetchData} method + \li replaced fitViewportToGeoShape() method with \l[QML]{QtLocation::Map::}{visibleRegion} property + \li added \l[QML]{QtLocation::Map::}{color} property + \li added \l[QML]{QtLocation::Map::}{clearData} method +\endlist + +\b{\l{QtLocation::MapGestureArea}{MapGestureArea} Component} +\list + \li removed movementStopped() signal + \li replaced isPanActive and isPinchActive properties with \l[QML]{QtLocation::MapGestureArea::}{panActive} + and \l[QML]{QtLocation::MapGestureArea::}{pinchActive} properties + \li replaced activeGestures with \l[QML]{QtLocation::MapGestureArea::}{acceptedGestures} + \li replaced MapGestureArea.ZoomGesture with \l[QML]{QtLocation::MapGestureArea::acceptedGestures}{MapGestureArea.PinchGesture} + \li removed properties panEnabled and pinchEnabled, please use \l[QML]{QtLocation::MapGestureArea::}{acceptedGestures} instead +\endlist + +\b{\l{QtLocation::MapPolyline}{MapPolyline} Component} +\list + \li added \l[QML]{QtLocation::MapPolyline::}{containsCoordinate} method + \li added \l[QML]{QtLocation::MapPolyline::}{coordinateAt} method + \li added \l[QML]{QtLocation::MapPolyline::}{insertCoordinate} method + \li added \l[QML]{QtLocation::MapPolyline::}{replaceCoordinate} method + \li added \l[QML]{QtLocation::MapPolyline::}{removeCoordinate} method +\endlist + +\b Geoservice's plugin parameters +\list + \li the \l{Qt Location HERE Plugin}{HERE} plugin uses the \c here prefix in front of each plugin parameter name + \li the \l{Qt Location Open Street Map Plugin}{OSM} plugin uses the \c osm prefix in front of each plugin parameter name +\endlist +*/ diff --git a/src/location/doc/src/qtlocation-cpp.qdoc b/src/location/doc/src/qtlocation-cpp.qdoc new file mode 100644 index 0000000..c2c29b3 --- /dev/null +++ b/src/location/doc/src/qtlocation-cpp.qdoc @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtlocation-cpp.html +\title Qt Location C++ API +\brief Information about the Qt Location C++ API + +The Location API provides a library for mapping, navigation and place information. + +\tableofcontents + +The Qt Location API provides the developer with a set of functions to interact +with maps, navigational data and places of interest. This is particularly useful +when associated with current position information which can be retrieved via the +\l QtPositioning module. + +With the Maps API we can associate a position with a map in various formats supplied by a backend. +Then the Places API could be used to populate places on the Map or even +specify the current position as a place of interest and associate it with +an icon, contact details and other information. + +The following table provides links to more detailed information on sections of the +Qt Location C++ API. + +\table + \row + \li \l {Maps and Navigation (C++)}{Maps and Navigation} + \li Displaying maps and finding routes. + \row + \li \l {Places (C++)} {Places} + \li Searching for and managing points of interest. + \row + \li \l {Qt Location GeoServices}{Geoservices Plugin Implementation} + \li Implement new geoservices and positioning plugins. +\endtable + + +\section1 Geoservice Provider Classes + + \annotatedlist QtLocation-common + + +\section1 Maps and Navigation Classes + +Currently it is not possible to interact with maps data via C++. The only available interface is the \l {Maps and Navigation (QML)} API. + + \annotatedlist QtLocation-maps + + \annotatedlist QtLocation-routing + + \annotatedlist QtLocation-geocoding + + +\section1 Places Classes + + \annotatedlist QtLocation-places + + +\section1 Geoservices and Positioning Plugin Classes + + \annotatedlist QtLocation-impl + +*/ + diff --git a/src/location/doc/src/qtlocation-examples.qdoc b/src/location/doc/src/qtlocation-examples.qdoc new file mode 100644 index 0000000..02938c0 --- /dev/null +++ b/src/location/doc/src/qtlocation-examples.qdoc @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group qtlocation-examples + \title Qt Location Examples + \brief Examples for the Qt Location module + \ingroup all-examples + \ingroup qtlocation + + These examples show a range of different uses for \l{Qt Location}, + such as displaying a map within a QML user interface, implementing basic routing and + place search, as well as integrating positioning data types. + + These examples can work with any of the available geo services plugins. However, some plugins may + require additional \l {QtLocation::PluginParameter}{plugin parameters} in order to function correctly. + The default plugin used by these examples is \l {Qt Location Open Street Map Plugin}, which does not + require any parameters. The \l {Qt Location Mapbox GL Plugin} was used for the screenshots and can also + be used without any parameters, for development purposes, on the platforms it is available. +*/ + diff --git a/src/location/doc/src/qtlocation-geoservices.qdoc b/src/location/doc/src/qtlocation-geoservices.qdoc new file mode 100644 index 0000000..cc67e23 --- /dev/null +++ b/src/location/doc/src/qtlocation-geoservices.qdoc @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtlocation-geoservices.html +\title Qt Location GeoServices +\brief Implementing Qt Location GeoService plugins + +The Qt Location provides the majority of its functionality through GeoService plugins. This +document outlines how to develop a new GeoService plugin. + +\section1 Plugin Description + +Each plugin is described by a json file. The json describes the plugins capabilities and +version. Below is an example of a json file used by the OpenStreenMap plugin: + + +\quotefile ../../../plugins/geoservices/osm/osm_plugin.json + +The entries have the following meaning: + +\table + \header + \li Key + \li Description + \row + \li Keys + \li The unique name/key of the plugin. Each GeoService plugin must have a unique name. + \row + \li Provider + \li The provider name of the services. Multiple plugins may have the same name. + In such cases the Version string will be used to further distinguish the plugins. + \row + \li Experimental + \li Marks the service plugin as experimental. API developers may choose to ignore + such plugins when instanciating \l QGeoServiceProvider::QGeoServiceProvider(). + \row + \li Version + \li The plugin version. If multiple plugins have the same provider name, the plugin + with the higest version will be used. + \row + \li Features + \li List of features provided by the plugin/service. Each feature is a string + representation of the corresponding features in \l QGeoServiceProvider. For more + details see \l QGeoServiceProvider::routingFeatures(), + \l QGeoServiceProvider::geocodingFeatures() and \l QGeoServiceProvider::placesFeatures(). + +\endtable + +\section1 Implementing Plugins + +A plugin implementer needs to subclass QGeoServiceProviderFactory and as +many of the ManagerEngine classes as they want to provide implementations for. + +Subclassing QGeoServiceProviderFactory will only involves overriding of one of the following +methods: + +\list + \li \l QGeoServiceProviderFactory::createGeocodingManagerEngine() + \li \l QGeoServiceProviderFactory::createRoutingManagerEngine() + \li \l QGeoServiceProviderFactory::createPlaceManagerEngine() +\endlist + +If a plugin does not provide an engine the relevant function should return 0. + +\annotatedlist QtLocation-impl + +*/ diff --git a/src/location/doc/src/qtlocation-qml.qdoc b/src/location/doc/src/qtlocation-qml.qdoc new file mode 100644 index 0000000..38f43ef --- /dev/null +++ b/src/location/doc/src/qtlocation-qml.qdoc @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlmodule QtLocation 5.11 + \title Qt Location QML Types + \ingroup qmlmodules + \brief Provides QML types for mapping and location information + +\section1 Overview + +Provided that a position has been obtained, this module can add a +\l{QtLocation::Map}{Map} with Places of Interest (POI) and +\l{QtLocation::Place}{Places}. The user can be made aware of nearby features +and related information, displayed graphically. Features on the \l Map can be +places of business, entertainment, and so on. They may include paths, roads, +or forms of transport, enabling navigation optimization and assistance. + +To perform navigation we need \l {Route}s from start to destination. These routes +are made up of segments, where each \l {QtLocation::RouteSegment}{RouteSegment} +can be considered a navigation subtask: drive 100 meters, turn left. The beginning and +end of each segment is a \e waypoint, that is, one part of our journey. + +A typical use case for the API is a user looking for a particular type of +place, say a restaurant. The user could enter a search string into the map +application and respond to a list or display of results for restaurants +"near" the device. The application could then be used to navigate to the +restaurant using an optimized route that is aware of features in the +environment that can help or hinder the journey. The navigation then +proceeds with the user's progress monitored by means of the current +\l Location. In the context of this API the map application would be aware +of the location and size of various places and the location of the user. +Plugins would supply the data required by the application to determine routes +and navigation instructions. The \l Place types would hold information about the +destination and surrounding objects including displayable representations. +The \l Map type would enable this information to be displayed, panned, +zoomed and so on. The \l Route would be determined by a plugin with each +\l RouteSegment holding the navigation instructions guided by the updated +current \l Location. + +\l {Plugin}s are a means of specifying which location-based service to use. For +example, a plugin may allow connection to a provider's service that provides +geocoding and routing information, which can be consumed by the application. +There may be various GeoServices plugins for various tasks with some plugins +providing more than one service. One QML \l Plugin must be created for each +required GeoService plugin. Plugins are required for maps, routing and geocoding, +and places, however the default plugin handles all four of these services. A plugin may +require online access or may support on-board maps and data. + +\note Plugins may not provide features such as paging or relevance hints. + +The following links provide more detailed information about maps and places: + +\table + \row + \li \l {Maps and Navigation (QML)}{Maps and Navigation} + \li Displaying maps and finding routes. + \row + \li \l {QML PLaces API} {Places} + \li Searching for and managing points of interest. +\endtable + +\section1 Common QML Types + +\annotatedlist qml-QtLocation5-common + +\section1 Maps QML Types + +\annotatedlist qml-QtLocation5-maps + +\section1 Navigation and Routing QML Types + +\annotatedlist qml-QtLocation5-routing + +\section1 Geocoding QML Types + +\annotatedlist qml-QtLocation5-geocoding + +\section1 Places QML Types + +\annotatedlist qml-QtLocation5-places + +\section1 Alphabetical Listing of All QML Types +*/ diff --git a/src/location/doc/src/qtlocation.qdoc b/src/location/doc/src/qtlocation.qdoc new file mode 100644 index 0000000..1f27a14 --- /dev/null +++ b/src/location/doc/src/qtlocation.qdoc @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + + +/*! + \module QtLocation + \title Qt Location C++ Classes + \ingroup modules + \qtvariable location + + \brief Provides C++ interfaces to retrieve location and navigational + information. + + The C++ API provides access to geocoding and navigation information, + and also place search. Use the \l{Maps and Navigation (QML)}{QML} + API to render this information on an interactive map that supports + touch gestures, overlays, and so on. + + Include the appropriate header in your C++ code. For example, + applications using routes can include: + + \code #include \endcode + + Add the \e location keyword in the project file to link against the + Qt Location library: + + \code QT += location \endcode + + See more in the \l{Qt Location}{Qt Location Overview}. + +*/ + + + +/*! +\page qtlocation-index.html +\title Qt Location +\brief Provides QML and C++ interfaces to create location-aware +applications. +\ingroup technology-apis + +The Qt Location API helps you create viable mapping solutions using the data +available from some of the popular location services. + +\section1 Overview + +The Qt Location API enables you to: +\list + \li access and present map data, + \li support touch gesture on a specific area of the map, + \li query for a specific geographical location and route, + \li add additional layers on top, such as polylines and circles, + \li and search for places and related images. +\endlist + +\section1 Getting Started + +To load the Qt Location module, add the following statement to your .qml files + +\code + import QtPositioning 5.11 + import QtLocation 5.11 +\endcode + +The QtLocation QML module depends on the QtPositioning QML module. +Therefore every QML application that imports the QtLocation QML module must always +import the QtPositioning module as well. + +For C++ projects include the header appropriate for the current use case, +for example applications using routes may use + +\code #include \endcode + +The .pro file should have the \e location keyword added + +\code QT += location \endcode + +\section2 Submodules + +The API is split into sub-modules, which provide QML and C++ interfaces for +specific purposes. They focus mainly on Map and Place information. The required +position data can be retrieved using the \l {QtPositioning} module. + +\section3 Places + +The Places submodule is the natural complement to Positioning, providing a +source of geographical data about Places of Interest (POI). Besides the source +information, the API provides information about the location, size, and +other related information about a POI. The Places API can also +retrieve images, reviews, and other content related to a place. + +\table +\row + \li Places introduction: + \li \l{QML Places API}{for QML} + \li \l{Places (C++)}{for C++} +\endtable + +\section3 Maps and Navigation + +The module provides the QML and C++ alternatives for maps and navigation. +The C++ alternative provides utility classes to get geocoding (finding a geographic +coordinate from a street address) and navigation (including driving and walking +directions) information, whereas its QML counterpart provides UI components to render +the information. + +\table +\row +\li Maps and Navigation introduction: + \li \l{Maps and Navigation (QML)}{for QML} + \li \l{Maps and Navigation (C++)}{for C++} +\endtable + +\section1 API References and Examples + +The following are lists of the classes and UI components provided by the module, +with example applications to demonstrate their usage: + +\table + \row + \li \l {Qt Location QML Types}{QML API Reference} + \li Full list of QML components in the Qt Location API + \row + \li \l {Qt Location C++ API}{C++ API Reference by domain} + \li Full list of C++ classes and methods of the Qt Location APIs sorted by domain + \row + \li \l {Qt Location C++ Classes}{C++ API Reference} + \li Full list of C++ classes and methods of the Qt Location APIs + \row + \li \l {Qt Location Examples}{Example Apps} + \li Examples demonstrating use of the Qt Location APIs + \row + \li \l {QML Maps}{Maps and Navigation Tutorial} + \li Tutorial introducing the QML Maps Types +\endtable + +\section1 Plugin References and Parameters + +Information about plugins, important notes on their usage, parameters that can +be provided to influence their behavior. + +\annotatedlist QtLocation-plugins + +\section2 Implementing New Back-Ends and Porting + +For systems integrators and distributors, information related to making +Qt Location available for a new platform. + +\table + \row + \li \l {Qt Location GeoServices}{GeoServices} + \li Information about the Qt Location GeoServices plugins + \row + \li \l {Places Backend} {Places} + \li Information for places backend implementors + \row + \li \l {Qt Location QML API changes since 5.4}{API changes} + \li Information about QML API changes since 5.4 +\endtable + + \section1 Licenses and Attributions + + Qt Location is available under commercial licenses from \l{The Qt Company}. + In addition, it is available under the + \l{GNU Lesser General Public License, version 3}, or + the \l{GNU General Public License, version 2}. + See \l{Qt Licensing} for further details. + + Furthermore Qt Location potentially contains third party + modules under following permissive licenses: + + \generatelist{groupsbymodule attributions-qtlocation} +*/ diff --git a/src/location/doc/src/src.pro b/src/location/doc/src/src.pro new file mode 100644 index 0000000..fe90f13 --- /dev/null +++ b/src/location/doc/src/src.pro @@ -0,0 +1,9 @@ +TEMPLATE = subdirs +SUBDIRS += snippets + +OTHER_FILES = \ + *.qdoc \ + *.qdocinc \ + plugins/*.qdoc \ + examples/*.qdoc \ + imports/*.qdoc diff --git a/src/location/labs/labs.pri b/src/location/labs/labs.pri new file mode 100644 index 0000000..79a2270 --- /dev/null +++ b/src/location/labs/labs.pri @@ -0,0 +1,5 @@ +QT += positioningquick-private +INCLUDEPATH += labs + +PRIVATE_HEADERS += $$files($$PWD/*.h) $$files($$PWD/qsg/*.h) +SOURCES += $$files($$PWD/*.cpp) $$files($$PWD/qsg/*.cpp) diff --git a/src/location/labs/qdeclarativenavigator.cpp b/src/location/labs/qdeclarativenavigator.cpp new file mode 100644 index 0000000..7dba341 --- /dev/null +++ b/src/location/labs/qdeclarativenavigator.cpp @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 or (at your option) any later version +** approved by the KDE Free Qt Foundation. The licenses are as published by +** the Free Software Foundation and appearing in the file LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativenavigator_p.h" +#include "qdeclarativenavigator_p_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Navigator + \instantiates QDeclarativeNavigator + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + + \brief The Navigator type offers functionalities to perform turn-by-turn navigation. + + The Navigator purpose is to use a plugin's turn-by-turn navigation implementation in a QML + application in a seamless manner. + This object may take control of the map position, orientation, tilting and zoom, as well as changing + the map style, elements on the map such as direction information. + In certain cases, it may also restrict user interaction with the Map and with the items on it. +*/ + +/*! + \qmlproperty Plugin Qt.labs.location::Navigator::plugin + + This property holds the plugin which provides the navigation functionality. + + This is a write-once property. Once the Navigator has a plugin associated with + it, any attempted modifications of the plugin property will be ignored. + + \sa Plugin +*/ + +/*! + \qmlproperty Map Qt.labs.location::Navigator::map + + This property holds the Map that the navigator is in charge of controlling. + + This is a write-once property. Once the Navigator has a Map associated with + it, any attempted modifications of the map property will be ignored. + + \sa Map +*/ + +/*! + \qmlproperty Route Qt.labs.location::Navigator::route + + This property holds the Route that the navigator is supposed to use + to perform the navigation. + + \note + This property is not necessarily the same as \l currentRoute. + currentRoute may differ, during routing, for various reasons. + It is reasonable to assume, however, that currentRoute's destination + will be the same as route's destination. + Setting this property while a navigation session is ongoing will + stop the navigation. + + \sa Route +*/ + +/*! + \qmlproperty PositionSource Qt.labs.location::Navigator::positionSource + + This property holds the PositionSource that the navigator will receive position + updates from to perform the navigation. + + This is a write-once property. Once the Navigator has a PositionSource associated with + it, any attempted modifications of the positionSource property will be ignored. + + \sa PositionSource +*/ + +/*! + \qmlproperty bool Qt.labs.location::Navigator::active + + This property tells whether the Navigator is navigating or not. + Set this property to \c true to start the navigation. + Set it to \c false to stop an active navigation session. +*/ + +/*! + \qmlproperty bool Qt.labs.location::Navigator::navigatorReady + + This read-only property tells whether the navigator is ready + to start the navigation or not. + A Navigator becomes ready once the plugin is attached and a navigation engine has been + instantiated, and the other required properties are set to valid values. +*/ + +/*! + \qmlproperty bool Qt.labs.location::Navigator::trackPositionSource + + This property tells whether the Navigator should control the Map camera to + keep track of position source updates. This property is enabled (\c true) by + default, and setting it to \c false is useful in cases where e.g. the user + starts gesturing over the map area. + + Navigator plugins can also control this property directly e.g. user map + interaction could trigger the property change. Honoring the user-specified + value of this property is plugin dependent. +*/ + +/*! + \qmlproperty Route Qt.labs.location::Navigator::currentRoute + + This read-only property holds the current route the navigator following. + This can be the same as \l route, or can be different, if the navigator + cannot follow the user-specified route. + For example if the position coming from \l positionSource is considerably + off route, the navigation engine might recalculate and start following a + new route. + + \sa Route +*/ + +/*! + \qmlproperty int Qt.labs.location::Navigator::currentSegment + + This read-only property holds the index of the current RouteSegment in the \l currentRoute. + + \sa RouteSegment +*/ + +/*! + \qmlsignal Qt.labs.location::Navigator::waypointReached(Waypoint waypoint) + + This signal is emitted when the waypoint \e waypoint has been reached. + + \sa Waypoint +*/ + +/*! + \qmlsignal Qt.labs.location::Navigator::destinationReached() + + This signal is emitted when the last waypoint of the route, the destination, + has been reached. +*/ + +QDeclarativeNavigatorPrivate::QDeclarativeNavigatorPrivate(QParameterizableObject *q_) + : q(q_) +{ +} + +void QDeclarativeNavigatorPrivate::updateReadyState() +{ + qobject_cast(q)->updateReadyState(); +} + + + +QDeclarativeNavigator::QDeclarativeNavigator(QObject *parent) + : QParameterizableObject(parent), d_ptr(new QDeclarativeNavigatorPrivate(this)) +{ +} + +QDeclarativeNavigator::~QDeclarativeNavigator() +{ +} + +void QDeclarativeNavigator::classBegin() +{ +} + +void QDeclarativeNavigator::componentComplete() +{ + d_ptr->m_completed = true; + // Children have been completed + d_ptr->m_parameters = quickChildren(); + if (d_ptr->m_plugin && d_ptr->m_plugin->isAttached()) + pluginReady(); +} + +QDeclarativeGeoServiceProvider *QDeclarativeNavigator::plugin() const +{ + return d_ptr->m_plugin; +} + +void QDeclarativeNavigator::setMap(QDeclarativeGeoMap *map) +{ + if (d_ptr->m_map || !map) // set once prop + return; + + d_ptr->m_map = map; + QDeclarativeNavigatorPrivate *dptr = d_ptr.data(); + connect(map, &QObject::destroyed, + [this, dptr]() { + this->mapChanged(); + dptr->updateReadyState(); + }); + emit mapChanged(); + updateReadyState(); +} + +QDeclarativeGeoMap *QDeclarativeNavigator::map() const +{ + return d_ptr->m_map; +} + +void QDeclarativeNavigator::setRoute(QDeclarativeGeoRoute *route) +{ + if (d_ptr->m_route == route) // This isn't set-once + return; + + const bool isReady = d_ptr->m_navigationManager && d_ptr->m_navigationManager->ready(); + const bool isActive = active(); + if (isReady && isActive) + setActive(false); // Stop current session + + d_ptr->m_route = route; + d_ptr->m_geoRoute = route ? route->route() : QGeoRoute(); + if (route) { + connect(route, &QObject::destroyed, + [this]() { + // Do not stop navigation if route disappears. d_ptr->m_geoRoute will still be valid. + // Engines can stop navigation if desired. + this->routeChanged(); + }); + } + emit routeChanged(); + updateReadyState(); +} + +QDeclarativeGeoRoute *QDeclarativeNavigator::route() const +{ + return d_ptr->m_route; +} + +void QDeclarativeNavigator::setPositionSource(QDeclarativePositionSource *positionSource) +{ + if (d_ptr->m_positionSource || !positionSource) // set once prop + return; + + d_ptr->m_positionSource = positionSource; + QDeclarativeNavigatorPrivate *dptr = d_ptr.data(); + QObject::connect(positionSource, &QObject::destroyed, + [this, dptr]() { + this->positionSourceChanged(); + dptr->updateReadyState(); + } + ); + emit positionSourceChanged(); + updateReadyState(); +} + +QDeclarativePositionSource *QDeclarativeNavigator::positionSource() const +{ + return d_ptr->m_positionSource; +} + +QNavigationManager *QDeclarativeNavigator::navigationManager() const +{ + return d_ptr->m_navigationManager; +} + +bool QDeclarativeNavigator::navigatorReady() const +{ + if (d_ptr->m_navigationManager) + return d_ptr->m_navigationManager->ready(); + return d_ptr->m_ready; +} + +bool QDeclarativeNavigator::trackPositionSource() const +{ + return d_ptr->m_trackPositionSource; +} + +void QDeclarativeNavigator::setTrackPositionSource(bool trackPositionSource) +{ + if (trackPositionSource == d_ptr->m_trackPositionSource) + return; + + d_ptr->m_trackPositionSource = trackPositionSource; + + emit trackPositionSourceChanged(trackPositionSource); +} + +QDeclarativeGeoRoute *QDeclarativeNavigator::currentRoute() const +{ + if (!d_ptr->m_ready || !d_ptr->m_navigationManager->active()) + return d_ptr->m_route.data(); + return d_ptr->m_currentRoute.data(); +} + +int QDeclarativeNavigator::currentSegment() const +{ + if (!d_ptr->m_ready || !d_ptr->m_navigationManager->active()) + return 0; + return d_ptr->m_currentSegment; +} + +bool QDeclarativeNavigator::active() const +{ + return d_ptr->m_active; +} + +void QDeclarativeNavigator::setPlugin(QDeclarativeGeoServiceProvider *plugin) +{ + if (d_ptr->m_plugin) + return; // set once property. + + d_ptr->m_plugin = plugin; + emit pluginChanged(); + + if (d_ptr->m_plugin->isAttached()) { + pluginReady(); + } else { + connect(d_ptr->m_plugin, &QDeclarativeGeoServiceProvider::attached, + this, &QDeclarativeNavigator::pluginReady); + } +} + +void QDeclarativeNavigator::setActive(bool active) +{ + if (d_ptr->m_active == active) + return; + + d_ptr->m_active = active; + if (!d_ptr->m_plugin) + return; + + if (active) + start(); + else + stop(); +} + +void QDeclarativeNavigator::start() +{ + if (!d_ptr->m_ready) { + qmlWarning(this) << QStringLiteral("Navigation manager not ready."); + return; + } + + if (!d_ptr->m_navigationManager->active()) + d_ptr->m_active = d_ptr->m_navigationManager->start(); +} + +void QDeclarativeNavigator::stop() +{ + if (!ensureEngine()) { // If somebody re-set route to null or something, this may become !d_ptr->m_ready + qmlWarning(this) << QStringLiteral("Navigation manager not ready."); + return; + } + + if (d_ptr->m_navigationManager->active()) + d_ptr->m_active = d_ptr->m_navigationManager->stop(); +} + +void QDeclarativeNavigator::pluginReady() +{ + if (!d_ptr->m_completed) + return; + + ensureEngine(); + updateReadyState(); + if (d_ptr->m_active) + start(); +} + +bool QDeclarativeNavigator::ensureEngine() +{ + if (d_ptr->m_navigationManager) + return true; + if (!d_ptr->m_completed || !d_ptr->m_plugin->isAttached()) + return false; + + d_ptr->m_navigationManager = d_ptr->m_plugin->sharedGeoServiceProvider()->navigationManager(); + if (d_ptr->m_navigationManager) { + d_ptr->m_navigationManager->setNavigator(d_ptr.data()); + d_ptr->m_navigationManager->setParameters(d_ptr->m_parameters); + connect(d_ptr->m_navigationManager, &QNavigationManager::waypointReached, this, &QDeclarativeNavigator::waypointReached); + connect(d_ptr->m_navigationManager, &QNavigationManager::destinationReached, this, &QDeclarativeNavigator::destinationReached); + connect(d_ptr->m_navigationManager, &QNavigationManager::currentRouteChanged, this, &QDeclarativeNavigator::onCurrentRouteChanged); + connect(d_ptr->m_navigationManager, &QNavigationManager::currentSegmentChanged, this, &QDeclarativeNavigator::onCurrentSegmentChanged); + connect(d_ptr->m_navigationManager, &QNavigationManager::activeChanged, this, [this](bool active){ + d_ptr->m_active = active; + emit activeChanged(active); + }); + emit navigatorReadyChanged(true); + return true; + } + return false; +} + +void QDeclarativeNavigator::updateReadyState() { + const bool oldReady = d_ptr->m_ready; + if (!d_ptr->m_navigationManager) + d_ptr->m_ready = false; + else + d_ptr->m_ready = d_ptr->m_navigationManager->ready(); + + if (oldReady != d_ptr->m_ready) + emit navigatorReadyChanged(d_ptr->m_ready); +} + +void QDeclarativeNavigator::onCurrentRouteChanged(const QGeoRoute &route) +{ + if (d_ptr->m_currentRoute) + d_ptr->m_currentRoute->deleteLater(); + d_ptr->m_currentRoute = new QDeclarativeGeoRoute(route, this); + emit currentRouteChanged(); +} + +void QDeclarativeNavigator::onCurrentSegmentChanged(int segment) +{ + d_ptr->m_currentSegment = segment; + emit currentSegmentChanged(); +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qdeclarativenavigator_p.h b/src/location/labs/qdeclarativenavigator_p.h new file mode 100644 index 0000000..3c83ec9 --- /dev/null +++ b/src/location/labs/qdeclarativenavigator_p.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVENAVIGATOR_P_H +#define QDECLARATIVENAVIGATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; +class QDeclarativeGeoMap; +class QNavigationManager; +class QDeclarativeGeoRoute; +class QDeclarativePositionSource; +class QDeclarativeGeoWaypoint; +class QGeoRoute; +class QGeoRouteSegment; +class QDeclarativeNavigatorPrivate; +class QDeclarativeGeoRouteSegment; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeNavigator : public QParameterizableObject, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeGeoServiceProvider *plugin READ plugin WRITE setPlugin NOTIFY pluginChanged) + Q_PROPERTY(QDeclarativeGeoMap *map READ map WRITE setMap NOTIFY mapChanged) + Q_PROPERTY(QDeclarativeGeoRoute *route READ route WRITE setRoute NOTIFY routeChanged) + Q_PROPERTY(QDeclarativePositionSource *positionSource READ positionSource WRITE setPositionSource NOTIFY positionSourceChanged) + Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(bool navigatorReady READ navigatorReady NOTIFY navigatorReadyChanged) + Q_PROPERTY(bool trackPositionSource READ trackPositionSource WRITE setTrackPositionSource NOTIFY trackPositionSourceChanged) + Q_PROPERTY(QDeclarativeGeoRoute *currentRoute READ currentRoute NOTIFY currentRouteChanged) + Q_PROPERTY(int currentSegment READ currentSegment NOTIFY currentSegmentChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + explicit QDeclarativeNavigator(QObject *parent = nullptr); + ~QDeclarativeNavigator(); + + // QQmlParserStatus interface + void classBegin() override; + void componentComplete() override; + + // QDeclarativeNavigator + void start(); + void stop(); + + void setActive(bool active); + bool active() const; + + void setPlugin(QDeclarativeGeoServiceProvider * plugin); + QDeclarativeGeoServiceProvider *plugin() const; + + void setMap(QDeclarativeGeoMap *map); + QDeclarativeGeoMap * map() const; + + void setRoute(QDeclarativeGeoRoute *route); + QDeclarativeGeoRoute *route() const; + + void setPositionSource(QDeclarativePositionSource *positionSource); + QDeclarativePositionSource *positionSource() const; + + QNavigationManager *navigationManager() const; + bool navigatorReady() const; + + void setTrackPositionSource(bool trackPositionSource); + bool trackPositionSource() const; + + QDeclarativeGeoRoute *currentRoute() const; + int currentSegment() const; + +signals: + void navigatorReadyChanged(bool ready); + void trackPositionSourceChanged(bool trackPositionSource); + void activeChanged(bool active); + void waypointReached(const QDeclarativeGeoWaypoint *pos); + void destinationReached(); + + void pluginChanged(); + void mapChanged(); + void routeChanged(); + void positionSourceChanged(); + void currentRouteChanged(); + void currentSegmentChanged(); + +private: + void pluginReady(); + bool ensureEngine(); + void updateReadyState(); + +private slots: + void onCurrentRouteChanged(const QGeoRoute &route); + void onCurrentSegmentChanged(int segment); + +private: + QScopedPointer d_ptr; + + friend class QDeclarativeNavigatorPrivate; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativeNavigator) + + +#endif // QDECLARATIVENAVIGATOR_P_H diff --git a/src/location/labs/qdeclarativenavigator_p_p.h b/src/location/labs/qdeclarativenavigator_p_p.h new file mode 100644 index 0000000..3710054 --- /dev/null +++ b/src/location/labs/qdeclarativenavigator_p_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVENAVIGATOR_P_P_H +#define QDECLARATIVENAVIGATOR_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; +class QDeclarativeGeoMap; +class QNavigationManager; +class QDeclarativeGeoRoute; +class QDeclarativePositionSource; +class QGeoMapParameter; +class QDeclarativeGeoRouteSegment; +class QParameterizableObject; + +class Q_LOCATION_PRIVATE_EXPORT QDeclarativeNavigatorPrivate +{ +public: + QDeclarativeNavigatorPrivate(QParameterizableObject *q_); + + void updateReadyState(); + + QParameterizableObject *q = nullptr; + QNavigationManager *m_navigationManager = nullptr; + QDeclarativeGeoServiceProvider *m_plugin = nullptr; + QPointer m_map; + QPointer m_route; + QGeoRoute m_geoRoute; + QPointer m_positionSource; + QPointer m_currentRoute; + QList m_parameters; + int m_currentSegment = 0; + bool m_active = false; + bool m_completed = false; + bool m_ready = false; + bool m_trackPositionSource = true; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVENAVIGATOR_P_P_H diff --git a/src/location/labs/qgeotiledmaplabs.cpp b/src/location/labs/qgeotiledmaplabs.cpp new file mode 100644 index 0000000..22d582c --- /dev/null +++ b/src/location/labs/qgeotiledmaplabs.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmaplabs_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoTiledMapLabsPrivate : public QGeoTiledMapPrivate +{ + Q_DECLARE_PUBLIC(QGeoTiledMapLabs) +public: + QGeoTiledMapLabsPrivate(QGeoTiledMappingManagerEngine *engine, QGeoTiledMapLabs *map); + virtual ~QGeoTiledMapLabsPrivate(); + + QGeoMapObjectPrivate *createMapObjectImplementation(QGeoMapObject *obj) override; + virtual QList mapObjects() const override; + void removeMapObject(QGeoMapObject *obj); + + void updateMapObjects(QSGNode *root, QQuickWindow *window); + void updateObjectsGeometry(); + +protected: + void changeViewportSize(const QSize &size) override; + void changeCameraData(const QGeoCameraData &oldCameraData) override; + void changeActiveMapType(const QGeoMapType mapType) override; + + QGeoMapObjectQSGSupport m_qsgSupport; +}; + +QGeoTiledMapLabsPrivate::QGeoTiledMapLabsPrivate(QGeoTiledMappingManagerEngine *engine, QGeoTiledMapLabs *map) + : QGeoTiledMapPrivate(engine) +{ + m_qsgSupport.m_map = map; +} + +QGeoTiledMapLabsPrivate::~QGeoTiledMapLabsPrivate() +{ + +} + +QGeoMapObjectPrivate *QGeoTiledMapLabsPrivate::createMapObjectImplementation(QGeoMapObject *obj) +{ + return m_qsgSupport.createMapObjectImplementationPrivate(obj); +} + +QList QGeoTiledMapLabsPrivate::mapObjects() const +{ + return m_qsgSupport.mapObjects(); +} + +void QGeoTiledMapLabsPrivate::removeMapObject(QGeoMapObject *obj) +{ + m_qsgSupport.removeMapObject(obj); +} + +void QGeoTiledMapLabsPrivate::updateMapObjects(QSGNode *root, QQuickWindow *window) +{ + m_qsgSupport.updateMapObjects(root, window); +} + +void QGeoTiledMapLabsPrivate::updateObjectsGeometry() +{ + m_qsgSupport.updateObjectsGeometry(); +} + +void QGeoTiledMapLabsPrivate::changeViewportSize(const QSize &size) +{ + updateObjectsGeometry(); + QGeoTiledMapPrivate::changeViewportSize(size); +} + +void QGeoTiledMapLabsPrivate::changeCameraData(const QGeoCameraData &oldCameraData) +{ + updateObjectsGeometry(); + QGeoTiledMapPrivate::changeCameraData(oldCameraData); +} + +void QGeoTiledMapLabsPrivate::changeActiveMapType(const QGeoMapType mapType) +{ + updateObjectsGeometry(); + QGeoTiledMapPrivate::changeActiveMapType(mapType); +} + + +/* + QGeoTiledMapLabs +*/ + + + +QGeoTiledMapLabs::QGeoTiledMapLabs(QGeoTiledMappingManagerEngine *engine, QObject *parent) + : QGeoTiledMap(*new QGeoTiledMapLabsPrivate(engine, this), engine, parent) +{ + +} + +QGeoTiledMapLabs::~QGeoTiledMapLabs() +{ + +} + +bool QGeoTiledMapLabs::createMapObjectImplementation(QGeoMapObject *obj) +{ + Q_D(QGeoTiledMapLabs); + return d->m_qsgSupport.createMapObjectImplementation(obj, d); +} + +QSGNode *QGeoTiledMapLabs::updateSceneGraph(QSGNode *node, QQuickWindow *window) +{ + Q_D(QGeoTiledMapLabs); + QSGNode *root = QGeoTiledMap::updateSceneGraph(node, window); + d->updateMapObjects(root, window); + return root; +} + +void QGeoTiledMapLabs::removeMapObject(QGeoMapObject *obj) +{ + Q_D(QGeoTiledMapLabs); + d->removeMapObject(obj); +} + +QGeoTiledMapLabs::QGeoTiledMapLabs(QGeoTiledMapLabsPrivate &dd, QGeoTiledMappingManagerEngine *engine, QObject *parent) + : QGeoTiledMap(dd, engine, parent) +{ + +} + +QT_END_NAMESPACE + diff --git a/src/location/labs/qgeotiledmaplabs_p.h b/src/location/labs/qgeotiledmaplabs_p.h new file mode 100644 index 0000000..4f17902 --- /dev/null +++ b/src/location/labs/qgeotiledmaplabs_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPLABS_P_H +#define QGEOTILEDMAPLABS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoServiceProvider; +class QDeclarativeGeoMap; +class QMapRouteObject; +class QNavigationManager; +class QGeoTiledMapLabsPrivate; +class Q_LOCATION_PRIVATE_EXPORT QGeoTiledMapLabs : public QGeoTiledMap +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoTiledMapLabs) +public: + QGeoTiledMapLabs(QGeoTiledMappingManagerEngine *engine, QObject *parent); + virtual ~QGeoTiledMapLabs(); + + bool createMapObjectImplementation(QGeoMapObject *obj) override; + void removeMapObject(QGeoMapObject *obj) override; + +protected: + QSGNode *updateSceneGraph(QSGNode *node, QQuickWindow *window) override; + + QSGClipNode *m_clip = nullptr; + QSGSimpleRectNode *m_simpleRectNode = nullptr; + + // From QGeoTiledMap + QGeoTiledMapLabs(QGeoTiledMapLabsPrivate &dd, QGeoTiledMappingManagerEngine *engine, QObject *parent); +private: + Q_DISABLE_COPY(QGeoTiledMapLabs) +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEDMAPLABS_P_H diff --git a/src/location/labs/qmapcircleobject.cpp b/src/location/labs/qmapcircleobject.cpp new file mode 100644 index 0000000..d7f1d58 --- /dev/null +++ b/src/location/labs/qmapcircleobject.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmapcircleobject_p.h" +#include "qmapcircleobject_p_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapCircleObject + \instantiates QMapCircleObject + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + \inherits QGeoMapObject + + \brief The MapCircleObject displays a circle on a Map. + + The MapCircleObject displays a circle on a Map. + The MapIconObject type only makes sense when contained in a Map or in a \l MapObjectView. +*/ + +QMapCircleObjectPrivate::QMapCircleObjectPrivate(QGeoMapObject *q) : QGeoMapObjectPrivate(q) +{ + +} + +QMapCircleObjectPrivate::~QMapCircleObjectPrivate() +{ + +} + +QGeoMapObject::Type QMapCircleObjectPrivate::type() const +{ + return QGeoMapObject::CircleType; +} + + + +// +// QMapCircleObjectPrivate default implementation +// + +QMapCircleObjectPrivateDefault::QMapCircleObjectPrivateDefault(QGeoMapObject *q) : QMapCircleObjectPrivate(q) +{ + +} + +QMapCircleObjectPrivateDefault::QMapCircleObjectPrivateDefault(const QMapCircleObjectPrivate &other) : QMapCircleObjectPrivate(other.q) +{ + m_center = other.center(); + m_radius = other.radius(); + m_fillColor = other.color(); + m_borderColor = other.borderColor(); + m_borderWidth = other.borderWidth(); +} + +QMapCircleObjectPrivateDefault::~QMapCircleObjectPrivateDefault() +{ + +} + +QGeoCoordinate QMapCircleObjectPrivateDefault::center() const +{ + return m_center; +} + +void QMapCircleObjectPrivateDefault::setCenter(const QGeoCoordinate ¢er) +{ + m_center = center; +} + +qreal QMapCircleObjectPrivateDefault::radius() const +{ + return m_radius; +} + +void QMapCircleObjectPrivateDefault::setRadius(qreal radius) +{ + m_radius = radius; +} + +QColor QMapCircleObjectPrivateDefault::color() const +{ + return m_fillColor; +} + +void QMapCircleObjectPrivateDefault::setColor(const QColor &color) +{ + m_fillColor = color; +} + +QColor QMapCircleObjectPrivateDefault::borderColor() const +{ + return m_borderColor; +} + +void QMapCircleObjectPrivateDefault::setBorderColor(const QColor &color) +{ + m_borderColor = color; +} + +qreal QMapCircleObjectPrivateDefault::borderWidth() const +{ + return m_borderWidth; +} + +void QMapCircleObjectPrivateDefault::setBorderWidth(qreal width) +{ + m_borderWidth = width; +} + +bool QMapCircleObjectPrivate::equals(const QGeoMapObjectPrivate &other) const +{ + if (other.type() != type()) // This check might be unnecessary, depending on how equals gets used + return false; + + const QMapCircleObjectPrivate &o = static_cast(other); + return (QGeoMapObjectPrivate::equals(o) + && center() == o.center() + && radius() == o.radius() + && color() == o.color() + && borderColor() == o.borderColor() + && borderWidth() == o.borderWidth()); +} + +QGeoMapObjectPrivate *QMapCircleObjectPrivateDefault::clone() +{ + return new QMapCircleObjectPrivateDefault(static_cast(*this)); +} + + + +QMapCircleObject::QMapCircleObject(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer(new QMapCircleObjectPrivateDefault(this)), parent) + +{ + QMapCircleObjectPrivate *d = static_cast(d_ptr.data()); + d->setBorderColor(QColor(Qt::black)); // These are QDeclarativeMapLineProperties defaults + d->setBorderWidth(1.0); +} + +QMapCircleObject::~QMapCircleObject() +{ + +} + +/*! + \qmlproperty coordinate Qt.labs.location::MapCircleObject::center + + This property holds the central point about which the circle is defined. + + \sa radius +*/ +QGeoCoordinate QMapCircleObject::center() const +{ + return static_cast(d_ptr.data())->center(); +} + +/*! + \qmlproperty real Qt.labs.location::MapCircleObject::radius + + This property holds the radius of the circle, in meters on the ground. + + \sa center +*/ +qreal QMapCircleObject::radius() const +{ + return static_cast(d_ptr.data())->radius(); +} + +/*! + \qmlproperty color Qt.labs.location::MapCircleObject::color + + This property holds the fill color of the circle when drawn. For no fill, + use a transparent color. +*/ +QColor QMapCircleObject::color() const +{ + return static_cast(d_ptr.data())->color(); +} + +/*! + \qmlpropertygroup Qt.labs.location::MapCircleObject::border + \qmlproperty int MapCircleObject::border.width + \qmlproperty color MapCircleObject::border.color + + This property is part of the border group property. + The border property holds the width and color used to draw the border of the circle. + The width is in pixels and is independent of the zoom level of the map. + + The default values correspond to a black border with a width of 1 pixel. + For no line, use a width of 0 or a transparent color. +*/ +QDeclarativeMapLineProperties *QMapCircleObject::border() +{ + if (!m_border) { + m_border = new QDeclarativeMapLineProperties; + connect(m_border, &QDeclarativeMapLineProperties::colorChanged, this, [this](const QColor &color){ + static_cast(d_ptr.data())->setBorderColor(color); + }); + connect(m_border, &QDeclarativeMapLineProperties::widthChanged, this, [this](qreal width){ + static_cast(d_ptr.data())->setBorderWidth(width); + }); + } + return m_border; +} + +void QMapCircleObject::setCenter(const QGeoCoordinate ¢er) +{ + auto ptr = static_cast(d_ptr.data()); + if (ptr->center() == center) + return; + + ptr->setCenter(center); + emit centerChanged(); +} + +void QMapCircleObject::setRadius(qreal radius) +{ + auto d = static_cast(d_ptr.data()); + if (d->radius() == radius) + return; + + d->setRadius(radius); + emit radiusChanged(); +} + +void QMapCircleObject::setColor(const QColor &color) +{ + auto d = static_cast(d_ptr.data()); + if (d->color() == color) + return; + + d->setColor(color); + emit colorChanged(); +} + +void QMapCircleObject::setMap(QGeoMap *map) +{ + QMapCircleObjectPrivate *d = static_cast(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + if (!map) { + // Map was set, now it has ben re-set to NULL, but not inside d_ptr. + // so m_map inside d_ptr can still be used to remove itself, inside the destructor. + d_ptr = new QMapCircleObjectPrivateDefault(*d); + // Old pimpl deleted implicitly by QExplicitlySharedDataPointer + } +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qmapcircleobject_p.h b/src/location/labs/qmapcircleobject_p.h new file mode 100644 index 0000000..9393047 --- /dev/null +++ b/src/location/labs/qmapcircleobject_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPCIRCLEOBJECT_P_H +#define QMAPCIRCLEOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapCircleObject : public QGeoMapObject +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter NOTIFY centerChanged) + Q_PROPERTY(qreal radius READ radius WRITE setRadius NOTIFY radiusChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + +public: + QMapCircleObject(QObject *parent = nullptr); + ~QMapCircleObject() override; + + QGeoCoordinate center() const; + qreal radius() const; + QColor color() const; + + void setCenter(const QGeoCoordinate ¢er); + void setRadius(qreal radius); + void setColor(const QColor &color); + + QDeclarativeMapLineProperties * border(); + void setMap(QGeoMap *map) override; + +signals: + void centerChanged(); + void radiusChanged(); + void colorChanged(); + +protected: + QDeclarativeMapLineProperties *m_border = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QMAPCIRCLEOBJECT_P_H diff --git a/src/location/labs/qmapcircleobject_p_p.h b/src/location/labs/qmapcircleobject_p_p.h new file mode 100644 index 0000000..b3353b6 --- /dev/null +++ b/src/location/labs/qmapcircleobject_p_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPCIRCLEOBJECT_P_P_H +#define QMAPCIRCLEOBJECT_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapCircleObjectPrivate : public QGeoMapObjectPrivate +{ +public: + QMapCircleObjectPrivate(QGeoMapObject *q); + ~QMapCircleObjectPrivate() override; + + virtual QGeoMapObject::Type type() const override final; + + virtual QGeoCoordinate center() const = 0; + virtual void setCenter(const QGeoCoordinate ¢er) = 0; + virtual qreal radius() const = 0; + virtual void setRadius(qreal radius) = 0; + virtual QColor color() const = 0; + virtual void setColor(const QColor &color) = 0; + virtual QColor borderColor() const = 0; + virtual void setBorderColor(const QColor &color) = 0; + virtual qreal borderWidth() const = 0; + virtual void setBorderWidth(qreal width) = 0; + + // QGeoMapObjectPrivate interface + bool equals(const QGeoMapObjectPrivate &other) const override; +}; + + +class Q_LOCATION_PRIVATE_EXPORT QMapCircleObjectPrivateDefault : public QMapCircleObjectPrivate +{ +public: + QMapCircleObjectPrivateDefault(QGeoMapObject *q); + QMapCircleObjectPrivateDefault(const QMapCircleObjectPrivate &other); + ~QMapCircleObjectPrivateDefault() override; + + // QMapCircleObjectPrivate interface + QGeoCoordinate center() const override; + void setCenter(const QGeoCoordinate ¢er) override; + qreal radius() const override; + void setRadius(qreal radius) override; + QColor color() const override; + void setColor(const QColor &color) override; + QColor borderColor() const override; + void setBorderColor(const QColor &color) override; + qreal borderWidth() const override; + void setBorderWidth(qreal width) override; + + // QGeoMapObjectPrivate interface + QGeoMapObjectPrivate *clone() override; + +public: + QGeoCoordinate m_center; + qreal m_radius = 0; + QColor m_fillColor = Qt::transparent; + QColor m_borderColor; + qreal m_borderWidth = 1.0; + +private: + QMapCircleObjectPrivateDefault(const QMapCircleObjectPrivateDefault &other) = delete; +}; + +QT_END_NAMESPACE + +#endif // QMAPCIRCLEOBJECT_P_P_H diff --git a/src/location/labs/qmapiconobject.cpp b/src/location/labs/qmapiconobject.cpp new file mode 100644 index 0000000..be82ebb --- /dev/null +++ b/src/location/labs/qmapiconobject.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmapiconobject_p.h" +#include "qmapiconobject_p_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapIconObject + \instantiates QMapIconObject + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + \inherits QGeoMapObject + + \brief The MapIconObject displays an icon on a Map. + + The MapIconObject displays an icon on a Map. + The MapIconObject type only makes sense when contained in a Map or in a \l MapObjectView. +*/ + +QMapIconObjectPrivate::~QMapIconObjectPrivate() +{ + +} + +QMapIconObjectPrivate::QMapIconObjectPrivate(QGeoMapObject *q) : QGeoMapObjectPrivate(q) +{ + +} + +QGeoMapObject::Type QMapIconObjectPrivate::type() const +{ + return QGeoMapObject::IconType; +} + +bool QMapIconObjectPrivate::equals(const QGeoMapObjectPrivate &other) const +{ + if (other.type() != type()) // This check might be unnecessary, depending on how equals gets used + return false; + + const QMapIconObjectPrivate &o = static_cast(other); + return (QGeoMapObjectPrivate::equals(o) + && content() == o.content() + && coordinate() == o.coordinate()); +} + +// +// QGeoMapIconPrivate default implementation +// + +QMapIconObjectPrivateDefault::QMapIconObjectPrivateDefault(QGeoMapObject *q) : QMapIconObjectPrivate(q) +{ + +} +QMapIconObjectPrivateDefault::QMapIconObjectPrivateDefault(const QMapIconObjectPrivate &other) : QMapIconObjectPrivate(other.q) +{ + m_coordinate = other.coordinate(); + m_content = other.content(); + m_size = other.size(); +} + +QMapIconObjectPrivateDefault::~QMapIconObjectPrivateDefault() +{ + +} + +QGeoCoordinate QMapIconObjectPrivateDefault::coordinate() const +{ + return m_coordinate; +} + +void QMapIconObjectPrivateDefault::setCoordinate(const QGeoCoordinate ¢er) +{ + m_coordinate = center; +} + +QVariant QMapIconObjectPrivateDefault::content() const +{ + return m_content; +} + +void QMapIconObjectPrivateDefault::setContent(const QVariant &content) +{ + m_content = content; +} + +QSizeF QMapIconObjectPrivateDefault::size() const +{ + return m_size; +} + +void QMapIconObjectPrivateDefault::setSize(const QSizeF &size) +{ + m_size = size; +} + +QGeoMapObjectPrivate *QMapIconObjectPrivateDefault::clone() +{ + return new QMapIconObjectPrivateDefault(static_cast(*this)); +} + + +/* + + QGeoMapIconPrivate default implementation + +*/ + + +QMapIconObject::QMapIconObject(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer(new QMapIconObjectPrivateDefault(this)), parent) +{} + +QMapIconObject::~QMapIconObject() +{ + +} + +/*! + \qmlproperty Variant Qt.labs.location::MapIconObject::content + + This property holds the content to be used for the icon. The actual content of this property is somehow + backend-dependent. The implementation for the raster engine accepts local urls or paths. + Other implementations may accept additional content types. +*/ +QVariant QMapIconObject::content() const +{ + const QMapIconObjectPrivate *d = static_cast(d_ptr.data()); + return d->content(); +} + +/*! + \qmlproperty Variant Qt.labs.location::MapIconObject::coordinate + + The coordinate where the icon is going to be shown. + What pixel of the icon matches the coordinate is somehow backend-dependent. + For example, due to limitations, some backends might associate the center of the icon with the + coordinate, others one of the corners. + If there is a choice, backend developers should use the center of the icon as the default anchor + point. + + The behavior is also intended to be customizable with a \l DynamicParameter, when + using backends that support anchoring arbitrary points of the icon to the coordinate. + What kind of parameter to use and how to achieve this behavior is intended to be + documented per-backend. +*/ +QGeoCoordinate QMapIconObject::coordinate() const +{ + const QMapIconObjectPrivate *d = static_cast(d_ptr.data()); + return d->coordinate(); +} + +void QMapIconObject::setContent(QVariant content) +{ + QMapIconObjectPrivate *d = static_cast(d_ptr.data()); + if (d->content() == content) + return; + + d->setContent(content); + emit contentChanged(content); +} + +void QMapIconObject::setCoordinate(const QGeoCoordinate ¢er) +{ + QMapIconObjectPrivate *d = static_cast(d_ptr.data()); + if (d->coordinate() == center) + return; + + d->setCoordinate(center); + emit coordinateChanged(center); +} + +/*! + \qmlproperty Variant Qt.labs.location::MapIconObject::size + + The size of the icon as it will be shown on the map. +*/ +QSizeF QMapIconObject::size() const +{ + const QMapIconObjectPrivate *d = static_cast(d_ptr.data()); + return d->size(); +} + + +void QMapIconObject::setSize(const QSizeF &size) +{ + QMapIconObjectPrivate *d = static_cast(d_ptr.data()); + if (d->size() == size) + return; + + d->setSize(size); + emit sizeChanged(); +} + +void QMapIconObject::setMap(QGeoMap *map) +{ + QMapIconObjectPrivate *d = static_cast(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + if (!map) { + // Map was set, now it has ben re-set to NULL, but not inside d_ptr. + // so m_map inside d_ptr can still be used to remove itself, inside the destructor. + d_ptr = new QMapIconObjectPrivateDefault(*d); + // Old pimpl deleted implicitly by QExplicitlySharedDataPointer + } +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qmapiconobject_p.h b/src/location/labs/qmapiconobject_p.h new file mode 100644 index 0000000..16b00d0 --- /dev/null +++ b/src/location/labs/qmapiconobject_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPICON_P_H +#define QGEOMAPICON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapIconObject : public QGeoMapObject +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) + Q_PROPERTY(QVariant content READ content WRITE setContent NOTIFY contentChanged) + Q_PROPERTY(QSizeF size READ size WRITE setSize NOTIFY sizeChanged) + +public: + QMapIconObject(QObject *parent = nullptr); + ~QMapIconObject() override; + + QVariant content() const; + QGeoCoordinate coordinate() const; + QSizeF size() const; + + void setContent(QVariant content); + void setCoordinate(const QGeoCoordinate &coordinate); + void setSize(const QSizeF &size); + + void setMap(QGeoMap *map) override; + +signals: + void contentChanged(QVariant content); + void coordinateChanged(QGeoCoordinate coordinate); + void sizeChanged(); +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPICON_P_H diff --git a/src/location/labs/qmapiconobject_p_p.h b/src/location/labs/qmapiconobject_p_p.h new file mode 100644 index 0000000..08a1a89 --- /dev/null +++ b/src/location/labs/qmapiconobject_p_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPICON_P_P_H +#define QGEOMAPICON_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapIconObjectPrivate : public QGeoMapObjectPrivate +{ +public: + QMapIconObjectPrivate(QGeoMapObject *q); + ~QMapIconObjectPrivate() override; + + virtual QGeoMapObject::Type type() const override final; + + virtual QGeoCoordinate coordinate() const = 0; + virtual void setCoordinate(const QGeoCoordinate &coordinate) = 0; + virtual QVariant content() const = 0; + virtual void setContent(const QVariant &content) = 0; + virtual QSizeF size() const = 0; + virtual void setSize(const QSizeF &size) = 0; + + // QGeoMapObjectPrivate interface + bool equals(const QGeoMapObjectPrivate &other) const override; +}; + +class Q_LOCATION_PRIVATE_EXPORT QMapIconObjectPrivateDefault : public QMapIconObjectPrivate +{ +public: + QMapIconObjectPrivateDefault(QGeoMapObject *q); + QMapIconObjectPrivateDefault(const QMapIconObjectPrivate &other); + ~QMapIconObjectPrivateDefault() override; + + // QGeoMapIconPrivate interface + QGeoCoordinate coordinate() const override; + void setCoordinate(const QGeoCoordinate &coordinate) override; + QVariant content() const override; + void setContent(const QVariant &content) override; + virtual QSizeF size() const override; + virtual void setSize(const QSizeF &size) override; + + // QMapIconObjectPrivate interface + QGeoMapObjectPrivate *clone() override; + +public: + QVariant m_content; + QGeoCoordinate m_coordinate; + QSizeF m_size; + +private: + QMapIconObjectPrivateDefault(const QMapIconObjectPrivateDefault &other) = delete; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPICON_P_P_H diff --git a/src/location/labs/qmapobjectview.cpp b/src/location/labs/qmapobjectview.cpp new file mode 100644 index 0000000..2ffc27b --- /dev/null +++ b/src/location/labs/qmapobjectview.cpp @@ -0,0 +1,387 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmapobjectview_p.h" +#include "qmapobjectview_p_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapObjectView + \instantiates QMapObjectView + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + \inherits QGeoMapObject + + \brief The MapObjectView is used to populate Map with map objects from a model. + + The MapObjectView is used to populate Map with map objects, either from a model or via + \l addMapObject or \l removeMapObject. + + The MapObjectView type only makes sense when contained in a Map, meaning that it will not work when added inside + other QML elements. + This can also be intended as an object layer on top of a Map. +*/ + +/* + + QMapObjectViewPrivate + +*/ + +static const QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::Asynchronous; + +QMapObjectViewPrivate::QMapObjectViewPrivate(QGeoMapObject *q) + : QGeoMapObjectPrivate(q) +{ +} + +QMapObjectViewPrivate::~QMapObjectViewPrivate() +{ + +} + +QGeoMapObject::Type QMapObjectViewPrivate::type() const +{ + return QGeoMapObject::ViewType; +} + + +/* + + QMapObjectViewPrivateDefault + +*/ + + +QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(const QMapObjectViewPrivate &other) : QMapObjectViewPrivate(other.q) +{ +} + +QMapObjectViewPrivateDefault::~QMapObjectViewPrivateDefault() +{ + +} + +QMapObjectViewPrivateDefault::QMapObjectViewPrivateDefault(QGeoMapObject *q) : QMapObjectViewPrivate(q) +{ + +} + +QGeoMapObjectPrivate *QMapObjectViewPrivateDefault::clone() +{ + return new QMapObjectViewPrivateDefault(*this); +} + +/* + + QMapObjectView + +*/ + + +QMapObjectView::QMapObjectView(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer(new QMapObjectViewPrivateDefault(this)), parent) +{ + +} + +QMapObjectView::~QMapObjectView() +{ + flushDelegateModel(); + flushUserAddedMapObjects(); +} + +QList QMapObjectView::geoMapObjectChildren() const +{ + auto kids = QGeoMapObject::geoMapObjectChildren(); + auto size = m_instantiatedMapObjects.count(); + for (int i = 0; i < size; ++i) { + auto obj = qobject_cast(m_instantiatedMapObjects[i]); + if (obj) + kids << obj; + } + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj) + kids << obj; + } + return kids; +} + +void QMapObjectView::classBegin() +{ + QQmlContext *ctx = qmlContext(this); + m_delegateModel = new QQmlDelegateModel(ctx, this); + m_delegateModel->classBegin(); + + QQmlInstanceModel *model = m_delegateModel; + connect(model, &QQmlInstanceModel::modelUpdated, this, &QMapObjectView::modelUpdated); + connect(model, &QQmlInstanceModel::createdItem, this, &QMapObjectView::createdItem); + connect(model, &QQmlInstanceModel::destroyingItem, this, &QMapObjectView::destroyingItem); + connect(model, &QQmlInstanceModel::initItem, this, &QMapObjectView::initItem); +} + +void QMapObjectView::componentComplete() +{ + QGeoMapObject::componentComplete(); + if (m_delegate) + m_delegateModel->setDelegate(m_delegate); + if (m_model.isValid()) + m_delegateModel->setModel(m_model); + m_delegateModel->componentComplete(); +} + +/*! + \qmlproperty Variant Qt.labs.location::MapObjectView::model + + This property holds the model that provides data used for creating the map items defined by the + delegate. Only QAbstractItemModel based models are supported. +*/ +QVariant QMapObjectView::model() const +{ + return m_model; +} + +/*! + \qmlproperty Component Qt.labs.location::MapObjectView::delegate + + This property holds the delegate which defines how each item in the + model should be displayed. The Component must contain exactly one + QGeoMapObject -derived object as the root object. +*/ +QQmlComponent *QMapObjectView::delegate() const +{ + return m_delegate; +} + +void QMapObjectView::setModel(QVariant model) +{ + if (m_model == model) + return; + m_model = model; + + if (d_ptr->m_componentCompleted) + m_delegateModel->setModel(model); + + emit modelChanged(model); +} + +void QMapObjectView::setDelegate(QQmlComponent *delegate) +{ + if (m_delegate == delegate) + return; + m_delegate = delegate; + + if (d_ptr->m_componentCompleted) + m_delegateModel->setDelegate(delegate); + + emit delegateChanged(delegate); +} + +/*! + \qmlmethod void Qt.labs.location::MapObjectView::addMapObject(MapObject object) + + Adds the given \a object to the MapObjectView (for example MapIconObject, MapRouteObject), and, + indirectly, to the underlying map. If the object already is on the MapObjectView, it will not be added again. + + \sa removeMapObject +*/ +void QMapObjectView::addMapObject(QGeoMapObject *object) +{ + if (m_userAddedMapObjects.indexOf(object) < 0) + m_userAddedMapObjects.append(object); + if (map() && object->map() != map()) + object->setMap(map()); +} + +/*! + \qmlmethod void Qt.labs.location::MapObjectView::removeMapObject(MapObject object) + + Removes the given \a object from the MapObjectView (for example MapIconObject, MapRouteObject), and, + indirectly, from the underlying map. + + \sa addMapObject +*/ +void QMapObjectView::removeMapObject(QGeoMapObject *object) +{ + int idx = m_userAddedMapObjects.indexOf(object); + if ( idx >= 0) { + object->setMap(nullptr); + m_userAddedMapObjects.remove(idx); + } +} + +void QMapObjectView::destroyingItem(QObject * /*object*/) +{ + +} + +void QMapObjectView::initItem(int /*index*/, QObject * /*object*/) +{ + +} + +void QMapObjectView::modelUpdated(const QQmlChangeSet &changeSet, bool reset) +{ + // move changes are expressed as one remove + one insert, with the same moveId. + // For simplicity, they will be treated as remove + insert. + // Changes will be also ignored, as they represent only data changes, not layout changes + if (reset) { // Assuming this means "remove everything already instantiated" + flushDelegateModel(); + } else { + // Remove map objects from the back to the front to retain the mapping to what is received from the changesets + const QVector &removes = changeSet.removes(); + std::map mapRemoves; + for (int i = 0; i < removes.size(); i++) + mapRemoves.insert(std::pair(removes.at(i).start(), i)); + + for (auto rit = mapRemoves.rbegin(); rit != mapRemoves.rend(); ++rit) { + const QQmlChangeSet::Change &c = removes.at(rit->second); + for (int idx = c.end() - 1; idx >= c.start(); --idx) + removeMapObjectFromMap(idx); + } + } + + for (const QQmlChangeSet::Change &c: changeSet.inserts()) { + for (int idx = c.start(); idx < c.end(); idx++) { + m_instantiatedMapObjects.insert(idx, nullptr); + QGeoMapObject *mo = qobject_cast(m_delegateModel->object(idx, incubationMode)); + if (mo) // if not, a createdItem signal will be emitted. + addMapObjectToMap(mo, idx); + } + } +} + +void QMapObjectView::addMapObjectToMap(QGeoMapObject *object, int index) +{ + if (!object) + return; + + m_instantiatedMapObjects[index] = object; + if (map()) + object->setMap(map()); + else + m_pendingMapObjects << object; + + // ToDo: + // Figure out the proper way to replace "mo->setVisible(visible());". Options: + // - simply leave it to the user to set up a property binding + // - set up a property binding automatically + // - add a viewVisibility member to QGeoMapObject that gets combined at all times, + // and a connection for it. +} + +void QMapObjectView::removeMapObjectFromMap(int index) +{ + if (index >= 0 && index < m_instantiatedMapObjects.size()) { + QGeoMapObject *mo = m_instantiatedMapObjects.takeAt(index); + if (!mo) { + m_delegateModel->cancel(index); + return; + } + mo->setMap(nullptr); + m_delegateModel->release(mo); + } +} + +// See QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) doc +// for explanation on when createdItem is emitted. +void QMapObjectView::createdItem(int index, QObject * /*object*/) +{ + if (m_instantiatedMapObjects.at(index)) + return; // The first call to object() apparently returned a valid item. Don't call it again. + + // If here, according to the documentation above, object() should be called again for index, + // or else, it will be destroyed exiting this scope + QGeoMapObject *mo = nullptr; + mo = qobject_cast(m_delegateModel->object(index, incubationMode)); + if (mo) + addMapObjectToMap(mo, index); +} + + +void QMapObjectView::flushDelegateModel() +{ + // Backward as removeItemFromMap modifies m_instantiatedItems + for (int i = m_instantiatedMapObjects.size() -1; i >= 0 ; i--) + removeMapObjectFromMap(i); +} + +void QMapObjectView::flushUserAddedMapObjects() +{ + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj) + obj->setMap(nullptr); // obj parent might not be this. If so, it would not be destroyed by destroying this view. + } +} + +void QMapObjectView::setMap(QGeoMap *map) +{ + QMapObjectViewPrivate *d = static_cast(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + for (int i = 0; i < m_userAddedMapObjects.size(); ++i) { + auto obj = m_userAddedMapObjects.at(i); + if (obj && obj->map() != map) + obj->setMap(map); + } + + if (!map) { + // Map was set, now it has ben re-set to NULL + flushDelegateModel(); + flushUserAddedMapObjects(); + d_ptr = new QMapObjectViewPrivateDefault(*d); + } else if (d->m_componentCompleted) { + // Map was null, now it's set AND delegateModel is already complete. + // some delegates may have been incubated but not added to the map. + for (int i = 0; i < m_pendingMapObjects.size(); ++i) { + auto obj = m_pendingMapObjects.at(i); + if (obj && obj->map() != map) + obj->setMap(map); + } + m_pendingMapObjects.clear(); + } +} + +QT_END_NAMESPACE + diff --git a/src/location/labs/qmapobjectview_p.h b/src/location/labs/qmapobjectview_p.h new file mode 100644 index 0000000..49b8088 --- /dev/null +++ b/src/location/labs/qmapobjectview_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPOBJECTVIEW_P_H +#define QMAPOBJECTVIEW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlDelegateModel; +class QMapObjectViewPrivate; +class QQmlChangeSet; +class Q_LOCATION_PRIVATE_EXPORT QMapObjectView : public QGeoMapObject +{ + Q_OBJECT + Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + Q_INTERFACES(QQmlParserStatus) +public: + QMapObjectView(QObject *parent = nullptr); + ~QMapObjectView() override; + + // QGeoMapObject interface + QList geoMapObjectChildren() const override; + void setMap(QGeoMap *map) override; + + // QQmlParserStatus interface + void classBegin() override; + void componentComplete() override; + + QVariant model() const; + void setModel(QVariant model); + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent * delegate); + +public Q_SLOTS: + // The dynamic API that matches Map.add/remove MapItem + void addMapObject(QGeoMapObject *object); + void removeMapObject(QGeoMapObject *object); + +signals: + void modelChanged(QVariant model); + void delegateChanged(QQmlComponent * delegate); + +protected Q_SLOTS: + void destroyingItem(QObject *object); + void initItem(int index, QObject *object); + void createdItem(int index, QObject *object); + void modelUpdated(const QQmlChangeSet &changeSet, bool reset); + +protected: + void addMapObjectToMap(QGeoMapObject *object, int index); + void removeMapObjectFromMap(int index); + void flushDelegateModel(); + void flushUserAddedMapObjects(); + + QVariant m_model; + QQmlComponent *m_delegate = nullptr; + QQmlDelegateModel *m_delegateModel = nullptr; + QVector> m_instantiatedMapObjects; + QVector> m_pendingMapObjects; + QVector> m_userAddedMapObjects; // A third list containing the objects dynamically added through addMapObject +}; + +QT_END_NAMESPACE + +#endif // QMAPOBJECTVIEW_P_H diff --git a/src/location/labs/qmapobjectview_p_p.h b/src/location/labs/qmapobjectview_p_p.h new file mode 100644 index 0000000..7550e20 --- /dev/null +++ b/src/location/labs/qmapobjectview_p_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPOBJECTVIEW_P_P_H +#define QMAPOBJECTVIEW_P_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQmlDelegateModel; +class QGeoMap; +class Q_LOCATION_PRIVATE_EXPORT QMapObjectViewPrivate : public QGeoMapObjectPrivate +{ +public: + QMapObjectViewPrivate(QGeoMapObject *q); + ~QMapObjectViewPrivate() override; + + virtual QGeoMapObject::Type type() const override final; +}; + +class Q_LOCATION_PRIVATE_EXPORT QMapObjectViewPrivateDefault : public QMapObjectViewPrivate +{ +public: + QMapObjectViewPrivateDefault(QGeoMapObject *q); + QMapObjectViewPrivateDefault(const QMapObjectViewPrivate &other); + ~QMapObjectViewPrivateDefault() override; + + + // QGeoMapObjectPrivate interface +public: + QGeoMapObjectPrivate *clone() override; +}; + +QT_END_NAMESPACE + +#endif // QMAPOBJECTVIEW_P_P_H diff --git a/src/location/labs/qmappolygonobject.cpp b/src/location/labs/qmappolygonobject.cpp new file mode 100644 index 0000000..1d49589 --- /dev/null +++ b/src/location/labs/qmappolygonobject.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmappolygonobject_p.h" +#include "qmappolygonobject_p_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapPolygonObject + \instantiates QMapPolygonObject + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + \inherits QGeoMapObject + + \brief The MapPolygonObject displays a polygon on a Map. + + The MapPolygonObject displays a polygon on a Map. + The MapPolygonObject type only makes sense when contained in a Map or in a \l MapObjectView. +*/ + +QMapPolygonObjectPrivate::QMapPolygonObjectPrivate(QGeoMapObject *q) : QGeoMapObjectPrivate(q) +{ + +} + +QMapPolygonObjectPrivate::~QMapPolygonObjectPrivate() +{ + +} + +QMapPolygonObjectPrivateDefault::QMapPolygonObjectPrivateDefault(QGeoMapObject *q) : QMapPolygonObjectPrivate(q) +{ + +} + +QMapPolygonObjectPrivateDefault::QMapPolygonObjectPrivateDefault(const QMapPolygonObjectPrivate &other) : QMapPolygonObjectPrivate(other.q) +{ + m_path = other.path(); + m_borderColor = other.borderColor(); + m_fillColor = other.fillColor(); + m_borderWidth = other.borderWidth(); +} + +QMapPolygonObjectPrivateDefault::~QMapPolygonObjectPrivateDefault() +{ + +} + +QGeoMapObject::Type QMapPolygonObjectPrivate::type() const +{ + return QGeoMapObject::PolygonType; +} + +QList QMapPolygonObjectPrivateDefault::path() const +{ + return m_path; +} + +void QMapPolygonObjectPrivateDefault::setPath(const QList &path) +{ + m_path = path; +} + +QColor QMapPolygonObjectPrivateDefault::fillColor() const +{ + return m_fillColor; +} + +void QMapPolygonObjectPrivateDefault::setFillColor(const QColor &color) +{ + m_fillColor = color; +} + +QColor QMapPolygonObjectPrivateDefault::borderColor() const +{ + return m_borderColor; +} + +void QMapPolygonObjectPrivateDefault::setBorderColor(const QColor &color) +{ + m_borderColor = color; +} + +qreal QMapPolygonObjectPrivateDefault::borderWidth() const +{ + return m_borderWidth; +} + +void QMapPolygonObjectPrivateDefault::setBorderWidth(qreal width) +{ + m_borderWidth = width; +} + +QGeoMapObjectPrivate *QMapPolygonObjectPrivateDefault::clone() +{ + return new QMapPolygonObjectPrivateDefault(static_cast(*this)); +} + +bool QMapPolygonObjectPrivate::equals(const QGeoMapObjectPrivate &other) const +{ + if (other.type() != type()) // This check might be unnecessary, depending on how equals gets used + return false; + + const QMapPolygonObjectPrivate &o = static_cast(other); + return (QGeoMapObjectPrivate::equals(o) + && path() == o.path() + && borderColor() == o.borderColor() + && fillColor() == o.fillColor() + && borderWidth() == o.borderWidth()); +} + + + + +QMapPolygonObject::QMapPolygonObject(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer(new QMapPolygonObjectPrivateDefault(this)), parent) +{ + QMapPolygonObjectPrivate *d = static_cast(d_ptr.data()); + d->setBorderColor(QColor(Qt::black)); // These are QDeclarativeMapLineProperties defaults + d->setBorderWidth(1.0); +} + +QMapPolygonObject::~QMapPolygonObject() +{} + +/*! + \qmlproperty VariantList Qt.labs.location::MapPolygonObject::path + + This property holds the ordered list of coordinates which + define the polygon border. +*/ +QVariantList QMapPolygonObject::path() const +{ + QVariantList p; + for (const QGeoCoordinate &c: static_cast(d_ptr.data())->path()) + p << QVariant::fromValue(c); + return p; +} + +void QMapPolygonObject::setPath(const QVariantList &path) +{ + QList p; + bool ok = false; + for (const auto &c: path) { + const QGeoCoordinate coord = parseCoordinate(c, &ok); + if (ok) + p << coord; + } + auto pimpl = static_cast(d_ptr.data()); + if (p != pimpl->path()) { + pimpl->setPath(p); + emit pathChanged(); + } +} + +/*! + \qmlproperty color Qt.labs.location::MapPolygonObject::color + + This property holds the fill color of the polygon when drawn. For no fill, + use a transparent color. +*/ +QColor QMapPolygonObject::color() const +{ + return static_cast(d_ptr.data())->fillColor(); +} + +/*! + \qmlpropertygroup Qt.labs.location::MapPolygonObject::border + \qmlproperty int MapPolygonObject::border.width + \qmlproperty color MapPolygonObject::border.color + + This property is part of the border property group. The border + property group holds the width and color used to draw the border. + + The width is in pixels and is independent of the zoom level of the map. + The default values correspond to a black border with a width of 1 pixel. + + For no border, use a width of 0 or a transparent color. +*/ +QDeclarativeMapLineProperties *QMapPolygonObject::border() +{ + if (!m_border) { + m_border = new QDeclarativeMapLineProperties; + connect(m_border, &QDeclarativeMapLineProperties::colorChanged, this, [this](const QColor &color){ + static_cast(d_ptr.data())->setBorderColor(color); + }); + connect(m_border, &QDeclarativeMapLineProperties::widthChanged, this, [this](qreal width){ + static_cast(d_ptr.data())->setBorderWidth(width); + }); + } + return m_border; +} + +void QMapPolygonObject::setColor(const QColor &fillColor) +{ + auto ptr = static_cast(d_ptr.data()); + + if (ptr->fillColor() == fillColor) + return; + + ptr->setFillColor(fillColor); + emit colorChanged(); +} + +void QMapPolygonObject::setMap(QGeoMap *map) +{ + QMapPolygonObjectPrivate *d = static_cast(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + if (!map) { + // Map was set, now it has ben re-set to NULL, but not inside d_ptr. + // so m_map inside d_ptr can still be used to remove itself, inside the destructor. + d_ptr = new QMapPolygonObjectPrivateDefault(*d); + // Old pimpl deleted implicitly by QExplicitlySharedDataPointer + } +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qmappolygonobject_p.h b/src/location/labs/qmappolygonobject_p.h new file mode 100644 index 0000000..03eef58 --- /dev/null +++ b/src/location/labs/qmappolygonobject_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPPOLYGONOBJECT_P_H +#define QMAPPOLYGONOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapPolygonObject : public QGeoMapObject +{ + Q_OBJECT + + Q_PROPERTY(QVariantList path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *border READ border CONSTANT) + +public: + QMapPolygonObject(QObject *parent = nullptr); + ~QMapPolygonObject() override; + + QVariantList path() const; + void setPath(const QVariantList &path); + + QColor color() const; + void setColor(const QColor &color); + + QDeclarativeMapLineProperties *border(); + void setMap(QGeoMap *map) override; + +signals: + void pathChanged(); + void colorChanged(); + +protected: + QDeclarativeMapLineProperties *m_border; +}; + +QT_END_NAMESPACE + +#endif // QMAPPOLYGONOBJECT_P_H diff --git a/src/location/labs/qmappolygonobject_p_p.h b/src/location/labs/qmappolygonobject_p_p.h new file mode 100644 index 0000000..d7e95d4 --- /dev/null +++ b/src/location/labs/qmappolygonobject_p_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPPOLYGONOBJECT_P_P_H +#define QMAPPOLYGONOBJECT_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapPolygonObjectPrivate : public QGeoMapObjectPrivate +{ +public: + QMapPolygonObjectPrivate(QGeoMapObject *q); + ~QMapPolygonObjectPrivate() override; + + virtual QGeoMapObject::Type type() const override final; + + virtual QList path() const = 0; + virtual void setPath(const QList &path) = 0; + virtual QColor fillColor() const = 0; + virtual void setFillColor(const QColor &color) = 0; + virtual QColor borderColor() const = 0; + virtual void setBorderColor(const QColor &color) = 0; + virtual qreal borderWidth() const = 0; + virtual void setBorderWidth(qreal width) = 0; + + // QGeoMapObjectPrivate interface + bool equals(const QGeoMapObjectPrivate &other) const override; +}; + +class Q_LOCATION_PRIVATE_EXPORT QMapPolygonObjectPrivateDefault : public QMapPolygonObjectPrivate +{ +public: + QMapPolygonObjectPrivateDefault(QGeoMapObject *q); + QMapPolygonObjectPrivateDefault(const QMapPolygonObjectPrivate &other); + ~QMapPolygonObjectPrivateDefault() override; + + // QMapPolygonObjectPrivate interface + QList path() const override; + void setPath(const QList &path) override; + QColor fillColor() const override; + void setFillColor(const QColor &color) override; + QColor borderColor() const override; + void setBorderColor(const QColor &color) override; + qreal borderWidth() const override; + void setBorderWidth(qreal width) override; + + // QGeoMapObjectPrivate interface + QGeoMapObjectPrivate *clone() override; + +public: + QList m_path; + QColor m_borderColor; + QColor m_fillColor = Qt::transparent; + qreal m_borderWidth = 0; + +private: + QMapPolygonObjectPrivateDefault(const QMapPolygonObjectPrivateDefault &other) = delete; +}; + +QT_END_NAMESPACE + + +#endif // QMAPPOLYGONOBJECT_P_P_H diff --git a/src/location/labs/qmappolylineobject.cpp b/src/location/labs/qmappolylineobject.cpp new file mode 100644 index 0000000..1c35196 --- /dev/null +++ b/src/location/labs/qmappolylineobject.cpp @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmappolylineobject_p.h" +#include "qmappolylineobject_p_p.h" +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapPolylineObject + \instantiates QMapPolylineObject + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + \inherits QGeoMapObject + + \brief The MapPolylineObject displays a polyline on a Map. + + The MapPolylineObject displays a polyline on a Map. + The MapPolylineObject type only makes sense when contained in a Map or in a \l MapObjectView. +*/ + +QMapPolylineObjectPrivate::QMapPolylineObjectPrivate(QGeoMapObject *q) : QGeoMapObjectPrivate(q) +{ + +} + +QMapPolylineObjectPrivate::~QMapPolylineObjectPrivate() +{ + +} + +QGeoMapObject::Type QMapPolylineObjectPrivate::type() const +{ + return QGeoMapObject::PolylineType; +} + +QMapPolylineObjectPrivateDefault::QMapPolylineObjectPrivateDefault(QGeoMapObject *q) : QMapPolylineObjectPrivate(q) +{ + +} + +QMapPolylineObjectPrivateDefault::QMapPolylineObjectPrivateDefault(const QMapPolylineObjectPrivate &other) : QMapPolylineObjectPrivate(other.q) +{ + m_path = other.path(); + m_color = other.color(); + m_width = other.width(); +} + +QMapPolylineObjectPrivateDefault::~QMapPolylineObjectPrivateDefault() +{ + +} + +QList QMapPolylineObjectPrivateDefault::path() const +{ + return m_path; +} + +void QMapPolylineObjectPrivateDefault::setPath(const QList &path) +{ + m_path = path; +} + +QColor QMapPolylineObjectPrivateDefault::color() const +{ + return m_color; +} + +void QMapPolylineObjectPrivateDefault::setColor(const QColor &color) +{ + m_color = color; +} + +qreal QMapPolylineObjectPrivateDefault::width() const +{ + return m_width; +} + +void QMapPolylineObjectPrivateDefault::setWidth(qreal width) +{ + m_width = width; +} + +bool QMapPolylineObjectPrivate::equals(const QGeoMapObjectPrivate &other) const +{ + if (other.type() != type()) // This check might be unnecessary, depending on how equals gets used + return false; + + const QMapPolylineObjectPrivate &o = static_cast(other); + return (QGeoMapObjectPrivate::equals(o) + && path() == o.path() + && color() == o.color() + && width() == o.width()); +} + +QGeoMapObjectPrivate *QMapPolylineObjectPrivateDefault::clone() +{ + return new QMapPolylineObjectPrivateDefault(static_cast(*this)); +} + +QMapPolylineObject::QMapPolylineObject(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer(new QMapPolylineObjectPrivateDefault(this)), parent) +{ + QMapPolylineObjectPrivate *d = static_cast(d_ptr.data()); + d->setColor(QColor(Qt::black)); // These are QDeclarativeMapLineProperties defaults + d->setWidth(1.0); +} + +QMapPolylineObject::~QMapPolylineObject() +{} + +/*! + \qmlproperty VariantList Qt.labs.location::MapPolylineObject::path + + This property holds the ordered list of coordinates which + define the polyline. +*/ +QVariantList QMapPolylineObject::path() const +{ + QVariantList p; + for (const QGeoCoordinate &c: static_cast(d_ptr.data())->path()) + p << QVariant::fromValue(c); + return p; +} + +/*! + \qmlpropertygroup Qt.labs.location::MapPolylineObject::line + \qmlproperty int MapPolylineObject::line.width + \qmlproperty color MapPolylineObject::line.color + + This property is part of the line property group. The line + property group holds the width and color used to draw the line. + + The width is in pixels and is independent of the zoom level of the map. + The default values correspond to a black border with a width of 1 pixel. + + For no line, use a width of 0 or a transparent color. +*/ +QDeclarativeMapLineProperties *QMapPolylineObject::border() +{ + if (!m_border) { + m_border = new QDeclarativeMapLineProperties; + connect(m_border, &QDeclarativeMapLineProperties::colorChanged, this, [this](const QColor &color){ + static_cast(d_ptr.data())->setColor(color); + }); + connect(m_border, &QDeclarativeMapLineProperties::widthChanged, this, [this](qreal width){ + static_cast(d_ptr.data())->setWidth(width); + }); + } + return m_border; +} + +void QMapPolylineObject::setPath(const QVariantList &path) +{ + QList p; + bool ok = false; + for (const auto &c: path) { + const QGeoCoordinate coord = parseCoordinate(c, &ok); + if (ok) + p << coord; + } + auto pimpl = static_cast(d_ptr.data()); + if (p != pimpl->path()) { + pimpl->setPath(p); + emit pathChanged(); + } +} + +void QMapPolylineObject::setMap(QGeoMap *map) +{ + QMapPolylineObjectPrivate *d = static_cast(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + if (!map) { + // Map was set, now it has ben re-set to NULL, but not inside d_ptr. + // so m_map inside d_ptr can still be used to remove itself, inside the destructor. + d_ptr = new QMapPolylineObjectPrivateDefault(*d); + // Old pimpl deleted implicitly by QExplicitlySharedDataPointer + } +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qmappolylineobject_p.h b/src/location/labs/qmappolylineobject_p.h new file mode 100644 index 0000000..68312fb --- /dev/null +++ b/src/location/labs/qmappolylineobject_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPPOLYLINEOBJECT_P_H +#define QMAPPOLYLINEOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapPolylineObject : public QGeoMapObject +{ + Q_OBJECT + + Q_PROPERTY(QVariantList path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QDeclarativeMapLineProperties *line READ border CONSTANT) + +public: + QMapPolylineObject(QObject *parent = nullptr); + ~QMapPolylineObject() override; + + QVariantList path() const; + void setPath(const QVariantList &path); + + QDeclarativeMapLineProperties *border(); + void setMap(QGeoMap *map) override; + +signals: + void pathChanged(); + +protected: + QDeclarativeMapLineProperties *m_border = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QMAPPOLYLINEOBJECT_P_H diff --git a/src/location/labs/qmappolylineobject_p_p.h b/src/location/labs/qmappolylineobject_p_p.h new file mode 100644 index 0000000..1d5919d --- /dev/null +++ b/src/location/labs/qmappolylineobject_p_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPPOLYLINEOBJECT_P_P_H +#define QMAPPOLYLINEOBJECT_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapPolylineObjectPrivate : public QGeoMapObjectPrivate +{ +public: + QMapPolylineObjectPrivate(QGeoMapObject *q); + ~QMapPolylineObjectPrivate() override; + + virtual QGeoMapObject::Type type() const override final; + + virtual QList path() const = 0; + virtual void setPath(const QList &path) = 0; + virtual QColor color() const = 0; + virtual void setColor(const QColor &color) = 0; + virtual qreal width() const = 0; + virtual void setWidth(qreal width) = 0; + + // QGeoMapObjectPrivate interface + bool equals(const QGeoMapObjectPrivate &other) const override; +}; + +class Q_LOCATION_PRIVATE_EXPORT QMapPolylineObjectPrivateDefault : public QMapPolylineObjectPrivate +{ +public: + QMapPolylineObjectPrivateDefault(QGeoMapObject *q); + QMapPolylineObjectPrivateDefault(const QMapPolylineObjectPrivate &other); + ~QMapPolylineObjectPrivateDefault() override; + + // QGeoMapPolylinePrivate interface + QList path() const override; + void setPath(const QList &path) override; + QColor color() const override; + void setColor(const QColor &color) override; + qreal width() const override; + void setWidth(qreal width) override; + + // QGeoMapObjectPrivate interface + QGeoMapObjectPrivate *clone() override; + +public: + QList m_path; + QColor m_color; + qreal m_width = 0; + +private: + QMapPolylineObjectPrivateDefault(const QMapPolylineObjectPrivateDefault &other) = delete; +}; + +QT_END_NAMESPACE + + +#endif // QMAPPOLYLINEOBJECT_P_P_H diff --git a/src/location/labs/qmaprouteobject.cpp b/src/location/labs/qmaprouteobject.cpp new file mode 100644 index 0000000..c3365d3 --- /dev/null +++ b/src/location/labs/qmaprouteobject.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qmaprouteobject_p.h" +#include "qmaprouteobject_p_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype MapRouteObject + \instantiates QMapRouteObject + \inqmlmodule Qt.labs.location + \ingroup qml-QtLocation5-maps + \inherits QGeoMapObject + + \brief The MapRouteObject displays a geographical route on a Map. + + The MapRouteObject type displays a Route obtained through a RouteModel or + other means, on the Map as a Polyline following the path of the Route. +*/ + +/* + + QGeoMapRoutePrivate + +*/ + +QMapRouteObjectPrivate::QMapRouteObjectPrivate(QGeoMapObject *q) : QGeoMapObjectPrivate(q) +{ + +} + +QMapRouteObjectPrivate::QMapRouteObjectPrivate(const QMapRouteObjectPrivate &other) : QGeoMapObjectPrivate(other) +{ + // QGeoMapRoutePrivate doesn't contain anything because QGeoRoute has already everything necessary. +} + +QMapRouteObjectPrivate::~QMapRouteObjectPrivate() +{ + +} + +QGeoMapObject::Type QMapRouteObjectPrivate::type() const +{ + return QGeoMapObject::RouteType; +} + +QDeclarativeGeoRoute *QMapRouteObjectPrivate::declarativeGeoRoute() const +{ + const QMapRouteObject *r = static_cast(q); + return r->m_route; +} + +/*! + \qmlproperty Route Qt.labs.location::MapRouteObject::route + + This property holds the route to be drawn. +*/ +QGeoRoute QMapRouteObjectPrivate::route() const +{ + const QDeclarativeGeoRoute *r = declarativeGeoRoute(); + if (r) + return r->route(); + return {}; +} + +void QMapRouteObjectPrivate::setRoute(const QDeclarativeGeoRoute *route) +{ + Q_UNUSED(route) +} + +bool QMapRouteObjectPrivate::equals(const QGeoMapObjectPrivate &other) const +{ + if (other.type() != type()) // This check might be unnecessary, depending on how equals gets used + return false; + + const QMapRouteObjectPrivate &o = static_cast(other); + return (QGeoMapObjectPrivate::equals(o) + && route() == o.route()); // Could also be done shallow, comparing declarativeGeoRoute() +} + +QGeoMapObjectPrivate *QMapRouteObjectPrivate::clone() +{ + return new QMapRouteObjectPrivate(*this); +} + + +/* + + QGeoMapRoute + +*/ + +QMapRouteObject::QMapRouteObject(QObject *parent) + : QGeoMapObject(QExplicitlySharedDataPointer(new QMapRouteObjectPrivate(this)), parent) +{ + +} + +QMapRouteObject::~QMapRouteObject() +{ + +} + +QDeclarativeGeoRoute *QMapRouteObject::route() const +{ + return m_route; +} + +QGeoRoute QMapRouteObject::geoRoute() const +{ + if (m_route) + return m_route->route(); + return {}; +} + +void QMapRouteObject::setRoute(QDeclarativeGeoRoute *route) +{ + if (route == m_route) + return; +// if ((!m_route && !route) || (m_route && route && m_route->route() == route->route())) +// return; + + m_route = route; + QMapRouteObjectPrivate *d = static_cast(d_ptr.data()); + d->setRoute(route); + emit routeChanged(route); +} + +void QMapRouteObject::setMap(QGeoMap *map) +{ + QMapRouteObjectPrivate *d = static_cast(d_ptr.data()); + if (d->m_map == map) + return; + + QGeoMapObject::setMap(map); // This is where the specialized pimpl gets created and injected + + if (!map) { + // Map was set, now it has ben re-set to NULL, but not inside d_ptr. + // so m_map inside d_ptr can still be used to remove itself, inside the destructor. + d_ptr = new QMapRouteObjectPrivate(*d); + // Old pimpl deleted implicitly by QExplicitlySharedDataPointer + } +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qmaprouteobject_p.h b/src/location/labs/qmaprouteobject_p.h new file mode 100644 index 0000000..dcc3580 --- /dev/null +++ b/src/location/labs/qmaprouteobject_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEMAPROUTEDELEGATE_P_H +#define QDECLARATIVEMAPROUTEDELEGATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDeclarativeGeoRoute; +class QGeoRoute; +class QMapRouteObjectPrivate; +class Q_LOCATION_PRIVATE_EXPORT QMapRouteObject : public QGeoMapObject +{ + Q_OBJECT + Q_PROPERTY(QDeclarativeGeoRoute *route READ route WRITE setRoute NOTIFY routeChanged) + +public: + explicit QMapRouteObject(QObject *parent = nullptr); + ~QMapRouteObject() override; + + QDeclarativeGeoRoute *route() const; + QGeoRoute geoRoute() const; + + void setMap(QGeoMap *map) override; + void setRoute(QDeclarativeGeoRoute * route); + +signals: + void routeChanged(QDeclarativeGeoRoute * route); + +protected: + QDeclarativeGeoRoute *m_route = nullptr; + + friend class QMapRouteObjectPrivate; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QMapRouteObject) + +#endif // QDECLARATIVEMAPROUTEDELEGATE_P_H diff --git a/src/location/labs/qmaprouteobject_p_p.h b/src/location/labs/qmaprouteobject_p_p.h new file mode 100644 index 0000000..e01b1cc --- /dev/null +++ b/src/location/labs/qmaprouteobject_p_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPROUTE_P_P_H +#define QGEOMAPROUTE_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +QT_BEGIN_NAMESPACE + +class QGeoRoute; + +class Q_LOCATION_PRIVATE_EXPORT QMapRouteObjectPrivate : public QGeoMapObjectPrivate +{ +public: + QMapRouteObjectPrivate(QGeoMapObject *q); + QMapRouteObjectPrivate(const QMapRouteObjectPrivate &other); + ~QMapRouteObjectPrivate() override; + + virtual QGeoMapObject::Type type() const override final; + + QDeclarativeGeoRoute *declarativeGeoRoute() const; + + virtual QGeoRoute route() const; + virtual void setRoute(const QDeclarativeGeoRoute *route); + + // QGeoMapObjectPrivate interface + bool equals(const QGeoMapObjectPrivate &other) const override; + QGeoMapObjectPrivate *clone() override; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPROUTE_P_P_H diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp new file mode 100644 index 0000000..e0e3a6d --- /dev/null +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapobjectqsgsupport_p.h" +#include + +QT_BEGIN_NAMESPACE + +static int findMapObject(QGeoMapObject *o, const QList &list) +{ + for (int i = 0; i < list.size(); ++i) + { + if (list.at(i).object.data() == o) + return i; + } + return -1; +} + +bool QGeoMapObjectQSGSupport::createMapObjectImplementation(QGeoMapObject *obj, QGeoMapPrivate *d) +{ + QExplicitlySharedDataPointer pimpl = + QExplicitlySharedDataPointer(d->createMapObjectImplementation(obj)); + if (pimpl.constData()) { + bool res = obj->setImplementation(pimpl); + if (res) + emit m_map->sgNodeChanged(); + return res; + } + return false; +} + +QGeoMapObjectPrivate *QGeoMapObjectQSGSupport::createMapObjectImplementationPrivate(QGeoMapObject *obj) +{ + QGeoMapObjectPrivate *res = nullptr; + + { + QQSGMapObject *sgo = nullptr; + switch (obj->type()) { + case QGeoMapObject::PolylineType: { + QMapPolylineObjectPrivate &oldImpl = static_cast(*obj->implementation()); + QMapPolylineObjectPrivateQSG *pimpl = + new QMapPolylineObjectPrivateQSG(oldImpl); + sgo = pimpl; + res = pimpl; + break; + } + case QGeoMapObject::PolygonType: { + QMapPolygonObjectPrivate &oldImpl = static_cast(*obj->implementation()); + QMapPolygonObjectPrivateQSG *pimpl = + new QMapPolygonObjectPrivateQSG(oldImpl); + sgo = pimpl; + res = pimpl; + break; + } + case QGeoMapObject::CircleType: { + QMapCircleObjectPrivate &oldImpl = static_cast(*obj->implementation()); + QMapCircleObjectPrivateQSG *pimpl = + new QMapCircleObjectPrivateQSG(oldImpl); + sgo = pimpl; + res = pimpl; + break; + } + case QGeoMapObject::RouteType: { + QMapRouteObjectPrivate &oldImpl = static_cast(*obj->implementation()); + QMapRouteObjectPrivateQSG *pimpl = + new QMapRouteObjectPrivateQSG(oldImpl); + sgo = pimpl; + res = pimpl; + break; + } + case QGeoMapObject::IconType: { + QMapIconObjectPrivate &oldImpl = static_cast(*obj->implementation()); + QMapIconObjectPrivateQSG *pimpl = + new QMapIconObjectPrivateQSG(oldImpl); + sgo = pimpl; + res = pimpl; + break; + } + default: + // Use the following warning only for debugging purposes. + // qWarning() << "QGeoMapObjectQSGSupport::createMapObjectImplementationPrivate: not instantiating pimpl for unsupported object type " << obj->type(); + break; + } + + if (res) { + QPointer p(obj); + MapObject mo(p, sgo); + m_pendingMapObjects << mo; + } + } + return res; +} + +QList QGeoMapObjectQSGSupport::mapObjects() const +{ + return QList(); +} + +void QGeoMapObjectQSGSupport::removeMapObject(QGeoMapObject *obj) +{ + int idx = findMapObject(obj, m_mapObjects); + if (idx >= 0) { + const MapObject &mo = m_mapObjects.takeAt(idx); + obj->disconnect(m_map); + m_removedMapObjects << mo; + emit m_map->sgNodeChanged(); + } else { + idx = findMapObject(obj, m_pendingMapObjects); + if (idx >= 0) { + m_pendingMapObjects.removeAt(idx); + obj->disconnect(m_map); + } else { + // obj not here. + } + } +} + +void QGeoMapObjectQSGSupport::updateMapObjects(QSGNode *root, QQuickWindow *window) +{ + for (int i = 0; i < m_removedMapObjects.size(); ++i) { + MapObject mo = m_removedMapObjects[i]; + if (mo.qsgNode) { + root->removeChildNode(mo.qsgNode); + delete mo.qsgNode; + mo.qsgNode = nullptr; + // mo.sgObject is now invalid as it is destroyed right after appending + // mo to m_removedMapObjects + } + } + m_removedMapObjects.clear(); + + for (int i = 0; i < m_mapObjects.size(); ++i) { + // already added as node + if (Q_UNLIKELY(!m_mapObjects.at(i).object)) { + qWarning() << "unexpected NULL pointer in m_mapObjects at "<updateMapObjectNode(oldNode, &mo.visibleNode, root, window); + if (Q_UNLIKELY(!mo.qsgNode)) { + qWarning() << "updateMapObjectNode for "<type() << " returned NULL"; + } else if (mo.visibleNode && (mo.visibleNode->visible() != mo.object->visible())) { + mo.visibleNode->setVisible(mo.object->visible()); + mo.qsgNode->markDirty(QSGNode::DirtySubtreeBlocked); + } + } + + QList toRemove; + for (int i = 0; i < m_pendingMapObjects.size(); ++i) { + // already added as node + MapObject &mo = m_pendingMapObjects[i]; + QQSGMapObject *sgo = mo.sgObject; + QSGNode *oldNode = mo.qsgNode; + sgo->updateGeometry(); // or subtree will be blocked + mo.qsgNode = sgo->updateMapObjectNode(oldNode, &mo.visibleNode, root, window); + if (mo.qsgNode) { + if (mo.visibleNode && (mo.visibleNode->visible() != mo.object->visible())) { + mo.visibleNode->setVisible(mo.object->visible()); + mo.qsgNode->markDirty(QSGNode::DirtySubtreeBlocked); + } + m_mapObjects << mo; + toRemove.push_front(i); + QObject::connect(mo.object, SIGNAL(visibleChanged()), m_map, SIGNAL(sgNodeChanged())); + } else { + // leave it to be processed, don't spit warnings + } + } + + for (int i: qAsConst(toRemove)) + m_pendingMapObjects.removeAt(i); +} + +void QGeoMapObjectQSGSupport::updateObjectsGeometry() +{ + for (int i = 0; i < m_mapObjects.size(); ++i) { + // already added as node + if (Q_UNLIKELY(!m_mapObjects.at(i).object)) { + qWarning() << "unexpected NULL pointer in m_mapObjects at "<updateGeometry(); + } + emit m_map->sgNodeChanged(); +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h b/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h new file mode 100644 index 0000000..bb0477c --- /dev/null +++ b/src/location/labs/qsg/qgeomapobjectqsgsupport_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPOBJECTQSGSUPPORT_P_H +#define QGEOMAPOBJECTQSGSUPPORT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct Q_LOCATION_PRIVATE_EXPORT MapObject { + MapObject(QPointer &o, QQSGMapObject *sgo) + : object(o), sgObject(sgo) {} + + QPointer object; + QQSGMapObject *sgObject = nullptr; // this is a QMap*ObjectPrivateQSG. it becomes invalid when the pimpl is destroyed + VisibleNode *visibleNode = nullptr; // This is a Map*Node (like a MapPolygonNode) that is a QSGNode. This doesn't disappear by itself + QSGNode *qsgNode = nullptr; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapObjectQSGSupport +{ +public: + bool createMapObjectImplementation(QGeoMapObject *obj, QGeoMapPrivate *d); + QGeoMapObjectPrivate *createMapObjectImplementationPrivate(QGeoMapObject *obj); + QList mapObjects() const; + void removeMapObject(QGeoMapObject *obj); + void updateMapObjects(QSGNode *root, QQuickWindow *window); + void updateObjectsGeometry(); + + QList m_mapObjects; + QList m_pendingMapObjects; + QList m_removedMapObjects; + QGeoMap *m_map = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPOBJECTQSGSUPPORT_P_H diff --git a/src/location/labs/qsg/qmapcircleobjectqsg.cpp b/src/location/labs/qsg/qmapcircleobjectqsg.cpp new file mode 100644 index 0000000..775016b --- /dev/null +++ b/src/location/labs/qsg/qmapcircleobjectqsg.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmapcircleobjectqsg_p_p.h" + +QT_BEGIN_NAMESPACE + +static const int CircleSamples = 128; + +QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(QGeoMapObject *q) + : QMapCircleObjectPrivateDefault(q) +{ + +} + +QMapCircleObjectPrivateQSG::QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate &other) + : QMapCircleObjectPrivateDefault(other) +{ + // Data already cloned by the *Default copy constructor, but necessary + // update operations triggered by setters overrides + setCenter(center()); + setRadius(radius()); + setColor(color()); + setBorderColor(borderColor()); + setBorderWidth(borderWidth()); +} + +QMapCircleObjectPrivateQSG::~QMapCircleObjectPrivateQSG() +{ + if (m_map) + m_map->removeMapObject(q); +} + +void QMapCircleObjectPrivateQSG::updateCirclePath() +{ + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + QList path; + QDeclarativeCircleMapItem::calculatePeripheralPoints(path, center(), radius(), CircleSamples, m_leftBound); + m_circlePath.clear(); + for (const QGeoCoordinate &c : path) + m_circlePath << p.geoToMapProjection(c); +} + +void QMapCircleObjectPrivateQSG::updateGeometry() +{ + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator + || !qIsFinite(m_radius) || !m_center.isValid()) + return; + + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + QScopedValueRollback rollback(m_updatingGeometry); + m_updatingGeometry = true; + + updateCirclePath(); + QList circlePath = m_circlePath; + + int pathCount = circlePath.size(); + bool preserve = QDeclarativeCircleMapItem::preserveCircleGeometry(circlePath, center(), radius(), p); + // using leftBound_ instead of the analytically calculated circle_.boundingGeoRectangle().topLeft()); + // to fix QTBUG-62154 + m_geometry.markSourceDirty(); + m_geometry.setPreserveGeometry(true, m_leftBound); // to set the geoLeftBound_ + m_geometry.setPreserveGeometry(preserve, m_leftBound); + + bool invertedCircle = false; + if (QDeclarativeCircleMapItem::crossEarthPole(center(), radius()) && circlePath.size() == pathCount) { + m_geometry.updateScreenPointsInvert(circlePath, *m_map); // invert fill area for really huge circles + invertedCircle = true; + } else { + m_geometry.updateSourcePoints(*m_map, circlePath); + m_geometry.updateScreenPoints(*m_map); + } + + m_borderGeometry.clear(); + + //if (borderColor() != Qt::transparent && borderWidth() > 0) + { + QList closedPath = circlePath; + closedPath << closedPath.first(); + + if (invertedCircle) { + closedPath = m_circlePath; + closedPath << closedPath.first(); + std::reverse(closedPath.begin(), closedPath.end()); + } + + m_borderGeometry.markSourceDirty(); + m_borderGeometry.setPreserveGeometry(true, m_leftBound); + m_borderGeometry.setPreserveGeometry(preserve, m_leftBound); + + // Use srcOrigin_ from fill geometry after clipping to ensure that translateToCommonOrigin won't fail. + const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + + m_borderGeometry.clearSource(); + + QDoubleVector2D borderLeftBoundWrapped; + QList > clippedPaths = + m_borderGeometry.clipPath(*m_map, closedPath, borderLeftBoundWrapped); + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + m_borderGeometry.pathToScreen(*m_map, clippedPaths, borderLeftBoundWrapped); + m_borderGeometry.updateScreenPoints(*m_map, borderWidth(), false); + } else { + m_borderGeometry.clear(); + } + } + + QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); + m_geometry.translate(origin - m_geometry.firstPointOffset()); + m_borderGeometry.translate(origin - m_borderGeometry.firstPointOffset()); +} + +QGeoMapObjectPrivate *QMapCircleObjectPrivateQSG::clone() +{ + return new QMapCircleObjectPrivateQSG(static_cast(*this)); +} + +QSGNode *QMapCircleObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow * /*window*/) +{ +// Q_UNUSED(visibleNode) // coz of -Werror=unused-but-set-parameter + MapPolygonNode *node = static_cast(oldNode); + + bool created = false; + if (!node) { + node = new MapPolygonNode(); + *visibleNode = static_cast(node); + created = true; + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode || created) { + //QMapPolygonObject *p = static_cast(q); + node->update(color(), borderColor(), &m_geometry, &m_borderGeometry); + m_geometry.setPreserveGeometry(false); + m_borderGeometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_borderGeometry.markClean(); + } + + if (created) + root->appendChildNode(node); + + return node; +} + + +void QMapCircleObjectPrivateQSG::setCenter(const QGeoCoordinate ¢er) +{ + m_center = center; + updateGeometry(); + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapCircleObjectPrivateQSG::setRadius(qreal radius) +{ + m_radius = radius; + updateGeometry(); + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapCircleObjectPrivateQSG::setColor(const QColor &color) +{ + m_fillColor = color; + updateGeometry(); + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapCircleObjectPrivateQSG::setBorderColor(const QColor &color) +{ + m_borderColor = color; + updateGeometry(); + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapCircleObjectPrivateQSG::setBorderWidth(qreal width) +{ + m_borderWidth = width; + updateGeometry(); + if (m_map) + emit m_map->sgNodeChanged(); +} + + +QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h new file mode 100644 index 0000000..4e8162f --- /dev/null +++ b/src/location/labs/qsg/qmapcircleobjectqsg_p_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPCIRCLEOBJECTQSG_P_H +#define QMAPCIRCLEOBJECTQSG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapCircleObjectPrivateQSG : public QMapCircleObjectPrivateDefault, public QQSGMapObject +{ +public: + QMapCircleObjectPrivateQSG(QGeoMapObject *q); + QMapCircleObjectPrivateQSG(const QMapCircleObjectPrivate &other); + ~QMapCircleObjectPrivateQSG() override; + + void updateCirclePath(); + + // QQSGMapObject + void updateGeometry() override; + QSGNode *updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window) override; + + // QGeoMapCirclePrivate interface + void setCenter(const QGeoCoordinate ¢er) override; + void setRadius(qreal radius) override; + void setColor(const QColor &color) override; + void setBorderColor(const QColor &color) override; + void setBorderWidth(qreal width) override; + + // QGeoMapObjectPrivate + QGeoMapObjectPrivate *clone() override; + +public: + // Data Members + QList m_circlePath; + QGeoCoordinate m_leftBound; + QGeoMapCircleGeometry m_geometry; + QGeoMapPolylineGeometry m_borderGeometry; + bool m_updatingGeometry = false; +}; + +QT_END_NAMESPACE + +#endif // QMAPCIRCLEOBJECT_P_P_H diff --git a/src/location/labs/qsg/qmapiconobjectqsg.cpp b/src/location/labs/qsg/qmapiconobjectqsg.cpp new file mode 100644 index 0000000..47c3969 --- /dev/null +++ b/src/location/labs/qsg/qmapiconobjectqsg.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmapiconobjectqsg_p_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class RootNode : public QSGTransformNode, public VisibleNode +{ +public: + RootNode() { } + + bool isSubtreeBlocked() const override + { + return subtreeBlocked(); + } +}; + +QMapIconObjectPrivateQSG::QMapIconObjectPrivateQSG(QGeoMapObject *q) + : QMapIconObjectPrivateDefault(q) +{ + +} + +QMapIconObjectPrivateQSG::QMapIconObjectPrivateQSG(const QMapIconObjectPrivate &other) + : QMapIconObjectPrivateDefault(other) +{ + setContent(content()); + setCoordinate(coordinate()); +} + +QMapIconObjectPrivateQSG::~QMapIconObjectPrivateQSG() +{ + if (m_map) + m_map->removeMapObject(q); +} + +void QMapIconObjectPrivateQSG::updateGeometry() +{ + if (!m_map) + return; + + m_geometryDirty = true; + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + + m_itemPosition = p.coordinateToItemPosition(coordinate()); + if (m_itemPosition.isFinite()) { + m_transformation.setToIdentity(); + m_transformation.translate(QVector3D(m_itemPosition.x(), m_itemPosition.y(), 0)); + } + + // TODO: support and test for zoomLevel +} + +QSGNode *QMapIconObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window) +{ + Q_UNUSED(visibleNode) + bool created = false; + RootNode *node = static_cast(oldNode); + if (!node) { + node = new RootNode(); + m_imageNode = window->createImageNode(); + m_imageNode->setOwnsTexture(true); + node->appendChildNode(m_imageNode); + *visibleNode = static_cast(node); + created = true; + } + + if (m_imageDirty) { + m_imageDirty = false; + m_imageNode->setTexture(window->createTextureFromImage(m_image)); + QRect rect = m_image.rect(); + m_imageNode->setSourceRect(rect); + m_imageNode->setRect(QRectF(QPointF(0,0), m_size)); + } + + if (m_geometryDirty) { + m_geometryDirty = false; + if (!m_itemPosition.isFinite()) { + node->setSubtreeBlocked(true); + } else { + node->setSubtreeBlocked(false); + node->setMatrix(m_transformation); + } + } + + if (created) + root->appendChildNode(node); + + return node; +} + +void QMapIconObjectPrivateQSG::setCoordinate(const QGeoCoordinate &coordinate) +{ + QMapIconObjectPrivateDefault::setCoordinate(coordinate); + updateGeometry(); +} + +template +static T *getContent(const QVariant &content) +{ + QObject *obj = qvariant_cast(content); + return qobject_cast(obj); +} + +static inline QString imageId(const QUrl &url) +{ + return url.toString(QUrl::RemoveScheme | QUrl::RemoveAuthority).mid(1); +} + +void QMapIconObjectPrivateQSG::clearContent() +{ + m_image = QImage(); +} + +void QMapIconObjectPrivateQSG::setContent(const QVariant &content) +{ + // First reset all local containers + clearContent(); + QQmlEngine *engine = qmlEngine(q); + + // Then pull the new content + QMapIconObjectPrivateDefault::setContent(content); + switch (content.type()) { + case QVariant::UserType: { + // TODO: Handle QObject subclasses -- first decide which ones + break; + } + case QVariant::String: + case QVariant::Url: { + // URL, including image/texture providers + // Supporting only image providers for now + const QUrl url = content.toUrl(); + if (!url.isValid()) { + m_image = QImage(content.toString()); + m_imageDirty = true; + updateGeometry(); + } else if (url.scheme().isEmpty() || url.scheme() == QLatin1String("file")) { + m_image = QImage(url.toString(QUrl::RemoveScheme)); + m_imageDirty = true; + updateGeometry(); + } else if (url.scheme() == QLatin1String("image")) { + QQuickImageProvider *provider = static_cast(engine->imageProvider(url.host())); + QSize outSize; + m_image = provider->requestImage(imageId(url), &outSize, QSize()); + if (outSize.isEmpty()) + break; + m_imageDirty = true; + updateGeometry(); + } else { // ToDo: Use QNAM + + } + + break; + } + case QVariant::ByteArray: { + // ToDo: Build the image from bytearray + break; + } + default: + qWarning() << "Unsupported parameter type: " << content.type(); + break; + } + + if (m_map && m_imageDirty) + emit m_map->sgNodeChanged(); +} + +void QMapIconObjectPrivateQSG::setSize(const QSizeF &size) +{ + QMapIconObjectPrivateDefault::setSize(size); + updateGeometry(); +} + +QGeoMapObjectPrivate *QMapIconObjectPrivateQSG::clone() +{ + return new QMapIconObjectPrivateQSG(static_cast(*this)); +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmapiconobjectqsg_p_p.h b/src/location/labs/qsg/qmapiconobjectqsg_p_p.h new file mode 100644 index 0000000..c57828a --- /dev/null +++ b/src/location/labs/qsg/qmapiconobjectqsg_p_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPICONOBJECTQSG_P_P_H +#define QMAPICONOBJECTQSG_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QSGImageNode; +class Q_LOCATION_PRIVATE_EXPORT QMapIconObjectPrivateQSG : public QMapIconObjectPrivateDefault, public QQSGMapObject +{ +public: + QMapIconObjectPrivateQSG(QGeoMapObject *q); + QMapIconObjectPrivateQSG(const QMapIconObjectPrivate &other); + ~QMapIconObjectPrivateQSG() override; + + void clearContent(); + + // QQSGMapObject + void updateGeometry() override; + QSGNode *updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window) override; + + // QGeoMapIconPrivate interface + void setCoordinate(const QGeoCoordinate &coordinate) override; + void setContent(const QVariant &content) override; + void setSize(const QSizeF &size) override; + + // QGeoMapObjectPrivate + QGeoMapObjectPrivate *clone() override; + +public: + // Data Members + bool m_imageDirty = false; + bool m_geometryDirty = false; + QImage m_image; + QSGImageNode *m_imageNode = nullptr; + QDoubleVector2D m_itemPosition; + QMatrix4x4 m_transformation; +}; + +QT_END_NAMESPACE + +#endif // QMAPICONOBJECTQSG_P_P_H diff --git a/src/location/labs/qsg/qmappolygonobjectqsg.cpp b/src/location/labs/qsg/qmappolygonobjectqsg.cpp new file mode 100644 index 0000000..99a84ec --- /dev/null +++ b/src/location/labs/qsg/qmappolygonobjectqsg.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmappolygonobjectqsg_p_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +QMapPolygonObjectPrivateQSG::QMapPolygonObjectPrivateQSG(QGeoMapObject *q) + : QMapPolygonObjectPrivate(q) +{ + +} + +QMapPolygonObjectPrivateQSG::QMapPolygonObjectPrivateQSG(const QMapPolygonObjectPrivate &other) + : QMapPolygonObjectPrivate(other.q) +{ + setPath(other.path()); + setFillColor(other.fillColor()); + setBorderColor(other.borderColor()); + setBorderWidth(other.borderWidth()); +} + +QMapPolygonObjectPrivateQSG::~QMapPolygonObjectPrivateQSG() +{ + if (m_map) + m_map->removeMapObject(q); +} + +QList QMapPolygonObjectPrivateQSG::projectPath() +{ + QList geopathProjected_; + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return geopathProjected_; + + const QGeoProjectionWebMercator &p = + static_cast(m_map->geoProjection()); + geopathProjected_.reserve(m_geoPath.path().size()); + for (const QGeoCoordinate &c : m_geoPath.path()) + geopathProjected_ << p.geoToMapProjection(c); + return geopathProjected_; +} + +QSGNode *QMapPolygonObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow * /*window*/) +{ + Q_UNUSED(visibleNode) + MapPolygonNode *node = static_cast(oldNode); + + bool created = false; + if (!node) { + if (!m_geometry.size() && !m_borderGeometry.size()) + return nullptr; + node = new MapPolygonNode(); + *visibleNode = static_cast(node); + created = true; + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || !m_borderGeometry.isScreenDirty() || !oldNode || created) { + node->update(fillColor(), borderColor(), &m_geometry, &m_borderGeometry); + m_geometry.setPreserveGeometry(false); + m_borderGeometry.setPreserveGeometry(false); + m_geometry.markClean(); + m_borderGeometry.markClean(); + } + + if (created) + root->appendChildNode(node); + + return node; +} + +QList QMapPolygonObjectPrivateQSG::path() const +{ + return m_geoPath.path(); +} + +QColor QMapPolygonObjectPrivateQSG::fillColor() const +{ + return m_fillColor; +} + +QColor QMapPolygonObjectPrivateQSG::borderColor() const +{ + return m_borderColor; +} + +qreal QMapPolygonObjectPrivateQSG::borderWidth() const +{ + return m_borderWidth; +} + +void QMapPolygonObjectPrivateQSG::setPath(const QList &path) +{ + m_geoPath.setPath(path); + updateGeometry(); + + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapPolygonObjectPrivateQSG::setFillColor(const QColor &color) +{ + m_fillColor = color; + updateGeometry(); + + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapPolygonObjectPrivateQSG::setBorderColor(const QColor &color) +{ + m_borderColor = color; + updateGeometry(); + + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapPolygonObjectPrivateQSG::setBorderWidth(qreal width) +{ + m_borderWidth = width; + updateGeometry(); + + if (m_map) + emit m_map->sgNodeChanged(); +} + +QGeoMapObjectPrivate *QMapPolygonObjectPrivateQSG::clone() +{ + return new QMapPolygonObjectPrivateQSG(static_cast(*this)); +} + +void QMapPolygonObjectPrivateQSG::updateGeometry() +{ + if (!m_map || m_geoPath.path().length() == 0 + || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + + QScopedValueRollback rollback(m_updatingGeometry); + m_updatingGeometry = true; + + const QList &geopathProjected = projectPath(); + + m_geometry.markSourceDirty(); + m_geometry.setPreserveGeometry(true, m_geoPath.boundingGeoRectangle().topLeft()); + m_geometry.updateSourcePoints(*m_map, geopathProjected); + m_geometry.updateScreenPoints(*m_map); + + m_borderGeometry.clear(); + + //if (border_.color() != Qt::transparent && border_.width() > 0) + { + const QGeoProjectionWebMercator &p = static_cast(m_map->geoProjection()); + QList closedPath = geopathProjected; + closedPath << closedPath.first(); + + m_borderGeometry.markSourceDirty(); + m_borderGeometry.setPreserveGeometry(true, m_geoPath.boundingGeoRectangle().topLeft()); + + const QGeoCoordinate &geometryOrigin = m_geometry.origin(); + + m_borderGeometry.clearSource(); + + QDoubleVector2D borderLeftBoundWrapped; + QList > clippedPaths = + m_borderGeometry.clipPath(*m_map.data(), closedPath, borderLeftBoundWrapped); + + if (clippedPaths.size()) { + borderLeftBoundWrapped = p.geoToWrappedMapProjection(geometryOrigin); + m_borderGeometry.pathToScreen(*m_map.data(), clippedPaths, borderLeftBoundWrapped); + m_borderGeometry.updateScreenPoints(*m_map.data(), borderWidth(), false); + } else { + m_borderGeometry.clear(); + } + } + + QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); + m_geometry.translate(origin - m_geometry.firstPointOffset()); + m_borderGeometry.translate(origin - m_borderGeometry.firstPointOffset()); +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h new file mode 100644 index 0000000..b288528 --- /dev/null +++ b/src/location/labs/qsg/qmappolygonobjectqsg_p_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPPOLYGONOBJECTQSG_P_P_H +#define QMAPPOLYGONOBJECTQSG_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapPolygonObjectPrivateQSG : public QMapPolygonObjectPrivate, public QQSGMapObject +{ +public: + QMapPolygonObjectPrivateQSG(QGeoMapObject *q); + QMapPolygonObjectPrivateQSG(const QMapPolygonObjectPrivate &other); + ~QMapPolygonObjectPrivateQSG() override; + + QList projectPath(); + + // QQSGMapObject + void updateGeometry() override; + QSGNode *updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window) override; + + // QGeoMapPolylinePrivate interface + QList path() const override; + QColor fillColor() const override; + QColor borderColor() const override; + qreal borderWidth() const override; + + void setPath(const QList &path) override; + void setFillColor(const QColor &color) override; + void setBorderColor(const QColor &color) override; + void setBorderWidth(qreal width) override; + + // QGeoMapObjectPrivate + QGeoMapObjectPrivate *clone() override; + + // Data Members + QGeoMapPolygonGeometry m_geometry; + QGeoMapPolylineGeometry m_borderGeometry; + QGeoPath m_geoPath; + + QColor m_fillColor; + QColor m_borderColor; + qreal m_borderWidth = 0; + bool m_updatingGeometry = false; +}; + +QT_END_NAMESPACE + +#endif // QMAPPOLYGONOBJECTQSG_P_P_H diff --git a/src/location/labs/qsg/qmappolylineobjectqsg.cpp b/src/location/labs/qsg/qmappolylineobjectqsg.cpp new file mode 100644 index 0000000..5b18432 --- /dev/null +++ b/src/location/labs/qsg/qmappolylineobjectqsg.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmappolylineobjectqsg_p_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +/* + Note: never use q, since this class is also used inside QMapRouteObjectPrivateQSG! +*/ + +QMapPolylineObjectPrivateQSG::QMapPolylineObjectPrivateQSG(QGeoMapObject *q) + : QMapPolylineObjectPrivate(q) +{ + +} + +QMapPolylineObjectPrivateQSG::QMapPolylineObjectPrivateQSG(const QMapPolylineObjectPrivate &other) + : QMapPolylineObjectPrivate(other.q) +{ + // do the appropriate internal update and trigger map repaint + setPath(other.path()); + setColor(other.color()); + setWidth(other.width()); +} + +QMapPolylineObjectPrivateQSG::~QMapPolylineObjectPrivateQSG() +{ + if (m_map) + m_map->removeMapObject(q); +} + +QList QMapPolylineObjectPrivateQSG::projectPath() +{ + QList geopathProjected_; + if (!m_map || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return geopathProjected_; + + const QGeoProjectionWebMercator &p = + static_cast(m_map->geoProjection()); + geopathProjected_.reserve(m_geoPath.path().size()); + for (const QGeoCoordinate &c : m_geoPath.path()) + geopathProjected_ << p.geoToMapProjection(c); + return geopathProjected_; +} + +void QMapPolylineObjectPrivateQSG::updateGeometry() +{ + if (!m_map || m_geoPath.path().length() == 0 + || m_map->geoProjection().projectionType() != QGeoProjection::ProjectionWebMercator) + return; + + QScopedValueRollback rollback(m_updatingGeometry); + m_updatingGeometry = true; + m_geometry.markSourceDirty(); + const QList &geopathProjected = projectPath(); + m_geometry.setPreserveGeometry(true, m_geoPath.boundingGeoRectangle().topLeft()); + m_geometry.updateSourcePoints(*m_map.data(), geopathProjected, m_geoPath.boundingGeoRectangle().topLeft()); + m_geometry.updateScreenPoints(*m_map.data(), width(), false); + + QPointF origin = m_map->geoProjection().coordinateToItemPosition(m_geometry.origin(), false).toPointF(); + m_geometry.translate(origin - m_geometry.firstPointOffset()); +} + +QSGNode *QMapPolylineObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow * /*window*/) +{ + Q_UNUSED(visibleNode) + MapPolylineNode *node = static_cast(oldNode); + + bool created = false; + if (!node) { + if (!m_geometry.size()) // condition to block the subtree + return nullptr; + node = new MapPolylineNode(); + *visibleNode = static_cast(node); + created = true; + } + + //TODO: update only material + if (m_geometry.isScreenDirty() || !oldNode || created) { + node->update(color(), &m_geometry); + m_geometry.setPreserveGeometry(false); + m_geometry.markClean(); + } + + if (created) + root->appendChildNode(node); + + return node; +} + +QList QMapPolylineObjectPrivateQSG::path() const { return m_geoPath.path(); } + +QColor QMapPolylineObjectPrivateQSG::color() const { return m_color; } + +qreal QMapPolylineObjectPrivateQSG::width() const { return m_width; } + +void QMapPolylineObjectPrivateQSG::setPath(const QList &path) +{ + m_geoPath.setPath(path); + updateGeometry(); + + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapPolylineObjectPrivateQSG::setColor(const QColor &color) +{ + m_color = color; + updateGeometry(); + + if (m_map) + emit m_map->sgNodeChanged(); +} + +void QMapPolylineObjectPrivateQSG::setWidth(qreal width) +{ + m_width = width; + updateGeometry(); + + if (m_map) + emit m_map->sgNodeChanged(); +} + +QGeoMapObjectPrivate *QMapPolylineObjectPrivateQSG::clone() +{ + return new QMapPolylineObjectPrivateQSG(static_cast(*this)); +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h new file mode 100644 index 0000000..792413e --- /dev/null +++ b/src/location/labs/qsg/qmappolylineobjectqsg_p_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPPOLYLINEOBJECTOBJECTSOVERLAY_H +#define QMAPPOLYLINEOBJECTOBJECTSOVERLAY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapPolylineObjectPrivateQSG : public QMapPolylineObjectPrivate, public QQSGMapObject +{ +public: + QMapPolylineObjectPrivateQSG(QGeoMapObject *q); + QMapPolylineObjectPrivateQSG(const QMapPolylineObjectPrivate &other); + ~QMapPolylineObjectPrivateQSG() override; + + QList projectPath(); + + // QQSGMapObject + void updateGeometry() override; + QSGNode *updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window) override; + + // QGeoMapPolylinePrivate interface + QList path() const override; + QColor color() const override; + qreal width() const override; + + void setPath(const QList &path) override; + void setColor(const QColor &color) override; + void setWidth(qreal width) override; + + // QGeoMapObjectPrivate + QGeoMapObjectPrivate *clone() override; + + // Data Members + QGeoMapPolylineGeometry m_geometry; + QGeoPath m_geoPath; + + QColor m_color; + qreal m_width = 0; + bool m_updatingGeometry = false; +}; + +QT_END_NAMESPACE + +#endif // QMAPPOLYLINEOBJECTOBJECTSOVERLAY_H diff --git a/src/location/labs/qsg/qmaprouteobjectqsg.cpp b/src/location/labs/qsg/qmaprouteobjectqsg.cpp new file mode 100644 index 0000000..eaea64f --- /dev/null +++ b/src/location/labs/qsg/qmaprouteobjectqsg.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmaprouteobjectqsg_p_p.h" + +QT_BEGIN_NAMESPACE + +QMapRouteObjectPrivateQSG::QMapRouteObjectPrivateQSG(QGeoMapObject *q) + : QMapRouteObjectPrivate(q) +{ + QScopedPointer poly(new QMapPolylineObjectPrivateQSG(q)); + m_polyline.swap(poly); + m_polyline->m_componentCompleted = true; +} + +QMapRouteObjectPrivateQSG::QMapRouteObjectPrivateQSG(const QMapRouteObjectPrivate &other) + : QMapRouteObjectPrivate(other) +{ + QScopedPointer poly(new QMapPolylineObjectPrivateQSG(other.q)); + m_polyline.swap(poly); + m_polyline->m_componentCompleted = true; + setRoute(other.declarativeGeoRoute()); +} + +QMapRouteObjectPrivateQSG::~QMapRouteObjectPrivateQSG() +{ + if (m_map) + m_map->removeMapObject(q); +} + +void QMapRouteObjectPrivateQSG::updateGeometry() +{ + m_polyline->updateGeometry(); +} + +QSGNode *QMapRouteObjectPrivateQSG::updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window) +{ + return m_polyline->updateMapObjectNode(oldNode, visibleNode, root, window); +} + +void QMapRouteObjectPrivateQSG::setRoute(const QDeclarativeGeoRoute *route) +{ + const QList &path = route->route().path(); + m_polyline->setColor(QColor("deepskyblue")); // ToDo: support MapParameters for this + m_polyline->setWidth(4); + m_polyline->setPath(path); // SGNodeChanged emitted by m_polyline +} + +QGeoMapObjectPrivate *QMapRouteObjectPrivateQSG::clone() +{ + return new QMapRouteObjectPrivateQSG(static_cast(*this)); +} + +void QMapRouteObjectPrivateQSG::setMap(QGeoMap *map) +{ + QGeoMapObjectPrivate::setMap(map); + m_polyline->setMap(map); +} + + +void QMapRouteObjectPrivateQSG::setVisible(bool visible) +{ + m_visible = visible; + m_polyline->setVisible(visible); +} + +QT_END_NAMESPACE diff --git a/src/location/labs/qsg/qmaprouteobjectqsg_p_p.h b/src/location/labs/qsg/qmaprouteobjectqsg_p_p.h new file mode 100644 index 0000000..0c94625 --- /dev/null +++ b/src/location/labs/qsg/qmaprouteobjectqsg_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPROUTEOBJECTQSG_P_P_H +#define QMAPROUTEOBJECTQSG_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QMapRouteObjectPrivateQSG : public QMapRouteObjectPrivate, public QQSGMapObject +{ +public: + QMapRouteObjectPrivateQSG(QGeoMapObject *q); + QMapRouteObjectPrivateQSG(const QMapRouteObjectPrivate &other); + ~QMapRouteObjectPrivateQSG() override; + + // QQSGMapObject + void updateGeometry() override; + QSGNode *updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window) override; + + // QMapRouteObjectPrivate interface + void setRoute(const QDeclarativeGeoRoute *route) override; + + // QGeoMapObjectPrivate interface + QGeoMapObjectPrivate *clone() override; + void setMap(QGeoMap *map) override; + void setVisible(bool visible) override; + + // Data Members + QScopedPointer m_polyline; +}; + +QT_END_NAMESPACE + +#endif // QMAPROUTEOBJECTQSG_P_P_H diff --git a/src/location/labs/qsg/qqsgmapobject.cpp b/src/location/labs/qsg/qqsgmapobject.cpp new file mode 100644 index 0000000..8c42dff --- /dev/null +++ b/src/location/labs/qsg/qqsgmapobject.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qqsgmapobject_p.h" +#include + +QT_BEGIN_NAMESPACE + +QQSGMapObject::QQSGMapObject() +{ + +} + +QQSGMapObject::~QQSGMapObject() +{ + +} + +QSGNode *QQSGMapObject::updateMapObjectNode(QSGNode *oldNode, + VisibleNode ** /*visibleNode*/, + QSGNode * /*root*/, + QQuickWindow * /*window*/) +{ + delete oldNode; + return 0; +} + +void QQSGMapObject::updateGeometry() +{ + +} + +QT_END_NAMESPACE + + diff --git a/src/location/labs/qsg/qqsgmapobject_p.h b/src/location/labs/qsg/qqsgmapobject_p.h new file mode 100644 index 0000000..5203680 --- /dev/null +++ b/src/location/labs/qsg/qqsgmapobject_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQSGMAPOBJECT_H +#define QQSGMAPOBJECT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickWindow; +class Q_LOCATION_PRIVATE_EXPORT QQSGMapObject +{ +public: + QQSGMapObject(); + virtual ~QQSGMapObject(); + + virtual QSGNode *updateMapObjectNode(QSGNode *oldNode, + VisibleNode **visibleNode, + QSGNode *root, + QQuickWindow *window); + virtual void updateGeometry(); +}; + +QT_END_NAMESPACE + +#endif // QQSGMAPOBJECT_H diff --git a/src/location/location.pro b/src/location/location.pro new file mode 100644 index 0000000..1535a85 --- /dev/null +++ b/src/location/location.pro @@ -0,0 +1,48 @@ +TARGET = QtLocation +QT = core-private positioning-private +android { + # adding qtconcurrent dependency here for the osm plugin + QT += concurrent +} + +CONFIG += simd optimize_full +QT_FOR_CONFIG += location-private + +# 3rdparty headers produce warnings with MSVC +msvc: CONFIG -= warning_clean + +INCLUDEPATH += ../3rdparty/earcut +INCLUDEPATH += ../3rdparty/poly2tri +INCLUDEPATH += ../3rdparty/clipper +INCLUDEPATH += ../3rdparty/clip2tri +INCLUDEPATH += ../positioning +INCLUDEPATH += ../imports/positioning +INCLUDEPATH *= $$PWD + +MODULE_PLUGIN_TYPES = \ + geoservices + +QMAKE_DOCS = $$PWD/doc/qtlocation.qdocconf +OTHER_FILES += configure.json doc/src/*.qdoc doc/src/plugins/*.qdoc # show .qdoc files in Qt Creator + +PUBLIC_HEADERS += \ + qlocation.h \ + qlocationglobal.h + +PRIVATE_HEADERS += \ + qlocationglobal_p.h + +SOURCES += \ + qlocation.cpp + +include(maps/maps.pri) +include(places/places.pri) +include(declarativemaps/declarativemaps.pri) +include(declarativeplaces/declarativeplaces.pri) +qtConfig(location-labs-plugin):include(labs/labs.pri) + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + +load(qt_module) + +LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lclip2tri$$qtPlatformTargetSuffix() diff --git a/src/location/maps/maps.pri b/src/location/maps/maps.pri new file mode 100644 index 0000000..b5be460 --- /dev/null +++ b/src/location/maps/maps.pri @@ -0,0 +1,102 @@ + +INCLUDEPATH += maps + +QT += gui quick + +PUBLIC_HEADERS += \ + maps/qgeocodereply.h \ + maps/qgeocodingmanagerengine.h \ + maps/qgeocodingmanager.h \ + maps/qgeomaneuver.h \ + maps/qgeoroute.h \ + maps/qgeoroutereply.h \ + maps/qgeorouterequest.h \ + maps/qgeoroutesegment.h \ + maps/qgeoroutingmanagerengine.h \ + maps/qgeoroutingmanager.h \ + maps/qgeoserviceproviderfactory.h \ + maps/qgeoserviceprovider.h + +PRIVATE_HEADERS += \ + maps/qgeomapparameter_p.h \ + maps/qgeocameracapabilities_p.h \ + maps/qgeocameradata_p.h \ + maps/qgeocameratiles_p.h \ + maps/qgeocodereply_p.h \ + maps/qgeocodingmanagerengine_p.h \ + maps/qgeocodingmanager_p.h \ + maps/qgeomaneuver_p.h \ + maps/qgeotiledmapscene_p.h \ + maps/qgeotilerequestmanager_p.h \ + maps/qgeomap_p.h \ + maps/qgeomap_p_p.h \ + maps/qgeotiledmap_p.h \ + maps/qgeotiledmap_p_p.h \ + maps/qgeotilefetcher_p.h \ + maps/qgeotilefetcher_p_p.h \ + maps/qgeomappingmanager_p.h \ + maps/qgeomappingmanager_p_p.h \ + maps/qgeomappingmanagerengine_p.h \ + maps/qgeomappingmanagerengine_p_p.h \ + maps/qgeotiledmappingmanagerengine_p.h \ + maps/qgeotiledmappingmanagerengine_p_p.h \ + maps/qgeomaptype_p.h \ + maps/qgeomaptype_p_p.h \ + maps/qgeoroute_p.h \ + maps/qgeoroutereply_p.h \ + maps/qgeorouterequest_p.h \ + maps/qgeoroutesegment_p.h \ + maps/qgeoroutingmanagerengine_p.h \ + maps/qgeoroutingmanager_p.h \ + maps/qgeoserviceprovider_p.h \ + maps/qabstractgeotilecache_p.h \ + maps/qgeofiletilecache_p.h \ + maps/qgeotiledmapreply_p.h \ + maps/qgeotiledmapreply_p_p.h \ + maps/qgeotilespec_p.h \ + maps/qgeotilespec_p_p.h \ + maps/qgeorouteparser_p.h \ + maps/qgeorouteparser_p_p.h \ + maps/qgeorouteparserosrmv5_p.h \ + maps/qgeorouteparserosrmv4_p.h \ + maps/qgeoprojection_p.h \ + maps/qnavigationmanagerengine_p.h \ + maps/qnavigationmanager_p.h \ + maps/qcache3q_p.h + +SOURCES += \ + maps/qgeocameracapabilities.cpp \ + maps/qgeocameradata.cpp \ + maps/qgeocameratiles.cpp \ + maps/qgeocodereply.cpp \ + maps/qgeocodingmanager.cpp \ + maps/qgeocodingmanagerengine.cpp \ + maps/qgeomaneuver.cpp \ + maps/qgeotilerequestmanager.cpp \ + maps/qgeomap.cpp \ + maps/qgeomappingmanager.cpp \ + maps/qgeomappingmanagerengine.cpp \ + maps/qgeotiledmappingmanagerengine.cpp \ + maps/qgeotilefetcher.cpp \ + maps/qgeomaptype.cpp \ + maps/qgeoroute.cpp \ + maps/qgeoroutereply.cpp \ + maps/qgeorouterequest.cpp \ + maps/qgeoroutesegment.cpp \ + maps/qgeoroutingmanager.cpp \ + maps/qgeoroutingmanagerengine.cpp \ + maps/qgeoserviceprovider.cpp \ + maps/qgeoserviceproviderfactory.cpp \ + maps/qabstractgeotilecache.cpp \ + maps/qgeofiletilecache.cpp \ + maps/qgeotiledmapreply.cpp \ + maps/qgeotilespec.cpp \ + maps/qgeotiledmap.cpp \ + maps/qgeotiledmapscene.cpp \ + maps/qgeorouteparser.cpp \ + maps/qgeorouteparserosrmv5.cpp \ + maps/qgeorouteparserosrmv4.cpp \ + maps/qgeomapparameter.cpp \ + maps/qnavigationmanagerengine.cpp \ + maps/qnavigationmanager.cpp \ + maps/qgeoprojection.cpp diff --git a/src/location/maps/qabstractgeotilecache.cpp b/src/location/maps/qabstractgeotilecache.cpp new file mode 100644 index 0000000..395206c --- /dev/null +++ b/src/location/maps/qabstractgeotilecache.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qabstractgeotilecache_p.h" + +#include "qgeotilespec_p.h" + +#include "qgeomappingmanager_p.h" + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QSet) + +QT_BEGIN_NAMESPACE + +QGeoTileTexture::QGeoTileTexture() + : textureBound(false) {} + +QGeoTileTexture::~QGeoTileTexture() +{ +} + +QAbstractGeoTileCache::QAbstractGeoTileCache(QObject *parent) + : QObject(parent) +{ + qRegisterMetaType(); + qRegisterMetaType >(); + qRegisterMetaType >(); +} + +QAbstractGeoTileCache::~QAbstractGeoTileCache() +{ +} + +void QAbstractGeoTileCache::printStats() +{ +} + +void QAbstractGeoTileCache::handleError(const QGeoTileSpec &, const QString &error) +{ + qWarning() << "tile request error " << error; +} + +void QAbstractGeoTileCache::setMaxDiskUsage(int diskUsage) +{ + Q_UNUSED(diskUsage); +} + +int QAbstractGeoTileCache::maxDiskUsage() const +{ + return 0; +} + +int QAbstractGeoTileCache::diskUsage() const +{ + return 0; +} + +void QAbstractGeoTileCache::setMaxMemoryUsage(int memoryUsage) +{ + Q_UNUSED(memoryUsage); +} + +int QAbstractGeoTileCache::maxMemoryUsage() const +{ + return 0; +} + +int QAbstractGeoTileCache::memoryUsage() const +{ + return 0; +} + +QString QAbstractGeoTileCache::baseCacheDirectory() +{ + QString dir; + + // Try the shared cache first and use a specific directory. (e.g. ~/.cache/QtLocation) + // If this is not supported by the platform, use the application-specific cache + // location. (e.g. ~/.cache//QtLocation) + dir = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation); + + if (!dir.isEmpty()) { + // The shared cache may not be writable when application isolation is enforced. + static bool writable = false; + static bool writableChecked = false; + if (!writableChecked) { + writableChecked = true; + QDir::root().mkpath(dir); + QFile writeTestFile(QDir(dir).filePath(QStringLiteral("qt_cache_check"))); + writable = writeTestFile.open(QIODevice::WriteOnly); + if (writable) + writeTestFile.remove(); + } + if (!writable) + dir = QString(); + } + + if (dir.isEmpty()) + dir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation); + + if (!dir.endsWith(QLatin1Char('/'))) + dir += QLatin1Char('/'); + + return dir; +} + +QString QAbstractGeoTileCache::baseLocationCacheDirectory() +{ + // This scheme allows to have the "tiles" prefix hardcoded here + // NOTE: changing the Qt version here requires changing it also in QGeoFileTileCache::init, + // in the code that remove old version tiles ! + return baseCacheDirectory() + QLatin1String("QtLocation/5.8/tiles/"); +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qabstractgeotilecache_p.h b/src/location/maps/qabstractgeotilecache_p.h new file mode 100644 index 0000000..352c520 --- /dev/null +++ b/src/location/maps/qabstractgeotilecache_p.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QABSTRACTGEOTILECACHE_P_H +#define QABSTRACTGEOTILECACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include "qcache3q_p.h" +#include +#include +#include + +#include "qgeotilespec_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGeoMappingManager; +class QGeoMappingManagerEngine; + +class QGeoTile; +class QAbstractGeoTileCache; + +class QThread; + +/* This is also used in the mapgeometry */ +class Q_LOCATION_PRIVATE_EXPORT QGeoTileTexture +{ +public: + + QGeoTileTexture(); + ~QGeoTileTexture(); + + QGeoTileSpec spec; + QImage image; + bool textureBound; +}; + +class Q_LOCATION_PRIVATE_EXPORT QAbstractGeoTileCache : public QObject +{ + Q_OBJECT +public: + enum CostStrategy { + Unitary, + ByteSize + }; + + enum CacheArea { + DiskCache = 0x01, + MemoryCache = 0x02, + AllCaches = 0xFF + }; + Q_DECLARE_FLAGS(CacheAreas, CacheArea) + + virtual ~QAbstractGeoTileCache(); + + virtual void setMaxDiskUsage(int diskUsage); + virtual int maxDiskUsage() const; + virtual int diskUsage() const; + + virtual void setMaxMemoryUsage(int memoryUsage); + virtual int maxMemoryUsage() const; + virtual int memoryUsage() const; + + virtual void setMinTextureUsage(int textureUsage) = 0; + virtual void setExtraTextureUsage(int textureUsage) = 0; + virtual int maxTextureUsage() const = 0; + virtual int minTextureUsage() const = 0; + virtual int textureUsage() const = 0; + virtual void clearAll() = 0; + virtual void setCostStrategyDisk(CostStrategy costStrategy) = 0; + virtual CostStrategy costStrategyDisk() const = 0; + virtual void setCostStrategyMemory(CostStrategy costStrategy) = 0; + virtual CostStrategy costStrategyMemory() const = 0; + virtual void setCostStrategyTexture(CostStrategy costStrategy) = 0; + virtual CostStrategy costStrategyTexture() const = 0; + + virtual QSharedPointer get(const QGeoTileSpec &spec) = 0; + + virtual void insert(const QGeoTileSpec &spec, + const QByteArray &bytes, + const QString &format, + QAbstractGeoTileCache::CacheAreas areas = QAbstractGeoTileCache::AllCaches) = 0; + virtual void handleError(const QGeoTileSpec &spec, const QString &errorString); + virtual void init() = 0; + + static QString baseCacheDirectory(); + static QString baseLocationCacheDirectory(); + +protected: + QAbstractGeoTileCache(QObject *parent = 0); + virtual void printStats() = 0; + + friend class QGeoTiledMappingManagerEngine; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractGeoTileCache::CacheAreas) + +QT_END_NAMESPACE + +#endif // QABSTRACTGEOTILECACHE_P_H diff --git a/src/location/maps/qcache3q_p.h b/src/location/maps/qcache3q_p.h new file mode 100644 index 0000000..148c1f8 --- /dev/null +++ b/src/location/maps/qcache3q_p.h @@ -0,0 +1,477 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCACHE3Q_H +#define QCACHE3Q_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +template +class QCache3QDefaultEvictionPolicy +{ +protected: + /* called just before a key/value pair is about to be _evicted_ */ + void aboutToBeEvicted(const Key &key, QSharedPointer obj); + /* called just before a key/value pair is about to be removed, by + * clear(), remove() or by the destructor (which calls clear) */ + void aboutToBeRemoved(const Key &key, QSharedPointer obj); +}; + +template +void QCache3QDefaultEvictionPolicy::aboutToBeEvicted(const Key &key, QSharedPointer obj) +{ + Q_UNUSED(key); + Q_UNUSED(obj); +} + +template +void QCache3QDefaultEvictionPolicy::aboutToBeRemoved(const Key &key, QSharedPointer obj) +{ + Q_UNUSED(key); + Q_UNUSED(obj); +} + +/* + * QCache3Q + * + * A cache template class for managing QSharedPointers to objects with an + * associated key. It's a lot like QCache, but uses an alternative algorithm + * '3Q' -- which is the '2Q' algorithm plus an extra queue for previously popular + * but evicted nodes, and a 'ghost' list of recent evictions to make a better + * placement choice if they are requested again. + * + * New nodes enter the cache on the "newbies" queue, which is evicted LRA + * (least-recently-added). If a newbie is popular enough (it has been requested + * more than promoteAt times), it will be promoted to a "regular". Regulars + * are evicted LRU (least-recently-used). If a regular is under consideration + * for eviction, its popularity is compared to the mean popularity of the whole + * regulars queue. If it is greater, it is instead moved to the "hobos" queue. + * The "hobos" queue is also evicted LRU, but has a maximum size constraint + * so eviction from it is less likely than from the regulars. + * + * Tweakables: + * * maxCost = maximum total cost for the whole cache + * * minRecent = minimum size that q1 ("newbies") has to be before eviction + * from it takes place + * * maxOldPopular = maximum size that q3 ("hobos") can reach before eviction + * from it takes place + * * promoteAt = minimum popularity necessary to promote a node from + * "newbie" to "regular" + */ +template > +class QCache3Q : public EvPolicy +{ +private: + class Queue; + class Node + { + public: + inline explicit Node() : q(0), n(0), p(0), pop(0), cost(0) {} + + Queue *q; + Node *n; + Node *p; + Key k; + QSharedPointer v; + quint64 pop; // popularity, incremented each ping + int cost; + }; + + class Queue + { + public: + inline explicit Queue() : f(0), l(0), cost(0), pop(0), size(0) {} + + Node *f; + Node *l; + int cost; // total cost of nodes on the queue + quint64 pop; // sum of popularity values on the queue + int size; // size of the queue + }; + + Queue *q1_; // "newbies": seen only once, evicted LRA (least-recently-added) + Queue *q2_; // regular nodes, promoted from newbies, evicted LRU + Queue *q3_; // "hobos": evicted from q2 but were very popular (above mean) + Queue *q1_evicted_; // ghosts of recently evicted newbies and regulars + QHash lookup_; + +public: + explicit QCache3Q(int maxCost = 0, int minRecent = -1, int maxOldPopular = -1); + inline ~QCache3Q() { clear(); delete q1_; delete q2_; delete q3_; delete q1_evicted_; } + + inline int maxCost() const { return maxCost_; } + void setMaxCost(int maxCost, int minRecent = -1, int maxOldPopular = -1); + + inline int promoteAt() const { return promote_; } + inline void setPromoteAt(int p) { promote_ = p; } + + inline int totalCost() const { return q1_->cost + q2_->cost + q3_->cost; } + + void clear(); + bool insert(const Key &key, QSharedPointer object, int cost = 1); + QSharedPointer object(const Key &key) const; + QSharedPointer operator[](const Key &key) const; + + void remove(const Key &key, bool force = false); + QList keys() const; + void printStats(); + + // Copy data directly into a queue. Designed for single use after construction + void deserializeQueue(int queueNumber, const QList &keys, + const QList > &values, const QList &costs); + // Copy data from specific queue into list + void serializeQueue(int queueNumber, QList > &buffer); + +private: + int maxCost_, minRecent_, maxOldPopular_; + int hitCount_, missCount_, promote_; + + void rebalance(); + void unlink(Node *n); + void link_front(Node *n, Queue *q); + +private: + // make these private so they can't be used + inline QCache3Q(const QCache3Q &) {} + inline QCache3Q &operator=(const QCache3Q &) {} +}; + +template +void QCache3Q::printStats() +{ + qDebug("\n=== cache %p ===", this); + qDebug("hits: %d (%.2f%%)\tmisses: %d\tfill: %.2f%%", hitCount_, + 100.0 * float(hitCount_) / (float(hitCount_ + missCount_)), + missCount_, + 100.0 * float(totalCost()) / float(maxCost())); + qDebug("q1g: size=%d, pop=%llu", q1_evicted_->size, q1_evicted_->pop); + qDebug("q1: cost=%d, size=%d, pop=%llu", q1_->cost, q1_->size, q1_->pop); + qDebug("q2: cost=%d, size=%d, pop=%llu", q2_->cost, q2_->size, q2_->pop); + qDebug("q3: cost=%d, size=%d, pop=%llu", q3_->cost, q3_->size, q3_->pop); +} + +template +QCache3Q::QCache3Q(int maxCost, int minRecent, int maxOldPopular) + : q1_(new Queue), q2_(new Queue), q3_(new Queue), q1_evicted_(new Queue), + maxCost_(maxCost), minRecent_(minRecent), maxOldPopular_(maxOldPopular), + hitCount_(0), missCount_(0), promote_(0) +{ + if (minRecent_ < 0) + minRecent_ = maxCost_ / 3; + if (maxOldPopular_ < 0) + maxOldPopular_ = maxCost_ / 5; +} + +template +void QCache3Q::serializeQueue(int queueNumber, QList > &buffer) +{ + Q_ASSERT(queueNumber >= 1 && queueNumber <= 4); + Queue *queue = queueNumber == 1 ? q1_ : + queueNumber == 2 ? q2_ : + queueNumber == 3 ? q3_ : + q1_evicted_; + for (Node *node = queue->f; node; node = node->n) + buffer.append(node->v); +} + +template +void QCache3Q::deserializeQueue(int queueNumber, const QList &keys, + const QList > &values, const QList &costs) +{ + Q_ASSERT(queueNumber >= 1 && queueNumber <= 4); + int bufferSize = keys.size(); + if (bufferSize == 0) + return; + clear(); + Queue *queue = queueNumber == 1 ? q1_ : + queueNumber == 2 ? q2_ : + queueNumber == 3 ? q3_ : + q1_evicted_; + for (int i = 0; iv = values[i]; + node->k = keys[i]; + node->cost = costs[i]; + link_front(node, queue); + lookup_[keys[i]] = node; + } +} + + +template +inline void QCache3Q::setMaxCost(int maxCost, int minRecent, int maxOldPopular) +{ + maxCost_ = maxCost; + minRecent_ = minRecent; + maxOldPopular_ = maxOldPopular; + if (minRecent_ < 0) + minRecent_ = maxCost_ / 3; + if (maxOldPopular_ < 0) + maxOldPopular_ = maxCost_ / 5; + rebalance(); +} + +template +bool QCache3Q::insert(const Key &key, QSharedPointer object, int cost) +{ + if (cost > maxCost_) { + return false; + } + + if (lookup_.contains(key)) { + Node *n = lookup_[key]; + n->v = object; + n->q->cost -= n->cost; + n->cost = cost; + n->q->cost += cost; + + if (n->q == q1_evicted_) { + if (n->pop > (uint)promote_) { + unlink(n); + link_front(n, q2_); + rebalance(); + } + } else if (n->q != q1_) { + Queue *q = n->q; + unlink(n); + link_front(n, q); + rebalance(); + } + + return true; + } + + Node *n = new Node; + n->v = object; + n->k = key; + n->cost = cost; + link_front(n, q1_); + lookup_[key] = n; + + rebalance(); + + return true; +} + +template +void QCache3Q::clear() +{ + while (q1_evicted_->f) { + Node *n = q1_evicted_->f; + unlink(n); + delete n; + } + + while (q1_->f) { + Node *n = q1_->f; + unlink(n); + EvPolicy::aboutToBeRemoved(n->k, n->v); + delete n; + } + + while (q2_->f) { + Node *n = q2_->f; + unlink(n); + EvPolicy::aboutToBeRemoved(n->k, n->v); + delete n; + } + + while (q3_->f) { + Node *n = q3_->f; + unlink(n); + EvPolicy::aboutToBeRemoved(n->k, n->v); + delete n; + } + + lookup_.clear(); +} + +template +void QCache3Q::unlink(Node *n) +{ + if (n->n) + n->n->p = n->p; + if (n->p) + n->p->n = n->n; + if (n->q->f == n) + n->q->f = n->n; + if (n->q->l == n) + n->q->l = n->p; + n->n = 0; + n->p = 0; + n->q->pop -= n->pop; + n->q->cost -= n->cost; + n->q->size--; + n->q = 0; +} + +template +void QCache3Q::link_front(Node *n, Queue *q) +{ + n->n = q->f; + n->p = 0; + n->q = q; + if (q->f) + q->f->p = n; + q->f = n; + if (!q->l) + q->l = n; + + q->pop += n->pop; + q->cost += n->cost; + q->size++; +} + +template +void QCache3Q::rebalance() +{ + while (q1_evicted_->size > (q1_->size + q2_->size + q3_->size) * 4) { + Node *n = q1_evicted_->l; + unlink(n); + lookup_.remove(n->k); + delete n; + } + + while ((q1_->cost + q2_->cost + q3_->cost) > maxCost_) { + if (q3_->cost > maxOldPopular_) { + Node *n = q3_->l; + unlink(n); + EvPolicy::aboutToBeEvicted(n->k, n->v); + lookup_.remove(n->k); + delete n; + } else if (q1_->cost > minRecent_) { + Node *n = q1_->l; + unlink(n); + EvPolicy::aboutToBeEvicted(n->k, n->v); + n->v.clear(); + n->cost = 0; + link_front(n, q1_evicted_); + } else { + Node *n = q2_->l; + unlink(n); + if (q2_->size && n->pop > (q2_->pop / q2_->size)) { + link_front(n, q3_); + } else { + EvPolicy::aboutToBeEvicted(n->k, n->v); + n->v.clear(); + n->cost = 0; + link_front(n, q1_evicted_); + } + } + } +} + +template +void QCache3Q::remove(const Key &key, bool force) +{ + if (!lookup_.contains(key)) { + return; + } + Node *n = lookup_[key]; + unlink(n); + if (n->q != q1_evicted_ && !force) + EvPolicy::aboutToBeRemoved(n->k, n->v); + lookup_.remove(key); + delete n; +} + +template +QList QCache3Q::keys() const +{ + return lookup_.keys(); +} + +template +QSharedPointer QCache3Q::object(const Key &key) const +{ + if (!lookup_.contains(key)) { + const_cast *>(this)->missCount_++; + return QSharedPointer(0); + } + + QCache3Q *me = const_cast *>(this); + + Node *n = me->lookup_[key]; + n->pop++; + n->q->pop++; + + if (n->q == q1_) { + me->hitCount_++; + + if (n->pop > (quint64)promote_) { + me->unlink(n); + me->link_front(n, q2_); + me->rebalance(); + } + } else if (n->q != q1_evicted_) { + me->hitCount_++; + + Queue *q = n->q; + me->unlink(n); + me->link_front(n, q); + me->rebalance(); + } else { + me->missCount_++; + } + + return n->v; +} + +template +inline QSharedPointer QCache3Q::operator[](const Key &key) const +{ + return object(key); +} + +QT_END_NAMESPACE + +#endif // QCACHE3Q_H diff --git a/src/location/maps/qgeocameracapabilities.cpp b/src/location/maps/qgeocameracapabilities.cpp new file mode 100644 index 0000000..bb6ec4d --- /dev/null +++ b/src/location/maps/qgeocameracapabilities.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocameracapabilities_p.h" + +#include +#include + +static const double invLog2 = 1.0 / std::log(2.0); + +static double zoomLevelTo256(double zoomLevelForTileSize, double tileSize) +{ + return std::log( std::pow(2.0, zoomLevelForTileSize) * tileSize / 256.0 ) * invLog2; +} + +QT_BEGIN_NAMESPACE + +class QGeoCameraCapabilitiesPrivate : public QSharedData +{ +public: + QGeoCameraCapabilitiesPrivate(); + QGeoCameraCapabilitiesPrivate(const QGeoCameraCapabilitiesPrivate &other); + ~QGeoCameraCapabilitiesPrivate(); + + QGeoCameraCapabilitiesPrivate &operator = (const QGeoCameraCapabilitiesPrivate &other); + + bool operator == (const QGeoCameraCapabilitiesPrivate &rhs) const; + + bool supportsBearing_; + bool supportsRolling_; + bool supportsTilting_; + + // this is mutable so that it can be set from accessor functions that are const + // TODO: remove the mutable here + mutable bool valid_; + + double minZoom_; + double maxZoom_; + double minTilt_; + double maxTilt_; + int tileSize_; + double minimumFieldOfView_; + double maximumFieldOfView_; + bool overzoomEnabled_; +}; + +QGeoCameraCapabilitiesPrivate::QGeoCameraCapabilitiesPrivate() + : supportsBearing_(false), + supportsRolling_(false), + supportsTilting_(false), + valid_(false), + minZoom_(0.0), + maxZoom_(0.0), + minTilt_(0.0), + maxTilt_(0.0), + tileSize_(256), + minimumFieldOfView_(45.0), // Defaulting to a fixed FOV of 45 degrees. Too large FOVs cause the loading of too many tiles + maximumFieldOfView_(45.0), + overzoomEnabled_(false) {} + + +QGeoCameraCapabilitiesPrivate::QGeoCameraCapabilitiesPrivate(const QGeoCameraCapabilitiesPrivate &other) + : QSharedData(other), + supportsBearing_(other.supportsBearing_), + supportsRolling_(other.supportsRolling_), + supportsTilting_(other.supportsTilting_), + valid_(other.valid_), + minZoom_(other.minZoom_), + maxZoom_(other.maxZoom_), + minTilt_(other.minTilt_), + maxTilt_(other.maxTilt_), + tileSize_(other.tileSize_), + minimumFieldOfView_(other.minimumFieldOfView_), + maximumFieldOfView_(other.maximumFieldOfView_), + overzoomEnabled_(other.overzoomEnabled_){} + + +QGeoCameraCapabilitiesPrivate::~QGeoCameraCapabilitiesPrivate() {} + +QGeoCameraCapabilitiesPrivate &QGeoCameraCapabilitiesPrivate::operator = (const QGeoCameraCapabilitiesPrivate &other) +{ + if (this == &other) + return *this; + + supportsBearing_ = other.supportsBearing_; + supportsRolling_ = other.supportsRolling_; + supportsTilting_ = other.supportsTilting_; + valid_ = other.valid_; + minZoom_ = other.minZoom_; + maxZoom_ = other.maxZoom_; + minTilt_ = other.minTilt_; + maxTilt_ = other.maxTilt_; + tileSize_ = other.tileSize_; + minimumFieldOfView_ = other.minimumFieldOfView_; + maximumFieldOfView_ = other.maximumFieldOfView_; + overzoomEnabled_ = other.overzoomEnabled_; + + return *this; +} + +bool QGeoCameraCapabilitiesPrivate::operator == (const QGeoCameraCapabilitiesPrivate &rhs) const +{ + return ((supportsBearing_ == rhs.supportsBearing_) + && (supportsRolling_ == rhs.supportsRolling_) + && (supportsTilting_ == rhs.supportsTilting_) + && (valid_ == rhs.valid_) + && (minZoom_ == rhs.minZoom_) + && (maxZoom_ == rhs.maxZoom_) + && (minTilt_ == rhs.minTilt_) + && (maxTilt_ == rhs.maxTilt_) + && (tileSize_ == rhs.tileSize_) + && (minimumFieldOfView_ == rhs.minimumFieldOfView_) + && (maximumFieldOfView_ == rhs.maximumFieldOfView_) + && (overzoomEnabled_ == rhs.overzoomEnabled_)); +} + +/*! + \class QGeoCameraCapabilities + \inmodule QtLocation + \ingroup QtLocation-impl + \since 5.6 + \internal + + \brief The QGeoCameraCapabilities class describes the limitations on camera settings imposed by a mapping plugin. + + Different mapping plugins will support different ranges of zoom levels, and not all mapping plugins will + be able to support, bearing, tilting and rolling of the camera. + + This class describes what the plugin supports, and is used to restrict changes to the camera information + associated with a \l QGeoMap such that the camera information stays within these limits. +*/ + +/*! + Constructs a camera capabilities object. +*/ +QGeoCameraCapabilities::QGeoCameraCapabilities() + : d(new QGeoCameraCapabilitiesPrivate()) {} + +/*! + Constructs a camera capabilities object from the contents of \a other. +*/ +QGeoCameraCapabilities::QGeoCameraCapabilities(const QGeoCameraCapabilities &other) + : d(other.d) {} + +/*! + Destroys this camera capabilities object. +*/ +QGeoCameraCapabilities::~QGeoCameraCapabilities() {} + +/*! + Assigns the contents of \a other to this camera capabilities object and + returns a reference to this camera capabilities object. +*/ +QGeoCameraCapabilities &QGeoCameraCapabilities::operator = (const QGeoCameraCapabilities &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +bool QGeoCameraCapabilities::operator == (const QGeoCameraCapabilities &rhs) const +{ + return (*(d.constData()) == *(rhs.d.constData())); +} + +bool QGeoCameraCapabilities::operator != (const QGeoCameraCapabilities &other) const +{ + return !(operator==(other)); +} + +void QGeoCameraCapabilities::setTileSize(int tileSize) +{ + if (tileSize < 1) + return; + d->tileSize_ = tileSize; +} + +int QGeoCameraCapabilities::tileSize() const +{ + return d->tileSize_; +} + +/*! + Returns whether this instance of the class is considered "valid". To be + valid, the instance must have had at least one capability set (to either + true or false) using a set method, or copied from another instance + (such as by the assignment operator). +*/ +bool QGeoCameraCapabilities::isValid() const +{ + return d->valid_; +} + +/*! + Sets the minimum zoom level supported by the associated plugin to \a maximumZoomLevel. + + Larger values of the zoom level correspond to more detailed views of the + map. +*/ +void QGeoCameraCapabilities::setMinimumZoomLevel(double minimumZoomLevel) +{ + d->minZoom_ = minimumZoomLevel; + d->valid_ = true; +} + +/*! + Returns the minimum zoom level supported by the associated plugin. + + Larger values of the zoom level correspond to more detailed views of the + map. +*/ +double QGeoCameraCapabilities::minimumZoomLevel() const +{ + return d->minZoom_; +} + +double QGeoCameraCapabilities::minimumZoomLevelAt256() const +{ + if (d->tileSize_ == 256) + return d->minZoom_; + return qMax(0, zoomLevelTo256(d->minZoom_, d->tileSize_)); +} + +/*! + Sets the maximum zoom level supported by the associated plugin to \a maximumZoomLevel. + + Larger values of the zoom level correspond to more detailed views of the + map. +*/ +void QGeoCameraCapabilities::setMaximumZoomLevel(double maximumZoomLevel) +{ + d->maxZoom_ = maximumZoomLevel; + d->valid_ = true; +} + +/*! + Returns the maximum zoom level supported by the associated plugin. + + Larger values of the zoom level correspond to more detailed views of the + map. +*/ +double QGeoCameraCapabilities::maximumZoomLevel() const +{ + return d->maxZoom_; +} + +double QGeoCameraCapabilities::maximumZoomLevelAt256() const +{ + if (d->tileSize_ == 256) + return d->maxZoom_; + return qMax(0, zoomLevelTo256(d->maxZoom_, d->tileSize_)); +} + +/*! + Sets whether the associated plugin can render a map when the camera + has an arbitrary bearing to \a supportsBearing. +*/ +void QGeoCameraCapabilities::setSupportsBearing(bool supportsBearing) +{ + d->supportsBearing_ = supportsBearing; + d->valid_ = true; +} + +/*! + Returns whether the associated plugin can render a map when the camera + has an arbitrary bearing. +*/ +bool QGeoCameraCapabilities::supportsBearing() const +{ + return d->supportsBearing_; +} + +/*! + Sets whether the associated plugin can render a map when the + camera is rolled to \a supportsRolling. +*/ +void QGeoCameraCapabilities::setSupportsRolling(bool supportsRolling) +{ + d->supportsRolling_ = supportsRolling; + d->valid_ = true; +} + +/*! + Returns whether the associated plugin can render a map when the + camera is rolled. +*/ +bool QGeoCameraCapabilities::supportsRolling() const +{ + return d->supportsRolling_; +} + +/*! + Sets whether the associated plugin can render a map when the + camera is tilted to \a supportsTilting. +*/ +void QGeoCameraCapabilities::setSupportsTilting(bool supportsTilting) +{ + d->supportsTilting_ = supportsTilting; + d->valid_ = true; +} + +/*! + Returns whether the associated plugin can render a map when the + camera is tilted. +*/ +bool QGeoCameraCapabilities::supportsTilting() const +{ + return d->supportsTilting_; +} + +/*! + Sets the minimum tilt supported by the associated plugin to \a minimumTilt. + + The value is in degrees where 0 is equivalent to 90 degrees between + the line of view and earth's surface, that is, looking straight down to earth. +*/ +void QGeoCameraCapabilities::setMinimumTilt(double minimumTilt) +{ + d->minTilt_ = minimumTilt; + d->valid_ = true; +} + +/*! + Returns the minimum tilt supported by the associated plugin. + + The value is in degrees where 0 is equivalent to 90 degrees between + the line of view and earth's surface, that is, looking straight down to earth. +*/ +double QGeoCameraCapabilities::minimumTilt() const +{ + return d->minTilt_; +} + +/*! + Sets the maximum tilt supported by the associated plugin to \a maximumTilt. + + The value is in degrees where 0 is equivalent to 90 degrees between + the line of view and earth's surface, that is, looking straight down to earth. +*/ +void QGeoCameraCapabilities::setMaximumTilt(double maximumTilt) +{ + d->maxTilt_ = maximumTilt; + d->valid_ = true; +} + +/*! + Returns the maximum tilt supported by the associated plugin. + + The value is in degrees where 0 is equivalent to 90 degrees between + the line of view and earth's surface, that is, looking straight down to earth. +*/ +double QGeoCameraCapabilities::maximumTilt() const +{ + return d->maxTilt_; +} + +/*! + Sets the minimum field of view supported by the associated plugin to \a minimumFieldOfView. + The value is in degrees and is clamped against a [1, 179] range. + + \since 5.9 +*/ +void QGeoCameraCapabilities::setMinimumFieldOfView(double minimumFieldOfView) +{ + d->minimumFieldOfView_ = qBound(1.0, minimumFieldOfView, 179.0); + d->valid_ = true; +} + +/*! + Returns the minimum field of view supported by the associated plugin. + The value is in degrees. + + \since 5.9 +*/ +double QGeoCameraCapabilities::minimumFieldOfView() const +{ + return d->minimumFieldOfView_; +} + +/*! + Sets the maximum field of view supported by the associated plugin to \a maximumFieldOfView. + The value is in degrees and is clamped against a [1, 179] range. + + \since 5.9 +*/ +void QGeoCameraCapabilities::setMaximumFieldOfView(double maximumFieldOfView) +{ + d->maximumFieldOfView_ = qBound(1.0, maximumFieldOfView, 179.0); + d->valid_ = true; +} + +/*! + Returns the maximum field of view supported by the associated plugin. + The value is in degrees. + + \since 5.9 +*/ +double QGeoCameraCapabilities::maximumFieldOfView() const +{ + return d->maximumFieldOfView_; +} + +/*! + Sets whether overzooming is supported by the associated plugin. + + Overzooming means that zoom levels outside the [minimumZL, maximumZL] range can be set, + and if tiles aren't available for those zoom levels, either tiles from other zoom levels + will be used, or nothing will be shown. + + Set this value to false if the plugin is not capable of that. For example if using + a mapping engine that always clamp the zoomLevel value, which may cause misalignment in case + of stacked map elements. + + \since 5.9 +*/ +void QGeoCameraCapabilities::setOverzoomEnabled(bool overzoomEnabled) +{ + d->overzoomEnabled_ = overzoomEnabled; + d->valid_ = true; +} + +/*! + Returns whether overzooming is supported by the associated plugin. + + \since 5.9 +*/ +bool QGeoCameraCapabilities::overzoomEnabled() const +{ + return d->overzoomEnabled_; +} + + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeocameracapabilities_p.h b/src/location/maps/qgeocameracapabilities_p.h new file mode 100644 index 0000000..9be0384 --- /dev/null +++ b/src/location/maps/qgeocameracapabilities_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCAMERACAPABILITIES_P_H +#define QGEOCAMERACAPABILITIES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoCameraCapabilitiesPrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoCameraCapabilities +{ +public: + QGeoCameraCapabilities(); + QGeoCameraCapabilities(const QGeoCameraCapabilities &other); + ~QGeoCameraCapabilities(); + + QGeoCameraCapabilities &operator = (const QGeoCameraCapabilities &other); + + bool operator == (const QGeoCameraCapabilities &other) const; + bool operator != (const QGeoCameraCapabilities &other) const; + + void setTileSize(int tileSize); + int tileSize() const; + + void setMinimumZoomLevel(double minimumZoomLevel); + double minimumZoomLevel() const; + double minimumZoomLevelAt256() const; + + void setMaximumZoomLevel(double maximumZoomLevel); + double maximumZoomLevel() const; + double maximumZoomLevelAt256() const; + + void setSupportsBearing(bool supportsBearing); + bool supportsBearing() const; + + void setSupportsRolling(bool supportsRolling); + bool supportsRolling() const; + + void setSupportsTilting(bool supportsTilting); + bool supportsTilting() const; + + void setMinimumTilt(double minimumTilt); + double minimumTilt() const; + + void setMaximumTilt(double maximumTilt); + double maximumTilt() const; + + void setMinimumFieldOfView(double minimumFieldOfView); + double minimumFieldOfView() const; + + void setMaximumFieldOfView(double maximumFieldOfView); + double maximumFieldOfView() const; + + void setOverzoomEnabled(bool overzoomEnabled); + bool overzoomEnabled() const; + + bool isValid() const; + +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +#endif // QGEOCAMERACAPABILITIES_P_H diff --git a/src/location/maps/qgeocameradata.cpp b/src/location/maps/qgeocameradata.cpp new file mode 100644 index 0000000..ecf7d46 --- /dev/null +++ b/src/location/maps/qgeocameradata.cpp @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeocameradata_p.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCameraDataPrivate : public QSharedData +{ +public: + QGeoCameraDataPrivate(); + QGeoCameraDataPrivate(const QGeoCameraDataPrivate &rhs); + + QGeoCameraDataPrivate &operator = (const QGeoCameraDataPrivate &rhs); + + bool operator == (const QGeoCameraDataPrivate &rhs) const; + + QGeoCoordinate m_center; + double m_bearing; + double m_tilt; + double m_roll; + double m_fieldOfView; + double m_zoomLevel; +}; + +QGeoCameraDataPrivate::QGeoCameraDataPrivate() + : QSharedData(), + m_center(0, 0), + m_bearing(0.0), + m_tilt(0.0), + m_roll(0.0), + m_fieldOfView(45.0), + m_zoomLevel(0.0) {} + +QGeoCameraDataPrivate::QGeoCameraDataPrivate(const QGeoCameraDataPrivate &rhs) + : QSharedData(rhs), + m_center(rhs.m_center), + m_bearing(rhs.m_bearing), + m_tilt(rhs.m_tilt), + m_roll(rhs.m_roll), + m_fieldOfView(rhs.m_fieldOfView), + m_zoomLevel(rhs.m_zoomLevel) {} + +QGeoCameraDataPrivate &QGeoCameraDataPrivate::operator = (const QGeoCameraDataPrivate &rhs) +{ + if (this == &rhs) + return *this; + + m_center = rhs.m_center; + m_bearing = rhs.m_bearing; + m_tilt = rhs.m_tilt; + m_roll = rhs.m_roll; + m_fieldOfView = rhs.m_fieldOfView; + m_zoomLevel = rhs.m_zoomLevel; + + return *this; +} + +bool QGeoCameraDataPrivate::operator == (const QGeoCameraDataPrivate &rhs) const +{ + return ((m_center == rhs.m_center) + && (m_bearing == rhs.m_bearing) + && (m_tilt == rhs.m_tilt) + && (m_roll == rhs.m_roll) + && (m_fieldOfView == rhs.m_fieldOfView) + && (m_zoomLevel == rhs.m_zoomLevel)); +} + +QVariant cameraInterpolator(const QGeoCameraData &start, + const QGeoCameraData &end, + qreal progress) +{ + QGeoCameraData result = start; + QGeoCoordinate from = start.center(); + QGeoCoordinate to = end.center(); + + if (from == to) { + if (progress < 0.5) { + result.setCenter(from); + } else { + result.setCenter(to); + } + } + else { + QGeoCoordinate coordinateResult = QWebMercator::coordinateInterpolation(from, to, progress); + result.setCenter(coordinateResult); + } + + double sf = 1.0 - progress; + double ef = progress; + + result.setBearing(sf * start.bearing() + ef * end.bearing()); + result.setTilt(sf * start.tilt() + ef * end.tilt()); + result.setRoll(sf * start.roll() + ef * end.roll()); + result.setFieldOfView(sf * start.fieldOfView() + ef * end.fieldOfView()); + result.setZoomLevel(sf * start.zoomLevel() + ef * end.zoomLevel()); + + return QVariant::fromValue(result); +} + +QGeoCameraData::QGeoCameraData() + : d(new QGeoCameraDataPrivate()) +{ + qRegisterMetaType(); + qRegisterAnimationInterpolator(cameraInterpolator); +} + +QGeoCameraData::QGeoCameraData(const QGeoCameraData &other) + : d(other.d) {} + +QGeoCameraData::~QGeoCameraData() +{ +} + +QGeoCameraData &QGeoCameraData::operator = (const QGeoCameraData &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +bool QGeoCameraData::operator == (const QGeoCameraData &rhs) const +{ + return (*(d.constData()) == *(rhs.d.constData())); +} + +bool QGeoCameraData::operator != (const QGeoCameraData &other) const +{ + return !(operator==(other)); +} + +void QGeoCameraData::setCenter(const QGeoCoordinate ¢er) +{ + d->m_center = center; +} + +QGeoCoordinate QGeoCameraData::center() const +{ + return d->m_center; +} + +void QGeoCameraData::setBearing(double bearing) +{ + d->m_bearing = bearing; +} + +double QGeoCameraData::bearing() const +{ + return d->m_bearing; +} + +void QGeoCameraData::setTilt(double tilt) +{ + d->m_tilt = tilt; +} + +double QGeoCameraData::tilt() const +{ + return d->m_tilt; +} + +void QGeoCameraData::setRoll(double roll) +{ + d->m_roll = roll; +} + +double QGeoCameraData::roll() const +{ + return d->m_roll; +} + +void QGeoCameraData::setFieldOfView(double fieldOfView) +{ + d->m_fieldOfView = fieldOfView; +} + +double QGeoCameraData::fieldOfView() const +{ + return d->m_fieldOfView; +} + +void QGeoCameraData::setZoomLevel(double zoomFactor) +{ + d->m_zoomLevel = zoomFactor; +} + +double QGeoCameraData::zoomLevel() const +{ + return d->m_zoomLevel; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeocameradata_p.h b/src/location/maps/qgeocameradata_p.h new file mode 100644 index 0000000..4912ec3 --- /dev/null +++ b/src/location/maps/qgeocameradata_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOCAMERADATA_P_H +#define QGEOCAMERADATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoCameraDataPrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoCameraData +{ +public: + QGeoCameraData(); + QGeoCameraData(const QGeoCameraData &other); + ~QGeoCameraData(); + + QGeoCameraData &operator = (const QGeoCameraData &other); + + bool operator == (const QGeoCameraData &other) const; + bool operator != (const QGeoCameraData &other) const; + + void setCenter(const QGeoCoordinate &coordinate); + QGeoCoordinate center() const; + + void setBearing(double bearing); + double bearing() const; + + void setTilt(double tilt); + double tilt() const; + + void setRoll(double roll); + double roll() const; + + void setFieldOfView(double fieldOfView); + double fieldOfView() const; + + // Zoom level is intended to be relative to a tileSize of 256^2 pixels. + // E.g., a zoom level of 0 must result in a mapWidth of 256, and so on. + void setZoomLevel(double zoomLevel); + double zoomLevel() const; + +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoCameraData) + +#endif // QGEOCAMERADATA_P_H diff --git a/src/location/maps/qgeocameratiles.cpp b/src/location/maps/qgeocameratiles.cpp new file mode 100644 index 0000000..327d54b --- /dev/null +++ b/src/location/maps/qgeocameratiles.cpp @@ -0,0 +1,902 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeocameratiles_p.h" +#include "qgeocameradata_p.h" +#include "qgeotilespec_p.h" +#include "qgeomaptype_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static QVector3D toVector3D(const QDoubleVector3D& in) +{ + return QVector3D(in.x(), in.y(), in.z()); +} + +static QDoubleVector3D toDoubleVector3D(const QVector3D& in) +{ + return QDoubleVector3D(in.x(), in.y(), in.z()); +} + +QT_BEGIN_NAMESPACE + +struct Frustum +{ + QDoubleVector3D apex; + QDoubleVector3D topLeftNear; + QDoubleVector3D topLeftFar; + QDoubleVector3D topRightNear; + QDoubleVector3D topRightFar; + QDoubleVector3D bottomLeftNear; + QDoubleVector3D bottomLeftFar; + QDoubleVector3D bottomRightNear; + QDoubleVector3D bottomRightFar; +}; + +typedef QVector PolygonVector; + +class QGeoCameraTilesPrivate +{ +public: + QGeoCameraTilesPrivate(); + ~QGeoCameraTilesPrivate(); + + QString m_pluginString; + QGeoMapType m_mapType; + int m_mapVersion; + QGeoCameraData m_camera; + QSize m_screenSize; + int m_tileSize; + QSet m_tiles; + + int m_intZoomLevel; + int m_sideLength; + + bool m_dirtyGeometry; + bool m_dirtyMetadata; + + double m_viewExpansion; + void updateMetadata(); + void updateGeometry(); + + Frustum createFrustum(double viewExpansion) const; + + struct ClippedFootprint + { + ClippedFootprint(const PolygonVector &left_, const PolygonVector &mid_, const PolygonVector &right_) + : left(left_), mid(mid_), right(right_) + {} + PolygonVector left; + PolygonVector mid; + PolygonVector right; + }; + + PolygonVector frustumFootprint(const Frustum &frustum) const; + + QPair splitPolygonAtAxisValue(const PolygonVector &polygon, int axis, double value) const; + ClippedFootprint clipFootprintToMap(const PolygonVector &footprint) const; + + QList > tileIntersections(double p1, int t1, double p2, int t2) const; + QSet tilesFromPolygon(const PolygonVector &polygon) const; + + struct TileMap + { + TileMap(); + + void add(int tileX, int tileY); + + QMap > data; + }; +}; + +QGeoCameraTiles::QGeoCameraTiles() + : d_ptr(new QGeoCameraTilesPrivate()) {} + +QGeoCameraTiles::~QGeoCameraTiles() +{ +} + +void QGeoCameraTiles::setCameraData(const QGeoCameraData &camera) +{ + if (d_ptr->m_camera == camera) + return; + + d_ptr->m_dirtyGeometry = true; + d_ptr->m_camera = camera; + d_ptr->m_intZoomLevel = static_cast(std::floor(d_ptr->m_camera.zoomLevel())); + d_ptr->m_sideLength = 1 << d_ptr->m_intZoomLevel; +} + +QGeoCameraData QGeoCameraTiles::cameraData() const +{ + return d_ptr->m_camera; +} + +void QGeoCameraTiles::setScreenSize(const QSize &size) +{ + if (d_ptr->m_screenSize == size) + return; + + d_ptr->m_dirtyGeometry = true; + d_ptr->m_screenSize = size; +} + +void QGeoCameraTiles::setPluginString(const QString &pluginString) +{ + if (d_ptr->m_pluginString == pluginString) + return; + + d_ptr->m_dirtyMetadata = true; + d_ptr->m_pluginString = pluginString; +} + +void QGeoCameraTiles::setMapType(const QGeoMapType &mapType) +{ + if (d_ptr->m_mapType == mapType) + return; + + d_ptr->m_dirtyMetadata = true; + d_ptr->m_mapType = mapType; +} + +QGeoMapType QGeoCameraTiles::activeMapType() const +{ + return d_ptr->m_mapType; +} + +void QGeoCameraTiles::setMapVersion(int mapVersion) +{ + if (d_ptr->m_mapVersion == mapVersion) + return; + + d_ptr->m_dirtyMetadata = true; + d_ptr->m_mapVersion = mapVersion; +} + +void QGeoCameraTiles::setTileSize(int tileSize) +{ + if (d_ptr->m_tileSize == tileSize) + return; + + d_ptr->m_dirtyGeometry = true; + d_ptr->m_tileSize = tileSize; +} + +void QGeoCameraTiles::setViewExpansion(double viewExpansion) +{ + d_ptr->m_viewExpansion = viewExpansion; + d_ptr->m_dirtyGeometry = true; +} + +int QGeoCameraTiles::tileSize() const +{ + return d_ptr->m_tileSize; +} + +const QSet& QGeoCameraTiles::createTiles() +{ + if (d_ptr->m_dirtyGeometry) { + d_ptr->m_tiles.clear(); + d_ptr->updateGeometry(); + d_ptr->m_dirtyGeometry = false; + } + + if (d_ptr->m_dirtyMetadata) { + d_ptr->updateMetadata(); + d_ptr->m_dirtyMetadata = false; + } + + return d_ptr->m_tiles; +} + +QGeoCameraTilesPrivate::QGeoCameraTilesPrivate() +: m_mapVersion(-1), + m_tileSize(0), + m_intZoomLevel(0), + m_sideLength(0), + m_dirtyGeometry(false), + m_dirtyMetadata(false), + m_viewExpansion(1.0) +{ +} + +QGeoCameraTilesPrivate::~QGeoCameraTilesPrivate() {} + +void QGeoCameraTilesPrivate::updateMetadata() +{ + typedef QSet::const_iterator iter; + + QSet newTiles; + + iter i = m_tiles.constBegin(); + iter end = m_tiles.constEnd(); + + for (; i != end; ++i) { + QGeoTileSpec tile = *i; + newTiles.insert(QGeoTileSpec(m_pluginString, m_mapType.mapId(), tile.zoom(), tile.x(), tile.y(), m_mapVersion)); + } + + m_tiles = newTiles; +} + +void QGeoCameraTilesPrivate::updateGeometry() +{ + // Find the frustum from the camera / screen / viewport information + // The larger frustum when stationary is a form of prefetching + Frustum f = createFrustum(m_viewExpansion); + + // Find the polygon where the frustum intersects the plane of the map + PolygonVector footprint = frustumFootprint(f); + + // Clip the polygon to the map, split it up if it cross the dateline + ClippedFootprint polygons = clipFootprintToMap(footprint); + + if (!polygons.left.isEmpty()) { + QSet tilesLeft = tilesFromPolygon(polygons.left); + m_tiles.unite(tilesLeft); + } + + if (!polygons.right.isEmpty()) { + QSet tilesRight = tilesFromPolygon(polygons.right); + m_tiles.unite(tilesRight); + } + + if (!polygons.mid.isEmpty()) { + QSet tilesRight = tilesFromPolygon(polygons.mid); + m_tiles.unite(tilesRight); + } +} + +Frustum QGeoCameraTilesPrivate::createFrustum(double viewExpansion) const +{ + double apertureSize = 1.0; + if (m_camera.fieldOfView() != 90.0) //aperture(90 / 2) = 1 + apertureSize = tan(QLocationUtils::radians(m_camera.fieldOfView()) * 0.5); + QDoubleVector3D center = m_sideLength * QWebMercator::coordToMercator(m_camera.center()); + + double f = m_screenSize.height(); + + double z = std::pow(2.0, m_camera.zoomLevel() - m_intZoomLevel) * m_tileSize; // between 1 and 2 * m_tileSize + + double altitude = (f / (2.0 * z)) / apertureSize; + QDoubleVector3D eye = center; + eye.setZ(altitude); + + QDoubleVector3D view = eye - center; + QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0)); + QDoubleVector3D up = QDoubleVector3D::normal(side, view); + + QMatrix4x4 mBearing; + // The rotation direction here is the opposite of QGeoTiledMapScene::setupCamera, + // as this is basically rotating the map against a fixed view frustum. + mBearing.rotate(1.0 * m_camera.bearing(), toVector3D(view)); + up = toDoubleVector3D(mBearing * toVector3D(up)); + + // same for tilting + QDoubleVector3D side2 = QDoubleVector3D::normal(up, view); + QMatrix4x4 mTilt; + mTilt.rotate(-1.0 * m_camera.tilt(), toVector3D(side2)); + eye = toDoubleVector3D((mTilt * toVector3D(view)) + toVector3D(center)); + + view = eye - center; + side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0)); + up = QDoubleVector3D::normal(view, side2); + + double nearPlane = 1 / (4.0 * m_tileSize ); + // farPlane plays a role on how much gets clipped when the map gets tilted. It used to be altitude + 1.0 + // The value of 8.0 has been chosen as an acceptable compromise. + // TODO: use m_camera.clipDistance(); when this will be introduced + double farPlane = altitude + 8.0; + + double aspectRatio = 1.0 * m_screenSize.width() / m_screenSize.height(); + + // Half values. Half width near, far, height near, far. + double hhn,hwn,hhf,hwf = 0.0; + + // This used to fix the (half) field of view at 45 degrees + // half because this assumed that viewSize = 2*nearPlane x 2*nearPlane + viewExpansion *= apertureSize; + + hhn = viewExpansion * nearPlane; + hwn = hhn * aspectRatio; + + hhf = viewExpansion * farPlane; + hwf = hhf * aspectRatio; + + QDoubleVector3D d = center - eye; + d.normalize(); + up.normalize(); + QDoubleVector3D right = QDoubleVector3D::normal(d, up); + + QDoubleVector3D cf = eye + d * farPlane; + QDoubleVector3D cn = eye + d * nearPlane; + + Frustum frustum; + + frustum.apex = eye; + + frustum.topLeftFar = cf - (up * hhf) - (right * hwf); + frustum.topRightFar = cf - (up * hhf) + (right * hwf); + frustum.bottomLeftFar = cf + (up * hhf) - (right * hwf); + frustum.bottomRightFar = cf + (up * hhf) + (right * hwf); + + frustum.topLeftNear = cn - (up * hhn) - (right * hwn); + frustum.topRightNear = cn - (up * hhn) + (right * hwn); + frustum.bottomLeftNear = cn + (up * hhn) - (right * hwn); + frustum.bottomRightNear = cn + (up * hhn) + (right * hwn); + + return frustum; +} + +static bool appendZIntersects(const QDoubleVector3D &start, + const QDoubleVector3D &end, + double z, + QVector &results) +{ + if (start.z() == end.z()) { + return false; + } else { + double f = (start.z() - z) / (start.z() - end.z()); + if ((f >= 0) && (f <= 1.0)) { + results.append((1 - f) * start + f * end); + return true; + } + } + return false; +} + +// Returns the intersection of the plane of the map and the camera frustum as a right handed polygon +PolygonVector QGeoCameraTilesPrivate::frustumFootprint(const Frustum &frustum) const +{ + PolygonVector points; + points.reserve(4); + + // The camera is always upright. Tilting angle never reach 90degrees. + // Meaning: bottom frustum edges always intersect the map plane, top ones may not. + + // Top Right + if (!appendZIntersects(frustum.apex, frustum.topRightFar, 0.0, points)) + appendZIntersects(frustum.topRightFar, frustum.bottomRightFar, 0.0, points); + + // Bottom Right + appendZIntersects(frustum.apex, frustum.bottomRightFar, 0.0, points); + + // Bottom Left + appendZIntersects(frustum.apex, frustum.bottomLeftFar, 0.0, points); + + // Top Left + if (!appendZIntersects(frustum.apex, frustum.topLeftFar, 0.0, points)) + appendZIntersects(frustum.topLeftFar, frustum.bottomLeftFar, 0.0, points); + + return points; +} + +QPair QGeoCameraTilesPrivate::splitPolygonAtAxisValue(const PolygonVector &polygon, int axis, double value) const +{ + PolygonVector polygonBelow; + PolygonVector polygonAbove; + + int size = polygon.size(); + + if (size == 0) { + return QPair(polygonBelow, polygonAbove); + } + + QVector comparisons = QVector(polygon.size()); + + for (int i = 0; i < size; ++i) { + double v = polygon.at(i).get(axis); + if (qFuzzyCompare(v - value + 1.0, 1.0)) { + comparisons[i] = 0; + } else { + if (v < value) { + comparisons[i] = -1; + } else if (value < v) { + comparisons[i] = 1; + } + } + } + + for (int index = 0; index < size; ++index) { + int prevIndex = index - 1; + if (prevIndex < 0) + prevIndex += size; + int nextIndex = (index + 1) % size; + + int prevComp = comparisons[prevIndex]; + int comp = comparisons[index]; + int nextComp = comparisons[nextIndex]; + + if (comp == 0) { + if (prevComp == -1) { + polygonBelow.append(polygon.at(index)); + if (nextComp == 1) { + polygonAbove.append(polygon.at(index)); + } + } else if (prevComp == 1) { + polygonAbove.append(polygon.at(index)); + if (nextComp == -1) { + polygonBelow.append(polygon.at(index)); + } + } else if (prevComp == 0) { + if (nextComp == -1) { + polygonBelow.append(polygon.at(index)); + } else if (nextComp == 1) { + polygonAbove.append(polygon.at(index)); + } else if (nextComp == 0) { + // do nothing + } + } + } else { + if (comp == -1) { + polygonBelow.append(polygon.at(index)); + } else if (comp == 1) { + polygonAbove.append(polygon.at(index)); + } + + // there is a point between this and the next point + // on the polygon that lies on the splitting line + // and should be added to both the below and above + // polygons + if ((nextComp != 0) && (nextComp != comp)) { + QDoubleVector3D p1 = polygon.at(index); + QDoubleVector3D p2 = polygon.at(nextIndex); + + double p1v = p1.get(axis); + double p2v = p2.get(axis); + + double f = (p1v - value) / (p1v - p2v); + + if (((0 <= f) && (f <= 1.0)) + || qFuzzyCompare(f + 1.0, 1.0) + || qFuzzyCompare(f + 1.0, 2.0) ) { + QDoubleVector3D midPoint = (1.0 - f) * p1 + f * p2; + polygonBelow.append(midPoint); + polygonAbove.append(midPoint); + } + } + } + } + + return QPair(polygonBelow, polygonAbove); +} + +static void addXOffset(PolygonVector &footprint, double xoff) +{ + for (QDoubleVector3D &v: footprint) + v.setX(v.x() + xoff); +} + +QGeoCameraTilesPrivate::ClippedFootprint QGeoCameraTilesPrivate::clipFootprintToMap(const PolygonVector &footprint) const +{ + bool clipX0 = false; + bool clipX1 = false; + bool clipY0 = false; + bool clipY1 = false; + + double side = 1.0 * m_sideLength; + double minX = std::numeric_limits::max(); + double maxX = std::numeric_limits::lowest(); + + for (const QDoubleVector3D &p: footprint) { + if (p.y() < 0.0) + clipY0 = true; + if (p.y() > side) + clipY1 = true; + } + + PolygonVector results = footprint; + + if (clipY0) { + results = splitPolygonAtAxisValue(results, 1, 0.0).second; + } + + if (clipY1) { + results = splitPolygonAtAxisValue(results, 1, side).first; + } + + for (const QDoubleVector3D &p: results) { + if ((p.x() < 0.0) || (qFuzzyIsNull(p.x()))) + clipX0 = true; + if ((p.x() > side) || (qFuzzyCompare(side, p.x()))) + clipX1 = true; + } + + for (const QDoubleVector3D &v : results) { + minX = qMin(v.x(), minX); + maxX = qMax(v.x(), maxX); + } + + double footprintWidth = maxX - minX; + + if (clipX0) { + if (clipX1) { + if (footprintWidth > side) { + PolygonVector rightPart = splitPolygonAtAxisValue(results, 0, side).second; + addXOffset(rightPart, -side); + rightPart = splitPolygonAtAxisValue(rightPart, 0, side).first; // clip it again, should it tend to infinite or so + + PolygonVector leftPart = splitPolygonAtAxisValue(results, 0, 0).first; + addXOffset(leftPart, side); + leftPart = splitPolygonAtAxisValue(leftPart, 0, 0).second; // same here + + results = splitPolygonAtAxisValue(results, 0, 0.0).second; + results = splitPolygonAtAxisValue(results, 0, side).first; + return ClippedFootprint(leftPart, results, rightPart); + } else { // fitting the WebMercator square exactly? + results = splitPolygonAtAxisValue(results, 0, 0.0).second; + results = splitPolygonAtAxisValue(results, 0, side).first; + return ClippedFootprint(PolygonVector(), results, PolygonVector()); + } + } else { + QPair pair = splitPolygonAtAxisValue(results, 0, 0.0); + if (pair.first.isEmpty()) { + // if we touched the line but didn't cross it... + for (int i = 0; i < pair.second.size(); ++i) { + if (qFuzzyIsNull(pair.second.at(i).x())) + pair.first.append(pair.second.at(i)); + } + if (pair.first.size() == 2) { + double y0 = pair.first[0].y(); + double y1 = pair.first[1].y(); + pair.first.clear(); + pair.first.append(QDoubleVector3D(side, y0, 0.0)); + pair.first.append(QDoubleVector3D(side - 0.001, y0, 0.0)); + pair.first.append(QDoubleVector3D(side - 0.001, y1, 0.0)); + pair.first.append(QDoubleVector3D(side, y1, 0.0)); + } else if (pair.first.size() == 1) { + // FIXME this is trickier + // - touching at one point on the tile boundary + // - probably need to build a triangular polygon across the edge + // - don't want to add another y tile if we can help it + // - initial version doesn't care + double y = pair.first.at(0).y(); + pair.first.clear(); + pair.first.append(QDoubleVector3D(side - 0.001, y, 0.0)); + pair.first.append(QDoubleVector3D(side, y + 0.001, 0.0)); + pair.first.append(QDoubleVector3D(side, y - 0.001, 0.0)); + } + } else { + addXOffset(pair.first, side); + if (footprintWidth > side) + pair.first = splitPolygonAtAxisValue(pair.first, 0, 0).second; + } + return ClippedFootprint(pair.first, pair.second, PolygonVector()); + } + } else { + if (clipX1) { + QPair pair = splitPolygonAtAxisValue(results, 0, side); + if (pair.second.isEmpty()) { + // if we touched the line but didn't cross it... + for (int i = 0; i < pair.first.size(); ++i) { + if (qFuzzyCompare(side, pair.first.at(i).x())) + pair.second.append(pair.first.at(i)); + } + if (pair.second.size() == 2) { + double y0 = pair.second[0].y(); + double y1 = pair.second[1].y(); + pair.second.clear(); + pair.second.append(QDoubleVector3D(0, y0, 0.0)); + pair.second.append(QDoubleVector3D(0.001, y0, 0.0)); + pair.second.append(QDoubleVector3D(0.001, y1, 0.0)); + pair.second.append(QDoubleVector3D(0, y1, 0.0)); + } else if (pair.second.size() == 1) { + // FIXME this is trickier + // - touching at one point on the tile boundary + // - probably need to build a triangular polygon across the edge + // - don't want to add another y tile if we can help it + // - initial version doesn't care + double y = pair.second.at(0).y(); + pair.second.clear(); + pair.second.append(QDoubleVector3D(0.001, y, 0.0)); + pair.second.append(QDoubleVector3D(0.0, y - 0.001, 0.0)); + pair.second.append(QDoubleVector3D(0.0, y + 0.001, 0.0)); + } + } else { + addXOffset(pair.second, -side); + if (footprintWidth > side) + pair.second = splitPolygonAtAxisValue(pair.second, 0, side).first; + } + return ClippedFootprint(PolygonVector(), pair.first, pair.second); + } else { + return ClippedFootprint(PolygonVector(), results, PolygonVector()); + } + } + +} + +QList > QGeoCameraTilesPrivate::tileIntersections(double p1, int t1, double p2, int t2) const +{ + if (t1 == t2) { + QList > results = QList >(); + results.append(QPair(0.0, t1)); + return results; + } + + int step = 1; + if (t1 > t2) { + step = -1; + } + + int size = 1 + ((t2 - t1) / step); + + QList > results = QList >(); + + results.append(QPair(0.0, t1)); + + if (step == 1) { + for (int i = 1; i < size; ++i) { + double f = (t1 + i - p1) / (p2 - p1); + results.append(QPair(f, t1 + i)); + } + } else { + for (int i = 1; i < size; ++i) { + double f = (t1 - i + 1 - p1) / (p2 - p1); + results.append(QPair(f, t1 - i)); + } + } + + return results; +} + +QSet QGeoCameraTilesPrivate::tilesFromPolygon(const PolygonVector &polygon) const +{ + int numPoints = polygon.size(); + + if (numPoints == 0) + return QSet(); + + QVector tilesX(polygon.size()); + QVector tilesY(polygon.size()); + + // grab tiles at the corners of the polygon + for (int i = 0; i < numPoints; ++i) { + + QDoubleVector2D p = polygon.at(i).toVector2D(); + + int x = 0; + int y = 0; + + if (qFuzzyCompare(p.x(), m_sideLength * 1.0)) + x = m_sideLength - 1; + else { + x = static_cast(p.x()) % m_sideLength; + if ( !qFuzzyCompare(p.x(), 1.0 * x) && qFuzzyCompare(p.x(), 1.0 * (x + 1)) ) + x++; + } + + if (qFuzzyCompare(p.y(), m_sideLength * 1.0)) + y = m_sideLength - 1; + else { + y = static_cast(p.y()) % m_sideLength; + if ( !qFuzzyCompare(p.y(), 1.0 * y) && qFuzzyCompare(p.y(), 1.0 * (y + 1)) ) + y++; + } + + tilesX[i] = x; + tilesY[i] = y; + } + + QGeoCameraTilesPrivate::TileMap map; + + // walk along the edges of the polygon and add all tiles covered by them + for (int i1 = 0; i1 < numPoints; ++i1) { + int i2 = (i1 + 1) % numPoints; + + double x1 = polygon.at(i1).get(0); + double x2 = polygon.at(i2).get(0); + + bool xFixed = qFuzzyCompare(x1, x2); + bool xIntegral = qFuzzyCompare(x1, std::floor(x1)) || qFuzzyCompare(x1 + 1.0, std::floor(x1 + 1.0)); + + QList > xIntersects + = tileIntersections(x1, + tilesX.at(i1), + x2, + tilesX.at(i2)); + + double y1 = polygon.at(i1).get(1); + double y2 = polygon.at(i2).get(1); + + bool yFixed = qFuzzyCompare(y1, y2); + bool yIntegral = qFuzzyCompare(y1, std::floor(y1)) || qFuzzyCompare(y1 + 1.0, std::floor(y1 + 1.0)); + + QList > yIntersects + = tileIntersections(y1, + tilesY.at(i1), + y2, + tilesY.at(i2)); + + int x = xIntersects.takeFirst().second; + int y = yIntersects.takeFirst().second; + + + /* + If the polygon coincides with the tile edges we must be + inclusive and grab all tiles on both sides. We also need + to handle tiles with corners coindent with the + corners of the polygon. + e.g. all tiles marked with 'x' will be added + + "+" - tile boundaries + "O" - polygon boundary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + x + x + x + + + + + + + + + + + + + + + + + + O O O O O + + + + + + + + + + + O 0 + + + + + x O x 0 x + + + + + O 0 + + + + + + + + + + + O 0 0 0 0 + + + + + + + + + + + + + + + + + + x + x + x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + */ + + + int xOther = x; + int yOther = y; + + if (xFixed && xIntegral) { + if (y2 < y1) { + xOther = qMax(0, x - 1); + } + } + + if (yFixed && yIntegral) { + if (x1 < x2) { + yOther = qMax(0, y - 1); + + } + } + + if (xIntegral) { + map.add(xOther, y); + if (yIntegral) + map.add(xOther, yOther); + + } + + if (yIntegral) + map.add(x, yOther); + + map.add(x,y); + + // top left corner + int iPrev = (i1 + numPoints - 1) % numPoints; + double xPrevious = polygon.at(iPrev).get(0); + double yPrevious = polygon.at(iPrev).get(1); + bool xPreviousFixed = qFuzzyCompare(xPrevious, x1); + if (xIntegral && xPreviousFixed && yIntegral && yFixed) { + if ((x2 > x1) && (yPrevious > y1)) { + if ((x - 1) > 0 && (y - 1) > 0) + map.add(x - 1, y - 1); + } else if ((x2 < x1) && (yPrevious < y1)) { + // what? + } + } + + // for the simple case where intersections do not coincide with + // the boundaries, we move along the edge and add tiles until + // the x and y intersection lists are exhausted + + while (!xIntersects.isEmpty() && !yIntersects.isEmpty()) { + QPair nextX = xIntersects.first(); + QPair nextY = yIntersects.first(); + if (nextX.first < nextY.first) { + x = nextX.second; + map.add(x, y); + xIntersects.removeFirst(); + + } else if (nextX.first > nextY.first) { + y = nextY.second; + map.add(x, y); + yIntersects.removeFirst(); + + } else { + map.add(x, nextY.second); + map.add(nextX.second, y); + x = nextX.second; + y = nextY.second; + map.add(x, y); + xIntersects.removeFirst(); + yIntersects.removeFirst(); + } + } + + while (!xIntersects.isEmpty()) { + x = xIntersects.takeFirst().second; + map.add(x, y); + if (yIntegral && yFixed) + map.add(x, yOther); + + } + + while (!yIntersects.isEmpty()) { + y = yIntersects.takeFirst().second; + map.add(x, y); + if (xIntegral && xFixed) + map.add(xOther, y); + } + } + + QSet results; + + int z = m_intZoomLevel; + + typedef QMap >::const_iterator iter; + iter i = map.data.constBegin(); + iter end = map.data.constEnd(); + + for (; i != end; ++i) { + int y = i.key(); + int minX = i->first; + int maxX = i->second; + for (int x = minX; x <= maxX; ++x) { + results.insert(QGeoTileSpec(m_pluginString, m_mapType.mapId(), z, x, y, m_mapVersion)); + } + } + + return results; +} + +QGeoCameraTilesPrivate::TileMap::TileMap() {} + +void QGeoCameraTilesPrivate::TileMap::add(int tileX, int tileY) +{ + if (data.contains(tileY)) { + int oldMinX = data.value(tileY).first; + int oldMaxX = data.value(tileY).second; + data.insert(tileY, QPair(qMin(tileX, oldMinX), qMax(tileX, oldMaxX))); + } else { + data.insert(tileY, QPair(tileX, tileX)); + } +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeocameratiles_p.h b/src/location/maps/qgeocameratiles_p.h new file mode 100644 index 0000000..3ff0068 --- /dev/null +++ b/src/location/maps/qgeocameratiles_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOCAMERATILES_P_H +#define QGEOCAMERATILES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCameraData; +class QGeoTileSpec; +class QGeoMapType; +class QGeoCameraTilesPrivate; +class QSize; + +class Q_LOCATION_PRIVATE_EXPORT QGeoCameraTiles { +public: + QGeoCameraTiles(); + ~QGeoCameraTiles(); + + void setCameraData(const QGeoCameraData &camera); + QGeoCameraData cameraData() const; + void setScreenSize(const QSize &size); + void setTileSize(int tileSize); + int tileSize() const; + void setViewExpansion(double viewExpansion); + void setPluginString(const QString &pluginString); + void setMapType(const QGeoMapType &mapType); + QGeoMapType activeMapType() const; + void setMapVersion(int mapVersion); + const QSet& createTiles(); + +protected: + QScopedPointer d_ptr; + Q_DISABLE_COPY(QGeoCameraTiles) +}; + +QT_END_NAMESPACE + +#endif // QGEOCAMERATILES_P_H diff --git a/src/location/maps/qgeocodereply.cpp b/src/location/maps/qgeocodereply.cpp new file mode 100644 index 0000000..5fefb6f --- /dev/null +++ b/src/location/maps/qgeocodereply.cpp @@ -0,0 +1,338 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodereply.h" +#include "qgeocodereply_p.h" + +QT_BEGIN_NAMESPACE +/*! + \class QGeoCodeReply + \inmodule QtLocation + \ingroup QtLocation-geocoding + \since 5.6 + + \brief The QGeoCodeReply class manages an operation started by an + instance of QGeoCodingManager. + + Instances of QGeoCodeReply manage the state and results of these + operations. + + The isFinished(), error() and errorString() methods provide information + on whether the operation has completed and if it completed successfully. + + The finished() and error(QGeoCodeReply::Error,QString) + signals can be used to monitor the progress of the operation. + + It is possible that a newly created QGeoCodeReply may be in a finished + state, most commonly because an error has occurred. Since such an instance + will never emit the finished() or + error(QGeoCodeReply::Error,QString) signals, it is + important to check the result of isFinished() before making the connections + to the signals. The documentation for QGeoCodingManager demonstrates how + this might be carried out. + + If the operation completes successfully the results will be able to be + accessed with locations(). +*/ + +/*! + \enum QGeoCodeReply::Error + + Describes an error which prevented the completion of the operation. + + \value NoError + No error has occurred. + \value EngineNotSetError + The geocoding manager that was used did not have a QGeoCodingManagerEngine instance associated with it. + \value CommunicationError + An error occurred while communicating with the service provider. + \value ParseError + The response from the service provider was in an unrecognizable format. + \value UnsupportedOptionError + The requested operation or one of the options for the operation are not + supported by the service provider. + \value CombinationError + An error occurred while results where being combined from multiple sources. + \value UnknownError + An error occurred which does not fit into any of the other categories. +*/ + +/*! + Constructs a geocode reply with the specified \a parent. +*/ +QGeoCodeReply::QGeoCodeReply(QObject *parent) + : QObject(parent), + d_ptr(new QGeoCodeReplyPrivate()) {} + +/*! + Constructs a geocode reply with a given \a error and \a errorString and the specified \a parent. +*/ +QGeoCodeReply::QGeoCodeReply(Error error, const QString &errorString, QObject *parent) + : QObject(parent), + d_ptr(new QGeoCodeReplyPrivate(error, errorString)) {} + +/*! + Destroys this reply object. +*/ +QGeoCodeReply::~QGeoCodeReply() +{ + delete d_ptr; +} + +/*! + Sets whether or not this reply has finished to \a finished. + + If \a finished is true, this will cause the finished() signal to be + emitted. + + If the operation completed successfully, QGeoCodeReply::setLocations() + should be called before this function. If an error occurred, + QGeoCodeReply::setError() should be used instead. +*/ +void QGeoCodeReply::setFinished(bool finished) +{ + d_ptr->isFinished = finished; + if (d_ptr->isFinished) + emit this->finished(); +} + +/*! + Return true if the operation completed successfully or encountered an + error which cause the operation to come to a halt. +*/ +bool QGeoCodeReply::isFinished() const +{ + return d_ptr->isFinished; +} + +/*! + Sets the error state of this reply to \a error and the textual + representation of the error to \a errorString. + + This will also cause error() and finished() signals to be emitted, in that + order. +*/ +void QGeoCodeReply::setError(QGeoCodeReply::Error error, const QString &errorString) +{ + d_ptr->error = error; + d_ptr->errorString = errorString; + emit this->error(error, errorString); + setFinished(true); +} + +/*! + Returns the error state of this reply. + + If the result is QGeoCodeReply::NoError then no error has occurred. +*/ +QGeoCodeReply::Error QGeoCodeReply::error() const +{ + return d_ptr->error; +} + +/*! + Returns the textual representation of the error state of this reply. + + If no error has occurred this will return an empty string. It is possible + that an error occurred which has no associated textual representation, in + which case this will also return an empty string. + + To determine whether an error has occurred, check to see if + QGeoCodeReply::error() is equal to QGeoCodeReply::NoError. +*/ +QString QGeoCodeReply::errorString() const +{ + return d_ptr->errorString; +} + +/*! + Sets the viewport which contains the results to \a viewport. +*/ +void QGeoCodeReply::setViewport(const QGeoShape &viewport) +{ + d_ptr->viewport = viewport; +} + +/*! + Returns the viewport which contains the results. + + This function will return 0 if no viewport bias + was specified in the QGeoCodingManager function which created this reply. +*/ +QGeoShape QGeoCodeReply::viewport() const +{ + return d_ptr->viewport; +} + +/*! + Returns a list of locations. + + The locations are the results of the operation corresponding to the + QGeoCodingManager function which created this reply. +*/ +QList QGeoCodeReply::locations() const +{ + return d_ptr->locations; +} + +/*! + Adds \a location to the list of locations in this reply. +*/ +void QGeoCodeReply::addLocation(const QGeoLocation &location) +{ + d_ptr->locations.append(location); +} + +/*! + Sets the list of \a locations in the reply. +*/ +void QGeoCodeReply::setLocations(const QList &locations) +{ + d_ptr->locations = locations; +} + +/*! + \fn void QGeoCodeReply::aborted() + \since 5.9 + + This signal is emitted when the operation has been cancelled. + + \sa abort() +*/ + +/*! + Cancels the operation immediately. + + This will do nothing if the reply is finished. + + \sa aborted() +*/ +void QGeoCodeReply::abort() +{ + if (!isFinished()) + setFinished(true); + emit aborted(); +} + +/*! + Returns the limit on the number of responses from each data source. + + If no limit was set this function will return -1. + + This may be more than locations().length() if the number of responses + was less than the number requested. +*/ +int QGeoCodeReply::limit() const +{ + return d_ptr->limit; +} + +/*! + Returns the offset into the entire result set at which to start + fetching results. +*/ +int QGeoCodeReply::offset() const +{ + return d_ptr->offset; +} + +/*! + Sets the limit on the number of responses from each data source to \a limit. + + If \a limit is -1 then all available responses will be returned. +*/ +void QGeoCodeReply::setLimit(int limit) +{ + d_ptr->limit = limit; +} + +/*! + Sets the offset in the entire result set at which to start + fetching result to \a offset. +*/ +void QGeoCodeReply::setOffset(int offset) +{ + d_ptr->offset = offset; +} + +/*! + \fn void QGeoCodeReply::finished() + + This signal is emitted when this reply has finished processing. + + If error() equals QGeoCodeReply::NoError then the processing + finished successfully. + + This signal and QGeoCodingManager::finished() will be + emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ +/*! + \fn void QGeoCodeReply::error(QGeoCodeReply::Error error, const QString &errorString) + + This signal is emitted when an error has been detected in the processing of + this reply. The finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error. + + This signal and QGeoCodingManager::error() will be emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/******************************************************************************* +*******************************************************************************/ + +QGeoCodeReplyPrivate::QGeoCodeReplyPrivate() + : error(QGeoCodeReply::NoError), + isFinished(false), + limit(-1), + offset(0) {} + +QGeoCodeReplyPrivate::QGeoCodeReplyPrivate(QGeoCodeReply::Error error, const QString &errorString) + : error(error), + errorString(errorString), + isFinished(true), + limit(-1), + offset(0) {} + +QGeoCodeReplyPrivate::~QGeoCodeReplyPrivate() {} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeocodereply.h b/src/location/maps/qgeocodereply.h new file mode 100644 index 0000000..c41e740 --- /dev/null +++ b/src/location/maps/qgeocodereply.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODEREPLY_H +#define QGEOCODEREPLY_H + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoShape; +class QGeoCodeReplyPrivate; + +class Q_LOCATION_EXPORT QGeoCodeReply : public QObject +{ + Q_OBJECT + +public: + enum Error { + NoError, + EngineNotSetError, + CommunicationError, + ParseError, + UnsupportedOptionError, + CombinationError, + UnknownError + }; + + explicit QGeoCodeReply(Error error, const QString &errorString, QObject *parent = nullptr); + virtual ~QGeoCodeReply(); + + bool isFinished() const; + Error error() const; + QString errorString() const; + + QGeoShape viewport() const; + QList locations() const; + + int limit() const; + int offset() const; + + virtual void abort(); + +Q_SIGNALS: + void finished(); + void aborted(); + void error(QGeoCodeReply::Error error, const QString &errorString = QString()); + +protected: + explicit QGeoCodeReply(QObject *parent = nullptr); + + void setError(Error error, const QString &errorString); + void setFinished(bool finished); + + void setViewport(const QGeoShape &viewport); + void addLocation(const QGeoLocation &location); + void setLocations(const QList &locations); + + void setLimit(int limit); + void setOffset(int offset); + +private: + QGeoCodeReplyPrivate *d_ptr; + Q_DISABLE_COPY(QGeoCodeReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeocodereply_p.h b/src/location/maps/qgeocodereply_p.h new file mode 100644 index 0000000..fefe788 --- /dev/null +++ b/src/location/maps/qgeocodereply_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODEREPLY_P_H +#define QGEOCODEREPLY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeocodereply.h" + +#include "qgeoshape.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGeoLocation; + +class QGeoCodeReplyPrivate +{ +public: + QGeoCodeReplyPrivate(); + QGeoCodeReplyPrivate(QGeoCodeReply::Error error, const QString &errorString); + ~QGeoCodeReplyPrivate(); + + QGeoCodeReply::Error error; + QString errorString; + bool isFinished; + + QGeoShape viewport; + QList locations; + + int limit; + int offset; +private: + Q_DISABLE_COPY(QGeoCodeReplyPrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeocodingmanager.cpp b/src/location/maps/qgeocodingmanager.cpp new file mode 100644 index 0000000..73d6841 --- /dev/null +++ b/src/location/maps/qgeocodingmanager.cpp @@ -0,0 +1,325 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodingmanager.h" +#include "qgeocodingmanager_p.h" +#include "qgeocodingmanagerengine.h" + +#include "qgeorectangle.h" +#include "qgeocircle.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoCodingManager + \inmodule QtLocation + \ingroup QtLocation-geocoding + \since 5.6 + + \brief The QGeoCodingManager class provides support for geocoding + operations. + + The geocode() and reverseGeocode() functions return + QGeoCodeReply objects, which manage these operations and report on the + result of the operations and any errors which may have occurred. + + The geocode() and reverseGeocode() functions can be used to convert + QGeoAddress instances to QGeoCoordinate instances and vice-versa. + + The geocode() function is also overloaded to allow a user to perform a free text + geocoding operation, if the string provided can be interpreted as + an address it can be geocoded to coordinate information. + + Instances of QGeoCodingManager can be accessed with + QGeoServiceProvider::geocodingManager(). +*/ + +/*! + Constructs a new manager with the specified \a parent and with the + implementation provided by \a engine. + + This constructor is used interally by QGeoServiceProviderFactory. Regular + users should acquire instances of QGeoCodingManager with + QGeoServiceProvider::geocodingManager(); +*/ +QGeoCodingManager::QGeoCodingManager(QGeoCodingManagerEngine *engine, QObject *parent) + : QObject(parent), + d_ptr(new QGeoCodingManagerPrivate()) +{ + d_ptr->engine = engine; + if (d_ptr->engine) { + d_ptr->engine->setParent(this); + + connect(d_ptr->engine, + SIGNAL(finished(QGeoCodeReply*)), + this, + SIGNAL(finished(QGeoCodeReply*))); + + connect(d_ptr->engine, + SIGNAL(error(QGeoCodeReply*,QGeoCodeReply::Error,QString)), + this, + SIGNAL(error(QGeoCodeReply*,QGeoCodeReply::Error,QString))); + } else { + qFatal("The geocoding manager engine that was set for this geocoding manager was NULL."); + } +} + +/*! + Destroys this manager. +*/ +QGeoCodingManager::~QGeoCodingManager() +{ + delete d_ptr; +} + +/*! + Returns the name of the engine which implements the behaviour of this + geocoding manager. + + The combination of managerName() and managerVersion() should be unique + amongst the plugin implementations. +*/ +QString QGeoCodingManager::managerName() const +{ +// if (!d_ptr->engine) +// return QString(); + + return d_ptr->engine->managerName(); +} + +/*! + Returns the version of the engine which implements the behaviour of this + geocoding manager. + + The combination of managerName() and managerVersion() should be unique + amongst the plugin implementations. +*/ +int QGeoCodingManager::managerVersion() const +{ +// if (!d_ptr->engine) +// return -1; + + return d_ptr->engine->managerVersion(); +} + +/*! + Begins the geocoding of \a address. Geocoding is the process of finding a + coordinate that corresponds to a given address. + + A QGeoCodeReply object will be returned, which can be used to manage the + geocoding operation and to return the results of the operation. + + This manager and the returned QGeoCodeReply object will emit signals + indicating if the operation completes or if errors occur. + + If supportsGeocoding() returns false an + QGeoCodeReply::UnsupportedOptionError will occur. + + Once the operation has completed, QGeoCodeReply::locations() can be used to + retrieve the results, which will consist of a list of QGeoLocation objects. + These objects represent a combination of coordinate and address data. + + The address data returned in the results may be different from \a address. + This will usually occur if the geocoding service backend uses a different + canonical form of addresses or if \a address was only partially filled out. + + If \a bounds is non-null and is a valid QGeoShape it will be used to + limit the results to those that are contained within \a bounds. This is + particularly useful if \a address is only partially filled out, as the + service will attempt to geocode all matches for the specified data. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoCodingManager::finished(), + QGeoCodingManager::error(), QGeoCodeReply::finished() or + QGeoCodeReply::error() with deleteLater(). +*/ +QGeoCodeReply *QGeoCodingManager::geocode(const QGeoAddress &address, const QGeoShape &bounds) +{ + return d_ptr->engine->geocode(address, bounds); +} + + +/*! + Begins the reverse geocoding of \a coordinate. Reverse geocoding is the + process of finding an address that corresponds to a given coordinate. + + A QGeoCodeReply object will be returned, which can be used to manage the + reverse geocoding operation and to return the results of the operation. + + This manager and the returned QGeoCodeReply object will emit signals + indicating if the operation completes or if errors occur. + + If supportsReverseGeocoding() returns false an + QGeoCodeReply::UnsupportedOptionError will occur. + + At that point QGeoCodeReply::locations() can be used to retrieve the + results, which will consist of a list of QGeoLocation objects. These objects + represent a combination of coordinate and address data. + + The coordinate data returned in the results may be different from \a + coordinate. This will usually occur if the reverse geocoding service + backend shifts the coordinates to be closer to the matching addresses, or + if the backend returns results at multiple levels of detail. + + If multiple results are returned by the reverse geocoding service backend + they will be provided in order of specificity. This normally occurs if the + backend is configured to reverse geocode across multiple levels of detail. + As an example, some services will return address and coordinate pairs for + the street address, the city, the state and the country. + + If \a bounds is non-null and a valid QGeoRectangle it will be used to + limit the results to those that are contained within \a bounds. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoCodingManager::finished(), + QGeoCodingManager::error(), QGeoCodeReply::finished() or + QGeoCodeReply::error() with deleteLater(). +*/ +QGeoCodeReply *QGeoCodingManager::reverseGeocode(const QGeoCoordinate &coordinate, const QGeoShape &bounds) +{ + return d_ptr->engine->reverseGeocode(coordinate, bounds); +} + +/*! + Begins geocoding for a location matching \a address. + + A QGeoCodeReply object will be returned, which can be used to manage the + geocoding operation and to return the results of the operation. + + This manager and the returned QGeoCodeReply object will emit signals + indicating if the operation completes or if errors occur. + + Once the operation has completed, QGeoCodeReply::locations() can be used to + retrieve the results, which will consist of a list of QGeoLocation objects. + These objects represent a combination of coordinate and address data. + + If \a limit is -1 the entire result set will be returned, otherwise at most + \a limit results will be returned. + + The \a offset parameter is used to ask the geocoding service to not return the + first \a offset results. + + The \a limit and \a offset results are used together to implement paging. + + If \a bounds is non-null and a valid QGeoShape it will be used to + limit the results to those that are contained within \a bounds. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoCodingManager::finished(), + QGeoCodingManager::error(), QGeoCodeReply::finished() or + QGeoCodeReply::error() with deleteLater(). +*/ +QGeoCodeReply *QGeoCodingManager::geocode(const QString &address, + int limit, + int offset, + const QGeoShape &bounds) +{ + QGeoCodeReply *reply = d_ptr->engine->geocode(address, + limit, + offset, + bounds); + return reply; +} + +/*! + Sets the locale to be used by this manager to \a locale. + + If this geocoding manager supports returning the results + in different languages, they will be returned in the language of \a locale. + + The locale used defaults to the system locale if this is not set. +*/ +void QGeoCodingManager::setLocale(const QLocale &locale) +{ + d_ptr->engine->setLocale(locale); +} + +/*! + Returns the locale used to hint to this geocoding manager about what + language to use for the results. +*/ +QLocale QGeoCodingManager::locale() const +{ + return d_ptr->engine->locale(); +} + +/*! +\fn void QGeoCodingManager::finished(QGeoCodeReply *reply) + + This signal is emitted when \a reply has finished processing. + + If reply::error() equals QGeoCodeReply::NoError then the processing + finished successfully. + + This signal and QGeoCodeReply::finished() will be emitted at the same + time. + + \note Do not delete the \a reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/*! +\fn void QGeoCodingManager::error(QGeoCodeReply *reply, QGeoCodeReply::Error error, QString errorString) + + This signal is emitted when an error has been detected in the processing of + \a reply. The QGeoCodingManager::finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error. + + This signal and QGeoCodeReply::error() will be emitted at the same time. + + \note Do not delete the \a reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/******************************************************************************* +*******************************************************************************/ + +QGeoCodingManagerPrivate::QGeoCodingManagerPrivate() + : engine(0) {} + +QGeoCodingManagerPrivate::~QGeoCodingManagerPrivate() +{ + delete engine; +} + +/******************************************************************************* +*******************************************************************************/ + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeocodingmanager.h b/src/location/maps/qgeocodingmanager.h new file mode 100644 index 0000000..0d89b05 --- /dev/null +++ b/src/location/maps/qgeocodingmanager.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGER_H +#define QGEOCODINGMANAGER_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QLocale; + +class QGeoCodingManagerEngine; +class QGeoCodingManagerPrivate; + +class Q_LOCATION_EXPORT QGeoCodingManager : public QObject +{ + Q_OBJECT +public: + ~QGeoCodingManager(); + + QString managerName() const; + int managerVersion() const; + + QGeoCodeReply *geocode(const QGeoAddress &address, + const QGeoShape &bounds = QGeoShape()); + QGeoCodeReply *geocode(const QString &searchString, + int limit = -1, + int offset = 0, + const QGeoShape &bounds = QGeoShape()); + + QGeoCodeReply *reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds = QGeoShape()); + + void setLocale(const QLocale &locale); + QLocale locale() const; + +Q_SIGNALS: + void finished(QGeoCodeReply *reply); + void error(QGeoCodeReply *reply, QGeoCodeReply::Error error, QString errorString = QString()); + +private: + explicit QGeoCodingManager(QGeoCodingManagerEngine *engine, QObject *parent = nullptr); + + QGeoCodingManagerPrivate *d_ptr; + Q_DISABLE_COPY(QGeoCodingManager) + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeocodingmanager_p.h b/src/location/maps/qgeocodingmanager_p.h new file mode 100644 index 0000000..de723ee --- /dev/null +++ b/src/location/maps/qgeocodingmanager_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGER_P_H +#define QGEOCODINGMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeocodingmanager.h" + +#include "qgeocodereply.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGeoCodingManagerEngine; + +class QGeoCodingManagerPrivate +{ +public: + QGeoCodingManagerPrivate(); + ~QGeoCodingManagerPrivate(); + + QGeoCodingManagerEngine *engine; + +private: + Q_DISABLE_COPY(QGeoCodingManagerPrivate) +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/location/maps/qgeocodingmanagerengine.cpp b/src/location/maps/qgeocodingmanagerengine.cpp new file mode 100644 index 0000000..fb7d4e0 --- /dev/null +++ b/src/location/maps/qgeocodingmanagerengine.cpp @@ -0,0 +1,334 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodingmanagerengine.h" +#include "qgeocodingmanagerengine_p.h" + +#include "qgeoaddress.h" +#include "qgeocoordinate.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoCodingManagerEngine + \inmodule QtLocation + \ingroup QtLocation-impl + \since 5.6 + + \brief The QGeoCodingManagerEngine class provides an interface and + convenience methods to implementers of QGeoServiceProvider plugins who want + to provide support for geocoding operations. + + In the default implementation, supportsGeocoding() and supportsReverseGeocoding() returns false while + geocode() and reverseGeocode() + cause QGeoCodeReply::UnsupportedOptionError to occur. + + If the service provider supports geocoding the subclass should provide an + implementation of geocode() and call setSupportsGeocoding(true) at + some point in time before geocode() is called. + + Similarly, if the service provider supports reverse geocoding the subclass + should provide an implementation reverseGeocode() and call + setSupportsReverseGeocoding(true) at some point in time before + reverseGeocode() is called. + + A subclass of QGeoCodingManagerEngine will often make use of a subclass + fo QGeoCodeReply internally, in order to add any engine-specific + data (such as a QNetworkReply object for network-based services) to the + QGeoCodeReply instances used by the engine. + + \sa QGeoCodingManager +*/ + +/*! + Constructs a new engine with the specified \a parent, using \a parameters + to pass any implementation specific data to the engine. +*/ +QGeoCodingManagerEngine::QGeoCodingManagerEngine(const QVariantMap ¶meters, QObject *parent) + : QObject(parent), + d_ptr(new QGeoCodingManagerEnginePrivate()) +{ + Q_UNUSED(parameters) +} + +/*! + Destroys this engine. +*/ +QGeoCodingManagerEngine::~QGeoCodingManagerEngine() +{ + delete d_ptr; +} + +/*! + Sets the name which this engine implementation uses to distinguish itself + from the implementations provided by other plugins to \a managerName. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +void QGeoCodingManagerEngine::setManagerName(const QString &managerName) +{ + d_ptr->managerName = managerName; +} + +/*! + Returns the name which this engine implementation uses to distinguish + itself from the implementations provided by other plugins. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +QString QGeoCodingManagerEngine::managerName() const +{ + return d_ptr->managerName; +} + +/*! + Sets the version of this engine implementation to \a managerVersion. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +void QGeoCodingManagerEngine::setManagerVersion(int managerVersion) +{ + d_ptr->managerVersion = managerVersion; +} + +/*! + Returns the version of this engine implementation. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +int QGeoCodingManagerEngine::managerVersion() const +{ + return d_ptr->managerVersion; +} + +/*! + Begins the geocoding of \a address. Geocoding is the process of finding a + coordinate that corresponds to a given address. + + A QGeoCodeReply object will be returned, which can be used to manage the + geocoding operation and to return the results of the operation. + + This engine and the returned QGeoCodeReply object will emit signals + indicating if the operation completes or if errors occur. + + If supportsGeocoding() returns false an + QGeoCodeReply::UnsupportedOptionError will occur. + + Once the operation has completed, QGeoCodeReply::locations() can be used to + retrieve the results, which will consist of a list of QGeoLocation objects. + These objects represent a combination of coordinate and address data. + + The address data returned in the results may be different from \a address. + This will usually occur if the geocoding service backend uses a different + canonical form of addresses or if \a address was only partially filled out. + + If \a bounds is non-null and a valid QGeoShape it will be used to + limit the results to those that are contained by \a bounds. This is + particularly useful if \a address is only partially filled out, as the + service will attempt to geocode all matches for the specified data. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoCodingManagerEngine::finished(), + QGeoCodingManagerEngine::error(), QGeoCodeReply::finished() or + QGeoCodeReply::error() with deleteLater(). +*/ +QGeoCodeReply *QGeoCodingManagerEngine::geocode(const QGeoAddress &address, + const QGeoShape &bounds) +{ + Q_UNUSED(address) + Q_UNUSED(bounds) + return new QGeoCodeReply(QGeoCodeReply::UnsupportedOptionError, + QLatin1String("Geocoding is not supported by this service provider."), this); +} + +/*! + Begins the reverse geocoding of \a coordinate. Reverse geocoding is the + process of finding an address that corresponds to a given coordinate. + + A QGeoCodeReply object will be returned, which can be used to manage the + reverse geocoding operation and to return the results of the operation. + + This engine and the returned QGeoCodeReply object will emit signals + indicating if the operation completes or if errors occur. + + If supportsReverseGeocoding() returns false an + QGeoCodeReply::UnsupportedOptionError will occur. + + At that point QGeoCodeReply::locations() can be used to retrieve the + results, which will consist of a list of QGeoLocation objects. These objects + represent a combination of coordinate and address data. + + The coordinate data returned in the results may be different from \a + coordinate. This will usually occur if the reverse geocoding service + backend shifts the coordinates to be closer to the matching addresses, or + if the backend returns results at multiple levels of detail. + + If multiple results are returned by the reverse geocoding service backend + they will be provided in order of specificity. This normally occurs if the + backend is configured to reverse geocode across multiple levels of detail. + As an example, some services will return address and coordinate pairs for + the street address, the city, the state and the country. + + If \a bounds is non-null and a valid QGeoShape it will be used to + limit the results to those that are contained by \a bounds. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoCodingManagerEngine::finished(), + QGeoCodingManagerEngine::error(), QGeoCodeReply::finished() or + QGeoCodeReply::error() with deleteLater(). +*/ +QGeoCodeReply *QGeoCodingManagerEngine::reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) +{ + Q_UNUSED(coordinate) + Q_UNUSED(bounds) + return new QGeoCodeReply(QGeoCodeReply::UnsupportedOptionError, + QLatin1String("Reverse geocoding is not supported by this service provider."), this); +} + +/*! + Begins geocoding for a location matching \a address. + + A QGeoCodeReply object will be returned, which can be used to manage the + geocoding operation and to return the results of the operation. + + This engine and the returned QGeoCodeReply object will emit signals + indicating if the operation completes or if errors occur. + + Once the operation has completed, QGeoCodeReply::locations() can be used to + retrieve the results, which will consist of a list of QGeoLocation objects. + These objects represent a combination of coordinate and address data. + + If \a limit is -1 the entire result set will be returned, otherwise at most + \a limit results will be returned. + + The \a offset parameter is used to ask the geocoding service to not return the + first \a offset results. + + The \a limit and \a offset results are used together to implement paging. + + If \a bounds is non-null and a valid QGeoShape it will be used to + limit the results to those that are contained by \a bounds. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoCodingManagerEngine::finished(), + QGeoCodingManagerEngine::error(), QGeoCodeReply::finished() or + QGeoCodeReply::error() with deleteLater(). +*/ +QGeoCodeReply *QGeoCodingManagerEngine::geocode(const QString &address, + int limit, + int offset, + const QGeoShape &bounds) +{ + Q_UNUSED(address) + Q_UNUSED(limit) + Q_UNUSED(offset) + Q_UNUSED(bounds) + + return new QGeoCodeReply(QGeoCodeReply::UnsupportedOptionError, + QLatin1String("Searching is not supported by this service provider."), this); +} + +/*! + Sets the locale to be used by this manager to \a locale. + + If this geocoding manager supports returning the results + in different languages, they will be returned in the language of \a locale. + + The locale used defaults to the system locale if this is not set. +*/ +void QGeoCodingManagerEngine::setLocale(const QLocale &locale) +{ + d_ptr->locale = locale; +} + +/*! + Returns the locale used to hint to this geocoding manager about what + language to use for the results. +*/ +QLocale QGeoCodingManagerEngine::locale() const +{ + return d_ptr->locale; +} + +/*! +\fn void QGeoCodingManagerEngine::finished(QGeoCodeReply *reply) + + This signal is emitted when \a reply has finished processing. + + If reply::error() equals QGeoCodeReply::NoError then the processing + finished successfully. + + This signal and QGeoCodeReply::finished() will be emitted at the same + time. + + \note Do not delete the \a reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/*! +\fn void QGeoCodingManagerEngine::error(QGeoCodeReply *reply, QGeoCodeReply::Error error, QString errorString) + + This signal is emitted when an error has been detected in the processing of + \a reply. The QGeoCodingManagerEngine::finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error. + + This signal and QGeoCodeReply::error() will be emitted at the same time. + + \note Do not delete the \a reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/******************************************************************************* +*******************************************************************************/ + +QGeoCodingManagerEnginePrivate::QGeoCodingManagerEnginePrivate() + : managerVersion(-1) +{} + +QGeoCodingManagerEnginePrivate::~QGeoCodingManagerEnginePrivate() +{ +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeocodingmanagerengine.h b/src/location/maps/qgeocodingmanagerengine.h new file mode 100644 index 0000000..eb3bd14 --- /dev/null +++ b/src/location/maps/qgeocodingmanagerengine.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGERENGINE_H +#define QGEOCODINGMANAGERENGINE_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoAddress; +class QGeoShape; +class QGeoCodingManagerEnginePrivate; + +class Q_LOCATION_EXPORT QGeoCodingManagerEngine : public QObject +{ + Q_OBJECT +public: + explicit QGeoCodingManagerEngine(const QVariantMap ¶meters, QObject *parent = nullptr); + virtual ~QGeoCodingManagerEngine(); + + QString managerName() const; + int managerVersion() const; + + virtual QGeoCodeReply *geocode(const QGeoAddress &address, const QGeoShape &bounds); + virtual QGeoCodeReply *geocode(const QString &address, + int limit, + int offset, + const QGeoShape &bounds); + virtual QGeoCodeReply *reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds); + + + void setLocale(const QLocale &locale); + QLocale locale() const; + +Q_SIGNALS: + void finished(QGeoCodeReply *reply); + void error(QGeoCodeReply *reply, QGeoCodeReply::Error error, QString errorString = QString()); + +private: + void setManagerName(const QString &managerName); + void setManagerVersion(int managerVersion); + + QGeoCodingManagerEnginePrivate *d_ptr; + Q_DISABLE_COPY(QGeoCodingManagerEngine) + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeocodingmanagerengine_p.h b/src/location/maps/qgeocodingmanagerengine_p.h new file mode 100644 index 0000000..b6fcb68 --- /dev/null +++ b/src/location/maps/qgeocodingmanagerengine_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGERENGINE_P_H +#define QGEOCODINGMANAGERENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeocodingmanagerengine.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCodingManagerEnginePrivate +{ +public: + QGeoCodingManagerEnginePrivate(); + ~QGeoCodingManagerEnginePrivate(); + + QString managerName; + int managerVersion; + + QLocale locale; + +private: + Q_DISABLE_COPY(QGeoCodingManagerEnginePrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeofiletilecache.cpp b/src/location/maps/qgeofiletilecache.cpp new file mode 100644 index 0000000..df89c88 --- /dev/null +++ b/src/location/maps/qgeofiletilecache.cpp @@ -0,0 +1,627 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeofiletilecache_p.h" + +#include "qgeotilespec_p.h" + +#include "qgeomappingmanager_p.h" + +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QSet) + +QT_BEGIN_NAMESPACE + +class QGeoCachedTileMemory +{ +public: + ~QGeoCachedTileMemory() + { + if (cache) + cache->evictFromMemoryCache(this); + } + + QGeoTileSpec spec; + QGeoFileTileCache *cache; + QByteArray bytes; + QString format; +}; + +void QCache3QTileEvictionPolicy::aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer obj) +{ + Q_UNUSED(key); + // set the cache pointer to zero so we can't call evictFromDiskCache + obj->cache = 0; +} + +void QCache3QTileEvictionPolicy::aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer obj) +{ + Q_UNUSED(key); + Q_UNUSED(obj); + // leave the pointer set if it's a real eviction +} + +QGeoCachedTileDisk::~QGeoCachedTileDisk() +{ + if (cache) + cache->evictFromDiskCache(this); +} + +QGeoFileTileCache::QGeoFileTileCache(const QString &directory, QObject *parent) + : QAbstractGeoTileCache(parent), directory_(directory), minTextureUsage_(0), extraTextureUsage_(0) + ,costStrategyDisk_(ByteSize), costStrategyMemory_(ByteSize), costStrategyTexture_(ByteSize) + ,isDiskCostSet_(false), isMemoryCostSet_(false), isTextureCostSet_(false) +{ + +} + +void QGeoFileTileCache::init() +{ + const QString basePath = baseCacheDirectory() + QLatin1String("QtLocation/"); + + // delete old tiles from QtLocation 5.7 or prior + // Newer version use plugin-specific subdirectories, versioned with qt version so those are not affected. + // TODO Remove cache cleanup in Qt 6 + QDir baseDir(basePath); + if (baseDir.exists()) { + const QStringList oldCacheFiles = baseDir.entryList(QDir::Files); + foreach (const QString& file, oldCacheFiles) + baseDir.remove(file); + const QStringList oldCacheDirs = { QStringLiteral("osm"), QStringLiteral("mapbox"), QStringLiteral("here") }; + foreach (const QString& d, oldCacheDirs) { + QDir oldCacheDir(basePath + QLatin1Char('/') + d); + if (oldCacheDir.exists()) + oldCacheDir.removeRecursively(); + } + } + + if (directory_.isEmpty()) { + directory_ = baseLocationCacheDirectory(); + qWarning() << "Plugin uses uninitialized QGeoFileTileCache directory which was deleted during startup"; + } + + QDir::root().mkpath(directory_); + + // default values + if (!isDiskCostSet_) { // If setMaxDiskUsage has not been called yet + if (costStrategyDisk_ == ByteSize) + setMaxDiskUsage(50 * 1024 * 1024); + else + setMaxDiskUsage(1000); + } + + if (!isMemoryCostSet_) { // If setMaxMemoryUsage has not been called yet + if (costStrategyMemory_ == ByteSize) + setMaxMemoryUsage(3 * 1024 * 1024); + else + setMaxMemoryUsage(100); + } + + if (!isTextureCostSet_) { // If setExtraTextureUsage has not been called yet + if (costStrategyTexture_ == ByteSize) + setExtraTextureUsage(6 * 1024 * 1024); + else + setExtraTextureUsage(30); // byte size of texture is >> compressed image, hence unitary cost should be lower + } + + loadTiles(); +} + +void QGeoFileTileCache::loadTiles() +{ + QStringList formats; + formats << QLatin1String("*.*"); + + QDir dir(directory_); + QStringList files = dir.entryList(formats, QDir::Files); +#if 0 // workaround for QTBUG-60581 + // Method: + // 1. read each queue file then, if each file exists, deserialize the data into the appropriate + // cache queue. + for (int i = 1; i<=4; i++) { + QString filename = dir.filePath(QString::fromLatin1("queue") + QString::number(i)); + QFile file(filename); + if (!file.open(QIODevice::ReadOnly)) + continue; + QList > queue; + QList specs; + QList costs; + while (!file.atEnd()) { + QByteArray line = file.readLine().trimmed(); + QString filename = QString::fromLatin1(line.constData(), line.length()); + if (dir.exists(filename)){ + files.removeOne(filename); + QGeoTileSpec spec = filenameToTileSpec(filename); + if (spec.zoom() == -1) + continue; + QSharedPointer tileDisk(new QGeoCachedTileDisk); + tileDisk->filename = dir.filePath(filename); + tileDisk->cache = this; + tileDisk->spec = spec; + QFileInfo fi(tileDisk->filename); + specs.append(spec); + queue.append(tileDisk); + if (costStrategyDisk_ == ByteSize) + costs.append(fi.size()); + else + costs.append(1); + + } + } + + diskCache_.deserializeQueue(i, specs, queue, costs); + file.close(); + } +#endif + // 2. remaining tiles that aren't registered in a queue get pushed into cache here + // this is a backup, in case the queue manifest files get deleted or out of sync due to + // the application not closing down properly + for (int i = 0; i < files.size(); ++i) { + QGeoTileSpec spec = filenameToTileSpec(files.at(i)); + if (spec.zoom() == -1) + continue; + QString filename = dir.filePath(files.at(i)); + addToDiskCache(spec, filename); + } +} + +QGeoFileTileCache::~QGeoFileTileCache() +{ +#if 0 // workaround for QTBUG-60581 + // write disk cache queues to disk + QDir dir(directory_); + for (int i = 1; i<=4; i++) { + QString filename = dir.filePath(QString::fromLatin1("queue") + QString::number(i)); + QFile file(filename); + if (!file.open(QIODevice::WriteOnly)){ + qWarning() << "Unable to write tile cache file " << filename; + continue; + } + QList > queue; + diskCache_.serializeQueue(i, queue); + foreach (const QSharedPointer &tile, queue) { + if (tile.isNull()) + continue; + + // we just want the filename here, not the full path + int index = tile->filename.lastIndexOf(QLatin1Char('/')); + QByteArray filename = tile->filename.mid(index + 1).toLatin1() + '\n'; + file.write(filename); + } + file.close(); + } +#endif +} + +void QGeoFileTileCache::printStats() +{ + textureCache_.printStats(); + memoryCache_.printStats(); + diskCache_.printStats(); +} + +void QGeoFileTileCache::setMaxDiskUsage(int diskUsage) +{ + diskCache_.setMaxCost(diskUsage); + isDiskCostSet_ = true; +} + +int QGeoFileTileCache::maxDiskUsage() const +{ + return diskCache_.maxCost(); +} + +int QGeoFileTileCache::diskUsage() const +{ + return diskCache_.totalCost(); +} + +void QGeoFileTileCache::setMaxMemoryUsage(int memoryUsage) +{ + memoryCache_.setMaxCost(memoryUsage); + isMemoryCostSet_ = true; +} + +int QGeoFileTileCache::maxMemoryUsage() const +{ + return memoryCache_.maxCost(); +} + +int QGeoFileTileCache::memoryUsage() const +{ + return memoryCache_.totalCost(); +} + +void QGeoFileTileCache::setExtraTextureUsage(int textureUsage) +{ + extraTextureUsage_ = textureUsage; + textureCache_.setMaxCost(minTextureUsage_ + extraTextureUsage_); + isTextureCostSet_ = true; +} + +void QGeoFileTileCache::setMinTextureUsage(int textureUsage) +{ + minTextureUsage_ = textureUsage; + textureCache_.setMaxCost(minTextureUsage_ + extraTextureUsage_); +} + +int QGeoFileTileCache::maxTextureUsage() const +{ + return textureCache_.maxCost(); +} + +int QGeoFileTileCache::minTextureUsage() const +{ + return minTextureUsage_; +} + + +int QGeoFileTileCache::textureUsage() const +{ + return textureCache_.totalCost(); +} + +void QGeoFileTileCache::clearAll() +{ + textureCache_.clear(); + memoryCache_.clear(); + diskCache_.clear(); + QDir dir(directory_); + dir.setNameFilters(QStringList() << QLatin1String("*-*-*-*.*")); + dir.setFilter(QDir::Files); + foreach (QString dirFile, dir.entryList()) { + dir.remove(dirFile); + } +} + +void QGeoFileTileCache::clearMapId(const int mapId) +{ + for (const QGeoTileSpec &k : diskCache_.keys()) + if (k.mapId() == mapId) + diskCache_.remove(k, true); + for (const QGeoTileSpec &k : memoryCache_.keys()) + if (k.mapId() == mapId) + memoryCache_.remove(k); + for (const QGeoTileSpec &k : textureCache_.keys()) + if (k.mapId() == mapId) + textureCache_.remove(k); + + // TODO: It seems the cache leaves residues, like some tiles do not get picked up. + // After the above calls, files that shouldnt be left behind are still on disk. + // Do an additional pass and make sure what has to be deleted gets deleted. + QDir dir(directory_); + QStringList formats; + formats << QLatin1String("*.*"); + QStringList files = dir.entryList(formats, QDir::Files); + qWarning() << "Old tile data detected. Cache eviction left out "<< files.size() << "tiles"; + for (const QString &tileFileName : files) { + QGeoTileSpec spec = filenameToTileSpec(tileFileName); + if (spec.mapId() != mapId) + continue; + QFile::remove(dir.filePath(tileFileName)); + } +} + +void QGeoFileTileCache::setCostStrategyDisk(QAbstractGeoTileCache::CostStrategy costStrategy) +{ + costStrategyDisk_ = costStrategy; +} + +QAbstractGeoTileCache::CostStrategy QGeoFileTileCache::costStrategyDisk() const +{ + return costStrategyDisk_; +} + +void QGeoFileTileCache::setCostStrategyMemory(QAbstractGeoTileCache::CostStrategy costStrategy) +{ + costStrategyMemory_ = costStrategy; +} + +QAbstractGeoTileCache::CostStrategy QGeoFileTileCache::costStrategyMemory() const +{ + return costStrategyMemory_; +} + +void QGeoFileTileCache::setCostStrategyTexture(QAbstractGeoTileCache::CostStrategy costStrategy) +{ + costStrategyTexture_ = costStrategy; +} + +QAbstractGeoTileCache::CostStrategy QGeoFileTileCache::costStrategyTexture() const +{ + return costStrategyTexture_; +} + +QSharedPointer QGeoFileTileCache::get(const QGeoTileSpec &spec) +{ + QSharedPointer tt = getFromMemory(spec); + if (tt) + return tt; + return getFromDisk(spec); +} + +void QGeoFileTileCache::insert(const QGeoTileSpec &spec, + const QByteArray &bytes, + const QString &format, + QAbstractGeoTileCache::CacheAreas areas) +{ + if (bytes.isEmpty()) + return; + + if (areas & QAbstractGeoTileCache::DiskCache) { + QString filename = tileSpecToFilename(spec, format, directory_); + addToDiskCache(spec, filename, bytes); + } + + if (areas & QAbstractGeoTileCache::MemoryCache) { + addToMemoryCache(spec, bytes, format); + } + + /* inserts do not hit the texture cache -- this actually reduces overall + * cache hit rates because many tiles come too late to be useful + * and act as a poison */ +} + +QString QGeoFileTileCache::tileSpecToFilenameDefault(const QGeoTileSpec &spec, const QString &format, const QString &directory) +{ + QString filename = spec.plugin(); + filename += QLatin1String("-"); + filename += QString::number(spec.mapId()); + filename += QLatin1String("-"); + filename += QString::number(spec.zoom()); + filename += QLatin1String("-"); + filename += QString::number(spec.x()); + filename += QLatin1String("-"); + filename += QString::number(spec.y()); + + //Append version if real version number to ensure backwards compatibility and eviction of old tiles + if (spec.version() != -1) { + filename += QLatin1String("-"); + filename += QString::number(spec.version()); + } + + filename += QLatin1String("."); + filename += format; + + QDir dir = QDir(directory); + + return dir.filePath(filename); +} + +QGeoTileSpec QGeoFileTileCache::filenameToTileSpecDefault(const QString &filename) +{ + QGeoTileSpec emptySpec; + + QStringList parts = filename.split('.'); + + if (parts.length() != 2) + return emptySpec; + + QString name = parts.at(0); + QStringList fields = name.split('-'); + + int length = fields.length(); + if (length != 5 && length != 6) + return emptySpec; + + QList numbers; + + bool ok = false; + for (int i = 1; i < length; ++i) { + ok = false; + int value = fields.at(i).toInt(&ok); + if (!ok) + return emptySpec; + numbers.append(value); + } + + //File name without version, append default + if (numbers.length() < 5) + numbers.append(-1); + + return QGeoTileSpec(fields.at(0), + numbers.at(0), + numbers.at(1), + numbers.at(2), + numbers.at(3), + numbers.at(4)); +} + +void QGeoFileTileCache::evictFromDiskCache(QGeoCachedTileDisk *td) +{ + QFile::remove(td->filename); +} + +void QGeoFileTileCache::evictFromMemoryCache(QGeoCachedTileMemory * /* tm */) +{ +} + +QSharedPointer QGeoFileTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename) +{ + QSharedPointer td(new QGeoCachedTileDisk); + td->spec = spec; + td->filename = filename; + td->cache = this; + + int cost = 1; + if (costStrategyDisk_ == ByteSize) { + QFileInfo fi(filename); + cost = fi.size(); + } + diskCache_.insert(spec, td, cost); + return td; +} + +bool QGeoFileTileCache::addToDiskCache(const QGeoTileSpec &spec, const QString &filename, const QByteArray &bytes) +{ + QSharedPointer td(new QGeoCachedTileDisk); + td->spec = spec; + td->filename = filename; + td->cache = this; + + int cost = 1; + if (costStrategyDisk_ == ByteSize) + cost = bytes.size(); + + if (diskCache_.insert(spec, td, cost)) { + QFile file(filename); + file.open(QIODevice::WriteOnly); + file.write(bytes); + file.close(); + return true; + } + return false; +} + +void QGeoFileTileCache::addToMemoryCache(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format) +{ + if (isTileBogus(bytes)) + return; + + QSharedPointer tm(new QGeoCachedTileMemory); + tm->spec = spec; + tm->cache = this; + tm->bytes = bytes; + tm->format = format; + + int cost = 1; + if (costStrategyMemory_ == ByteSize) + cost = bytes.size(); + memoryCache_.insert(spec, tm, cost); +} + +QSharedPointer QGeoFileTileCache::addToTextureCache(const QGeoTileSpec &spec, const QImage &image) +{ + QSharedPointer tt(new QGeoTileTexture); + tt->spec = spec; + tt->image = image; + + int cost = 1; + if (costStrategyTexture_ == ByteSize) + cost = image.width() * image.height() * image.depth() / 8; + textureCache_.insert(spec, tt, cost); + + return tt; +} + +QSharedPointer QGeoFileTileCache::getFromMemory(const QGeoTileSpec &spec) +{ + QSharedPointer tt = textureCache_.object(spec); + if (tt) + return tt; + + QSharedPointer tm = memoryCache_.object(spec); + if (tm) { + QImage image; + if (!image.loadFromData(tm->bytes)) { + handleError(spec, QLatin1String("Problem with tile image")); + return QSharedPointer(0); + } + QSharedPointer tt = addToTextureCache(spec, image); + if (tt) + return tt; + } + return QSharedPointer(); +} + +QSharedPointer QGeoFileTileCache::getFromDisk(const QGeoTileSpec &spec) +{ + QSharedPointer td = diskCache_.object(spec); + if (td) { + const QString format = QFileInfo(td->filename).suffix(); + QFile file(td->filename); + file.open(QIODevice::ReadOnly); + QByteArray bytes = file.readAll(); + file.close(); + + QImage image; + // Some tiles from the servers could be valid images but the tile fetcher + // might be able to recognize them as tiles that should not be shown. + // If that's the case, the tile fetcher should write "NoRetry" inside the file. + if (isTileBogus(bytes)) { + QSharedPointer tt(new QGeoTileTexture); + tt->spec = spec; + tt->image = image; + return tt; + } + + // This is a truly invalid image. The fetcher should try again. + if (!image.loadFromData(bytes)) { + handleError(spec, QLatin1String("Problem with tile image")); + return QSharedPointer(0); + } + + // Converting it here, instead of in each QSGTexture::bind() + if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) + image = image.convertToFormat(QImage::Format_ARGB32_Premultiplied); + + addToMemoryCache(spec, bytes, format); + QSharedPointer tt = addToTextureCache(td->spec, image); + if (tt) + return tt; + } + + return QSharedPointer(); +} + +bool QGeoFileTileCache::isTileBogus(const QByteArray &bytes) const +{ + if (bytes.size() == 7 && bytes == QByteArrayLiteral("NoRetry")) + return true; + return false; +} + +QString QGeoFileTileCache::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const +{ + return tileSpecToFilenameDefault(spec, format, directory); +} + +QGeoTileSpec QGeoFileTileCache::filenameToTileSpec(const QString &filename) const +{ + return filenameToTileSpecDefault(filename); +} + +QString QGeoFileTileCache::directory() const +{ + return directory_; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeofiletilecache_p.h b/src/location/maps/qgeofiletilecache_p.h new file mode 100644 index 0000000..ecbf708 --- /dev/null +++ b/src/location/maps/qgeofiletilecache_p.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOFILETILECACHE_P_H +#define QGEOFILETILECACHE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include +#include +#include "qcache3q_p.h" +#include +#include +#include + +#include "qgeotilespec_p.h" +#include "qgeotiledmappingmanagerengine_p.h" +#include "qabstractgeotilecache_p.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGeoMappingManager; + +class QGeoTile; +class QGeoCachedTileMemory; +class QGeoFileTileCache; + +class QPixmap; +class QThread; + +/* This would be internal to qgeofiletilecache.cpp except that the eviction + * policy can't be defined without it being concrete here */ +class QGeoCachedTileDisk +{ +public: + ~QGeoCachedTileDisk(); + + QGeoTileSpec spec; + QString filename; + QString format; + QGeoFileTileCache *cache; +}; + +/* Custom eviction policy for the disk cache, to avoid deleting all the files + * when the application closes */ +class Q_LOCATION_PRIVATE_EXPORT QCache3QTileEvictionPolicy : public QCache3QDefaultEvictionPolicy +{ +protected: + void aboutToBeRemoved(const QGeoTileSpec &key, QSharedPointer obj); + void aboutToBeEvicted(const QGeoTileSpec &key, QSharedPointer obj); +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoFileTileCache : public QAbstractGeoTileCache +{ + Q_OBJECT +public: + QGeoFileTileCache(const QString &directory = QString(), QObject *parent = 0); + ~QGeoFileTileCache(); + + void setMaxDiskUsage(int diskUsage) override; + int maxDiskUsage() const override; + int diskUsage() const override; + + void setMaxMemoryUsage(int memoryUsage) override; + int maxMemoryUsage() const override; + int memoryUsage() const override; + + void setMinTextureUsage(int textureUsage) override; + void setExtraTextureUsage(int textureUsage) override; + int maxTextureUsage() const override; + int minTextureUsage() const override; + int textureUsage() const override; + void clearAll() override; + void clearMapId(const int mapId); + void setCostStrategyDisk(CostStrategy costStrategy) override; + CostStrategy costStrategyDisk() const override; + void setCostStrategyMemory(CostStrategy costStrategy) override; + CostStrategy costStrategyMemory() const override; + void setCostStrategyTexture(CostStrategy costStrategy) override; + CostStrategy costStrategyTexture() const override; + + + QSharedPointer get(const QGeoTileSpec &spec) override; + + // can be called without a specific tileCache pointer + static void evictFromDiskCache(QGeoCachedTileDisk *td); + static void evictFromMemoryCache(QGeoCachedTileMemory *tm); + + void insert(const QGeoTileSpec &spec, + const QByteArray &bytes, + const QString &format, + QAbstractGeoTileCache::CacheAreas areas = QAbstractGeoTileCache::AllCaches) override; + + static QString tileSpecToFilenameDefault(const QGeoTileSpec &spec, const QString &format, const QString &directory); + static QGeoTileSpec filenameToTileSpecDefault(const QString &filename); + +protected: + void init() override; + void printStats() override; + void loadTiles(); + + QString directory() const; + + QSharedPointer addToDiskCache(const QGeoTileSpec &spec, const QString &filename); + bool addToDiskCache(const QGeoTileSpec &spec, const QString &filename, const QByteArray &bytes); + void addToMemoryCache(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format); + QSharedPointer addToTextureCache(const QGeoTileSpec &spec, const QImage &image); + QSharedPointer getFromMemory(const QGeoTileSpec &spec); + QSharedPointer getFromDisk(const QGeoTileSpec &spec); + + virtual bool isTileBogus(const QByteArray &bytes) const; + virtual QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const; + virtual QGeoTileSpec filenameToTileSpec(const QString &filename) const; + + QCache3Q diskCache_; + QCache3Q memoryCache_; + QCache3Q textureCache_; + + QString directory_; + + int minTextureUsage_; + int extraTextureUsage_; + CostStrategy costStrategyDisk_; + CostStrategy costStrategyMemory_; + CostStrategy costStrategyTexture_; + bool isDiskCostSet_; + bool isMemoryCostSet_; + bool isTextureCostSet_; +}; + +QT_END_NAMESPACE + +#endif // QGEOFILETILECACHE_P_H diff --git a/src/location/maps/qgeomaneuver.cpp b/src/location/maps/qgeomaneuver.cpp new file mode 100644 index 0000000..22f6f58 --- /dev/null +++ b/src/location/maps/qgeomaneuver.cpp @@ -0,0 +1,560 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomaneuver.h" +#include "qgeomaneuver_p.h" + +#include "qgeocoordinate.h" + +QT_BEGIN_NAMESPACE + +template<> +QGeoManeuverPrivate *QSharedDataPointer::clone() +{ + return d->clone(); +} + +/*! + \class QGeoManeuver + \inmodule QtLocation + \ingroup QtLocation-routing + \since 5.6 + + \brief The QGeoManeuver class represents the information relevant to the + point at which two QGeoRouteSegments meet. + + QGeoRouteSegment instances can be thought of as edges on a routing + graph, with QGeoManeuver instances as optional labels attached to the + vertices of the graph. + + The most interesting information help in a QGeoManeuver instance is + normally the textual navigation to provide and the position at which to + provide it, accessible by instructionText() and position() respectively. + + It is also possible to determine if a routing waypoint has been passed by + checking if waypoint() returns a valid QGeoCoordinate. +*/ + +/*! +\enum QGeoManeuver::InstructionDirection + +Describes the change in direction associated with the instruction text +that is associated with a QGeoManaeuver. + +\value NoDirection +There is no direction associated with the instruction text. + +\value DirectionForward +The instruction indicates that the direction of travel does not need to change. + +\value DirectionBearRight +The instruction indicates that the direction of travel should bear to the right. + +\value DirectionLightRight +The instruction indicates that a light turn to the right is required. + +\value DirectionRight +The instruction indicates that a turn to the right is required. + +\value DirectionHardRight +The instruction indicates that a hard turn to the right is required. + +\value DirectionUTurnRight +The instruction indicates that a u-turn to the right is required. + +\value DirectionUTurnLeft +The instruction indicates that a u-turn to the left is required. + +\value DirectionHardLeft +The instruction indicates that a hard turn to the left is required. + +\value DirectionLeft +The instruction indicates that a turn to the left is required. + +\value DirectionLightLeft +The instruction indicates that a light turn to the left is required. + +\value DirectionBearLeft +The instruction indicates that the direction of travel should bear to the left. + +*/ + +/*! + Constructs a invalid maneuver object. + + The maneuver will remain invalid until one of + setPosition(), setInstructionText(), setDirection(), + setTimeToNextInstruction(), setDistanceToNextInstruction() or + setWaypoint() is called. +*/ +QGeoManeuver::QGeoManeuver() + : d_ptr(new QGeoManeuverPrivateDefault()) {} + +/*! + Constructs a maneuver object from the contents of \a other. +*/ +QGeoManeuver::QGeoManeuver(const QGeoManeuver &other) + : d_ptr(other.d_ptr) {} + +/*! + Destroys this maneuver object. +*/ +QGeoManeuver::~QGeoManeuver() {} + +/*! + Assigns \a other to this maneuver object and then returns + a reference to this maneuver object. +*/ +QGeoManeuver &QGeoManeuver::operator= (const QGeoManeuver & other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns whether this maneuver is equal to \a other. +*/ +bool QGeoManeuver::operator== (const QGeoManeuver &other) const +{ + return ( (d_ptr.constData() == other.d_ptr.constData()) + || (*(d_ptr.constData()) == *(other.d_ptr.constData())) ); +} + +/*! + Returns whether this maneuver is not equal to \a other. +*/ +bool QGeoManeuver::operator!= (const QGeoManeuver &other) const +{ + return !(operator==(other)); +} + +/*! + Returns whether this maneuver is valid or not. + + Invalid maneuvers are used when there is no information + that needs to be attached to the endpoint of a QGeoRouteSegment instance. +*/ +bool QGeoManeuver::isValid() const +{ + return d_ptr->valid(); +} + +/*! + Sets the position where instructionText() should be displayed to \a + position. +*/ +void QGeoManeuver::setPosition(const QGeoCoordinate &position) +{ + d_ptr->setValid(true); + d_ptr->setPosition(position); +} + +/*! + Returns the position where instructionText() should be displayed. +*/ +QGeoCoordinate QGeoManeuver::position() const +{ + return d_ptr->position(); +} + +/*! + Sets the textual navigation instructions to \a instructionText. +*/ +void QGeoManeuver::setInstructionText(const QString &instructionText) +{ + d_ptr->setValid(true); + d_ptr->setText(instructionText); +} + +/*! + Returns the textual navigation instructions. +*/ +QString QGeoManeuver::instructionText() const +{ + return d_ptr->text(); +} + +/*! + Sets the direction associated with the associated instruction to \a + direction. +*/ +void QGeoManeuver::setDirection(QGeoManeuver::InstructionDirection direction) +{ + d_ptr->setValid(true); + d_ptr->setDirection(direction); +} + +/*! + Returns the direction associated with the associated instruction. +*/ +QGeoManeuver::InstructionDirection QGeoManeuver::direction() const +{ + return d_ptr->direction(); +} + +/*! + Sets the estimated time it will take to travel from the point at which the + associated instruction was issued and the point that the next instruction + should be issued, in seconds, to \a secs. +*/ +void QGeoManeuver::setTimeToNextInstruction(int secs) +{ + d_ptr->setValid(true); + d_ptr->setTimeToNextInstruction(secs); +} + +/*! + Returns the estimated time it will take to travel from the point at which + the associated instruction was issued and the point that the next + instruction should be issued, in seconds. +*/ +int QGeoManeuver::timeToNextInstruction() const +{ + return d_ptr->timeToNextInstruction(); +} + +/*! + Sets the distance, in meters, between the point at which the associated + instruction was issued and the point that the next instruction should be + issued to \a distance. +*/ +void QGeoManeuver::setDistanceToNextInstruction(qreal distance) +{ + d_ptr->setValid(true); + d_ptr->setDistanceToNextInstruction(distance); +} + +/*! + Returns the distance, in meters, between the point at which the associated + instruction was issued and the point that the next instruction should be + issued. +*/ +qreal QGeoManeuver::distanceToNextInstruction() const +{ + return d_ptr->distanceToNextInstruction(); +} + +/*! + Sets the waypoint associated with this maneuver to \a coordinate. +*/ +void QGeoManeuver::setWaypoint(const QGeoCoordinate &coordinate) +{ + d_ptr->setValid(true); + d_ptr->setWaypoint(coordinate); +} + +/*! + Returns the waypoint associated with this maneuver. + + If there is not waypoint associated with this maneuver an invalid + QGeoCoordinate will be returned. +*/ +QGeoCoordinate QGeoManeuver::waypoint() const +{ + return d_ptr->waypoint(); +} + +QGeoManeuver::QGeoManeuver(const QSharedDataPointer &dd) + : d_ptr(dd) {} + +/*! + Sets the extended attributes \a extendedAttributes associated with this maneuver. + + \since QtLocation 5.11 +*/ +void QGeoManeuver::setExtendedAttributes(const QVariantMap &extendedAttributes) +{ + d_ptr->setValid(true); + d_ptr->setExtendedAttributes(extendedAttributes); +} + +/*! + Returns the extended attributes associated with this maneuver. + + \since QtLocation 5.11 +*/ +QVariantMap QGeoManeuver::extendedAttributes() const +{ + return d_ptr->extendedAttributes(); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoManeuverPrivate::QGeoManeuverPrivate() +{ + +} + +QGeoManeuverPrivate::QGeoManeuverPrivate(const QGeoManeuverPrivate &other) + : QSharedData(other) +{ + +} + +QGeoManeuverPrivate::~QGeoManeuverPrivate() +{ + +} + +bool QGeoManeuverPrivate::operator==(const QGeoManeuverPrivate &other) const +{ + return equals(other); +} + +bool QGeoManeuverPrivate::equals(const QGeoManeuverPrivate &other) const +{ + return ((valid() == other.valid()) + && (position() == other.position()) + && (text() == other.text()) + && (direction() == other.direction()) + && (timeToNextInstruction() == other.timeToNextInstruction()) + && (distanceToNextInstruction() == other.distanceToNextInstruction()) + && (waypoint() == other.waypoint())); +} + +bool QGeoManeuverPrivate::valid() const +{ + return false; +} + +void QGeoManeuverPrivate::setValid(bool valid) +{ + Q_UNUSED(valid) +} + +QString QGeoManeuverPrivate::id() const +{ + return QString(); +} + +void QGeoManeuverPrivate::setId(const QString id) +{ + Q_UNUSED(id) +} + +QGeoCoordinate QGeoManeuverPrivate::position() const +{ + return QGeoCoordinate(); +} + +void QGeoManeuverPrivate::setPosition(const QGeoCoordinate &position) +{ + Q_UNUSED(position) +} + +QString QGeoManeuverPrivate::text() const +{ + return QString(); +} + +void QGeoManeuverPrivate::setText(const QString &text) +{ + Q_UNUSED(text) +} + +QGeoManeuver::InstructionDirection QGeoManeuverPrivate::direction() const +{ + return QGeoManeuver::NoDirection; +} + +void QGeoManeuverPrivate::setDirection(QGeoManeuver::InstructionDirection direction) +{ + Q_UNUSED(direction) +} + +int QGeoManeuverPrivate::timeToNextInstruction() const +{ + return 0; +} + +void QGeoManeuverPrivate::setTimeToNextInstruction(int timeToNextInstruction) +{ + Q_UNUSED(timeToNextInstruction) +} + +qreal QGeoManeuverPrivate::distanceToNextInstruction() const +{ + return 0; +} + +void QGeoManeuverPrivate::setDistanceToNextInstruction(qreal distanceToNextInstruction) +{ + Q_UNUSED(distanceToNextInstruction) +} + +QGeoCoordinate QGeoManeuverPrivate::waypoint() const +{ + return QGeoCoordinate(); +} + +void QGeoManeuverPrivate::setWaypoint(const QGeoCoordinate &waypoint) +{ + Q_UNUSED(waypoint) +} + +QVariantMap QGeoManeuverPrivate::extendedAttributes() const +{ + return QVariantMap(); +} + +void QGeoManeuverPrivate::setExtendedAttributes(const QVariantMap &extendedAttributes) +{ + Q_UNUSED(extendedAttributes) +} + + + +/******************************************************************************* +*******************************************************************************/ + +QGeoManeuverPrivateDefault::QGeoManeuverPrivateDefault() + : m_valid(false), + m_direction(QGeoManeuver::NoDirection), + m_timeToNextInstruction(0), + m_distanceToNextInstruction(0.0) {} + +QGeoManeuverPrivateDefault::QGeoManeuverPrivateDefault(const QGeoManeuverPrivateDefault &other) + : QGeoManeuverPrivate(other), + m_valid(other.m_valid), + m_position(other.m_position), + m_text(other.m_text), + m_direction(other.m_direction), + m_timeToNextInstruction(other.m_timeToNextInstruction), + m_distanceToNextInstruction(other.m_distanceToNextInstruction), + m_waypoint(other.m_waypoint) {} + +QGeoManeuverPrivateDefault::~QGeoManeuverPrivateDefault() {} + +QGeoManeuverPrivate *QGeoManeuverPrivateDefault::clone() +{ + return new QGeoManeuverPrivateDefault(*this); +} + +bool QGeoManeuverPrivateDefault::valid() const +{ + return m_valid; +} + +void QGeoManeuverPrivateDefault::setValid(bool valid) +{ + m_valid = valid; +} + +QString QGeoManeuverPrivateDefault::id() const +{ + return m_id; +} + +void QGeoManeuverPrivateDefault::setId(const QString id) +{ + m_id = id; +} + +QGeoCoordinate QGeoManeuverPrivateDefault::position() const +{ + return m_position; +} + +void QGeoManeuverPrivateDefault::setPosition(const QGeoCoordinate &position) +{ + m_position = position; +} + +QString QGeoManeuverPrivateDefault::text() const +{ + return m_text; +} + +void QGeoManeuverPrivateDefault::setText(const QString &text) +{ + m_text = text; +} + +QGeoManeuver::InstructionDirection QGeoManeuverPrivateDefault::direction() const +{ + return m_direction; +} + +void QGeoManeuverPrivateDefault::setDirection(QGeoManeuver::InstructionDirection direction) +{ + m_direction = direction; +} + +int QGeoManeuverPrivateDefault::timeToNextInstruction() const +{ + return m_timeToNextInstruction; +} + +void QGeoManeuverPrivateDefault::setTimeToNextInstruction(int timeToNextInstruction) +{ + m_timeToNextInstruction = timeToNextInstruction; +} + +qreal QGeoManeuverPrivateDefault::distanceToNextInstruction() const +{ + return m_distanceToNextInstruction; +} + +void QGeoManeuverPrivateDefault::setDistanceToNextInstruction(qreal distanceToNextInstruction) +{ + m_distanceToNextInstruction = distanceToNextInstruction; +} + +QGeoCoordinate QGeoManeuverPrivateDefault::waypoint() const +{ + return m_waypoint; +} + +void QGeoManeuverPrivateDefault::setWaypoint(const QGeoCoordinate &waypoint) +{ + m_waypoint = waypoint; +} + +QVariantMap QGeoManeuverPrivateDefault::extendedAttributes() const +{ + return m_extendedAttributes; +} + +void QGeoManeuverPrivateDefault::setExtendedAttributes(const QVariantMap &extendedAttributes) +{ + m_extendedAttributes = extendedAttributes; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeomaneuver.h b/src/location/maps/qgeomaneuver.h new file mode 100644 index 0000000..52a5b6a --- /dev/null +++ b/src/location/maps/qgeomaneuver.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMANEUVER_H +#define QGEOMANEUVER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QString; + +class QGeoCoordinate; +class QGeoManeuverPrivate; + +class Q_LOCATION_EXPORT QGeoManeuver +{ + +public: + enum InstructionDirection { + NoDirection, + DirectionForward, + DirectionBearRight, + DirectionLightRight, + DirectionRight, + DirectionHardRight, + DirectionUTurnRight, + DirectionUTurnLeft, + DirectionHardLeft, + DirectionLeft, + DirectionLightLeft, + DirectionBearLeft + }; + + QGeoManeuver(); + QGeoManeuver(const QGeoManeuver &other); + ~QGeoManeuver(); + + QGeoManeuver &operator= (const QGeoManeuver &other); + + bool operator== (const QGeoManeuver &other) const; + bool operator!= (const QGeoManeuver &other) const; + + bool isValid() const; + + void setPosition(const QGeoCoordinate &position); + QGeoCoordinate position() const; + + void setInstructionText(const QString &instructionText); + QString instructionText() const; + + void setDirection(InstructionDirection direction); + InstructionDirection direction() const; + + void setTimeToNextInstruction(int secs); + int timeToNextInstruction() const; + + void setDistanceToNextInstruction(qreal distance); + qreal distanceToNextInstruction() const; + + void setWaypoint(const QGeoCoordinate &coordinate); + QGeoCoordinate waypoint() const; + + void setExtendedAttributes(const QVariantMap &extendedAttributes); + QVariantMap extendedAttributes() const; + +protected: + QGeoManeuver(const QSharedDataPointer &dd); + +private: + QSharedDataPointer d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeomaneuver_p.h b/src/location/maps/qgeomaneuver_p.h new file mode 100644 index 0000000..8f2e7ec --- /dev/null +++ b/src/location/maps/qgeomaneuver_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMANEUVER_P_H +#define QGEOMANEUVER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoManeuverPrivate : public QSharedData +{ +public: + QGeoManeuverPrivate(); + QGeoManeuverPrivate(const QGeoManeuverPrivate &other); + virtual ~QGeoManeuverPrivate(); + virtual QGeoManeuverPrivate *clone() = 0; + + bool operator== (const QGeoManeuverPrivate &other) const; + + virtual bool valid() const; + virtual void setValid(bool valid); + + virtual QString id() const; + virtual void setId(const QString id); + + virtual QGeoCoordinate position() const; + virtual void setPosition(const QGeoCoordinate &position); + + virtual QString text() const; + virtual void setText(const QString &text); + + virtual QGeoManeuver::InstructionDirection direction() const; + virtual void setDirection(QGeoManeuver::InstructionDirection direction); + + virtual int timeToNextInstruction() const; + virtual void setTimeToNextInstruction(int timeToNextInstruction); + + virtual qreal distanceToNextInstruction() const; + virtual void setDistanceToNextInstruction(qreal distanceToNextInstruction); + + virtual QGeoCoordinate waypoint() const; + virtual void setWaypoint(const QGeoCoordinate &waypoint); + + virtual QVariantMap extendedAttributes() const; + virtual void setExtendedAttributes(const QVariantMap &extendedAttributes); + +protected: + virtual bool equals(const QGeoManeuverPrivate &other) const; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoManeuverPrivateDefault : public QGeoManeuverPrivate +{ +public: + QGeoManeuverPrivateDefault(); + QGeoManeuverPrivateDefault(const QGeoManeuverPrivateDefault &other); + ~QGeoManeuverPrivateDefault(); + virtual QGeoManeuverPrivate *clone() override; + + virtual bool valid() const override; + virtual void setValid(bool valid) override; + + virtual QString id() const override; + virtual void setId(const QString id) override; + + virtual QGeoCoordinate position() const override; + virtual void setPosition(const QGeoCoordinate &position) override; + + virtual QString text() const override; + virtual void setText(const QString &text) override; + + virtual QGeoManeuver::InstructionDirection direction() const override; + virtual void setDirection(QGeoManeuver::InstructionDirection direction) override; + + virtual int timeToNextInstruction() const override; + virtual void setTimeToNextInstruction(int timeToNextInstruction) override; + + virtual qreal distanceToNextInstruction() const override; + virtual void setDistanceToNextInstruction(qreal distanceToNextInstruction) override; + + virtual QGeoCoordinate waypoint() const override; + virtual void setWaypoint(const QGeoCoordinate &waypoint) override; + + virtual QVariantMap extendedAttributes() const override; + virtual void setExtendedAttributes(const QVariantMap &extendedAttributes) override; + + bool m_valid; + QString m_id; + QGeoCoordinate m_position; + QString m_text; + QGeoManeuver::InstructionDirection m_direction; + int m_timeToNextInstruction; + qreal m_distanceToNextInstruction; + QGeoCoordinate m_waypoint; + QVariantMap m_extendedAttributes; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeomap.cpp b/src/location/maps/qgeomap.cpp new file mode 100644 index 0000000..5f12516 --- /dev/null +++ b/src/location/maps/qgeomap.cpp @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomap_p.h" +#include "qgeomap_p_p.h" +#include "qgeocameracapabilities_p.h" +#include "qgeomappingmanagerengine_p.h" +#include "qdeclarativegeomapitembase_p.h" +#include "qgeomapobject_p.h" +#include "qgeomapobject_p_p.h" +#include + +QT_BEGIN_NAMESPACE + +QGeoMap::QGeoMap(QGeoMapPrivate &dd, QObject *parent) + : QObject(dd,parent) +{ +} + +QGeoMap::~QGeoMap() +{ + Q_D(QGeoMap); + clearParameters(); + for (QGeoMapObject *p : d->mapObjects()) + p->setMap(nullptr); // forces replacing pimpls with the default ones. +} + +void QGeoMap::setViewportSize(const QSize& size) +{ + Q_D(QGeoMap); + if (size == d->m_viewportSize) + return; + d->m_viewportSize = size; + d->m_geoProjection->setViewportSize(size); + d->changeViewportSize(size); +} + +QSize QGeoMap::viewportSize() const +{ + Q_D(const QGeoMap); + return d->m_viewportSize; +} + +int QGeoMap::viewportWidth() const +{ + Q_D(const QGeoMap); + return d->m_viewportSize.width(); +} + +int QGeoMap::viewportHeight() const +{ + Q_D(const QGeoMap); + return d->m_viewportSize.height(); +} + +void QGeoMap::setCameraData(const QGeoCameraData &cameraData) +{ + Q_D(QGeoMap); + if (cameraData == d->m_cameraData) + return; + d->m_cameraData = cameraData; + d->m_geoProjection->setCameraData(cameraData, false); + d->changeCameraData(cameraData); + emit cameraDataChanged(d->m_cameraData); +} + +void QGeoMap::setCameraCapabilities(const QGeoCameraCapabilities &cameraCapabilities) +{ + Q_D(QGeoMap); + d->setCameraCapabilities(cameraCapabilities); +} + +bool QGeoMap::handleEvent(QEvent *event) +{ + Q_UNUSED(event) + return false; +} + +bool QGeoMap::setBearing(qreal bearing, const QGeoCoordinate &coordinate) +{ + Q_D(QGeoMap); + bool res = d->m_geoProjection->setBearing(bearing, coordinate); + if (!res) + return false; + + setCameraData(geoProjection().cameraData()); + return true; +} + +bool QGeoMap::anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint) +{ + Q_D(QGeoMap); + QGeoCoordinate newCenter = geoProjection().anchorCoordinateToPoint(coordinate, anchorPoint); + newCenter.setLatitude(qBound(-d->m_maximumViewportLatitude, newCenter.latitude(), d->m_maximumViewportLatitude)); + QGeoCameraData data = cameraData(); + if (data.center() != newCenter) { + data.setCenter(newCenter); + setCameraData(data); + return true; + } + return false; +} + +bool QGeoMap::fitViewportToGeoRectangle(const QGeoRectangle &rectangle) +{ + Q_UNUSED(rectangle) + return false; +} + +QGeoShape QGeoMap::visibleRegion() const +{ + return geoProjection().visibleRegion(); +} + +QGeoCameraData QGeoMap::cameraData() const +{ + Q_D(const QGeoMap); + return d->m_cameraData; +} + +void QGeoMap::setActiveMapType(const QGeoMapType type) +{ + Q_D(QGeoMap); + if (type == d->m_activeMapType) + return; + d->m_activeMapType = type; + d->setCameraCapabilities(d->m_engine->cameraCapabilities(type.mapId())); // emits + d->changeActiveMapType(type); + emit activeMapTypeChanged(); +} + +const QGeoMapType QGeoMap::activeMapType() const +{ + Q_D(const QGeoMap); + return d->m_activeMapType; +} + +double QGeoMap::minimumZoom() const +{ + Q_D(const QGeoMap); + return d->m_geoProjection->minimumZoom(); +} + +double QGeoMap::maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const +{ + Q_D(const QGeoMap); + return d->maximumCenterLatitudeAtZoom(cameraData); +} + +double QGeoMap::mapWidth() const +{ + Q_D(const QGeoMap); + return d->mapWidth(); +} + +double QGeoMap::mapHeight() const +{ + Q_D(const QGeoMap); + return d->mapHeight(); +} + +const QGeoProjection &QGeoMap::geoProjection() const +{ + Q_D(const QGeoMap); + return *(d->m_geoProjection); +} + +QGeoCameraCapabilities QGeoMap::cameraCapabilities() const +{ + Q_D(const QGeoMap); + return d->m_cameraCapabilities; +} + +QGeoMap::Capabilities QGeoMap::capabilities() const +{ + return Capabilities(QGeoMap::SupportsNothing); +} + +void QGeoMap::prefetchData() +{ + +} + +void QGeoMap::clearData() +{ + +} + +void QGeoMap::addParameter(QGeoMapParameter *param) +{ + Q_D(QGeoMap); + if (param && !d->m_mapParameters.contains(param)) { + d->m_mapParameters.append(param); + d->addParameter(param); + } +} + +void QGeoMap::removeParameter(QGeoMapParameter *param) +{ + Q_D(QGeoMap); + if (param && d->m_mapParameters.contains(param)) { + d->removeParameter(param); + d->m_mapParameters.removeOne(param); + } +} + +void QGeoMap::clearParameters() +{ + Q_D(QGeoMap); + for (QGeoMapParameter *p : qAsConst(d->m_mapParameters)) + d->removeParameter(p); + d->m_mapParameters.clear(); +} + +QGeoMap::ItemTypes QGeoMap::supportedMapItemTypes() const +{ + Q_D(const QGeoMap); + return d->supportedMapItemTypes(); +} + +void QGeoMap::addMapItem(QDeclarativeGeoMapItemBase *item) +{ + Q_D(QGeoMap); + if (item && !d->m_mapItems.contains(item) && d->supportedMapItemTypes() & item->itemType()) { + d->m_mapItems.append(item); + d->addMapItem(item); + } +} + +void QGeoMap::removeMapItem(QDeclarativeGeoMapItemBase *item) +{ + Q_D(QGeoMap); + if (item && d->m_mapItems.contains(item)) { + d->removeMapItem(item); + d->m_mapItems.removeOne(item); + } +} + +void QGeoMap::clearMapItems() +{ + Q_D(QGeoMap); + for (QDeclarativeGeoMapItemBase *p : d->m_mapItems) + d->removeMapItem(p); + d->m_mapItems.clear(); +} + +/*! + Fills obj with a backend-specific pimpl. +*/ +bool QGeoMap::createMapObjectImplementation(QGeoMapObject *obj) +{ + Q_D(QGeoMap); + QExplicitlySharedDataPointer pimpl = + QExplicitlySharedDataPointer(d->createMapObjectImplementation(obj)); + if (pimpl.constData()) + return obj->setImplementation(pimpl); + return false; +} + +/*! + To be called in ~QGeoMapObjectPrivate overrides, if needed +*/ +void QGeoMap::removeMapObject(QGeoMapObject * /*obj*/) +{ +} + +QList QGeoMap::mapObjects() const +{ + Q_D(const QGeoMap); + return d->mapObjects(); +} + +QString QGeoMap::copyrightsStyleSheet() const +{ + return QStringLiteral("#copyright-root { background: rgba(255, 255, 255, 128) }"); +} + +void QGeoMap::setAcceptedGestures(bool pan, bool flick, bool pinch, bool rotate, bool tilt) +{ + Q_UNUSED(pan) + Q_UNUSED(flick) + Q_UNUSED(pinch) + Q_UNUSED(rotate) + Q_UNUSED(tilt) +} + +void QGeoMap::setCopyrightVisible(bool visible) +{ + Q_D(QGeoMap); + if (d->m_copyrightVisible == visible) + return; + + d->m_copyrightVisible = visible; +} + +QGeoMapPrivate::QGeoMapPrivate(QGeoMappingManagerEngine *engine, QGeoProjection *geoProjection) + : QObjectPrivate(), + m_geoProjection(geoProjection), + m_engine(engine), + m_activeMapType(QGeoMapType()) +{ + // Setting the default camera caps without emitting anything + if (engine) + m_cameraCapabilities = m_engine->cameraCapabilities(m_activeMapType.mapId()); +} + +QGeoMapPrivate::~QGeoMapPrivate() +{ + if (m_geoProjection) + delete m_geoProjection; +} + +void QGeoMapPrivate::setCameraCapabilities(const QGeoCameraCapabilities &cameraCapabilities) +{ + Q_Q(QGeoMap); + if (m_cameraCapabilities == cameraCapabilities) + return; + QGeoCameraCapabilities oldCaps = m_cameraCapabilities; + m_cameraCapabilities = cameraCapabilities; + emit q->cameraCapabilitiesChanged(oldCaps); +} + +const QGeoCameraCapabilities &QGeoMapPrivate::cameraCapabilities() const +{ + return m_cameraCapabilities; +} + +const QGeoMapPrivate *QGeoMapPrivate::get(const QGeoMap &map) +{ + return map.d_func(); +} + +void QGeoMapPrivate::addParameter(QGeoMapParameter *param) +{ + Q_UNUSED(param) +} + +void QGeoMapPrivate::removeParameter(QGeoMapParameter *param) +{ + Q_UNUSED(param) +} + +QGeoMap::ItemTypes QGeoMapPrivate::supportedMapItemTypes() const +{ + return QGeoMap::NoItem; +} + +void QGeoMapPrivate::addMapItem(QDeclarativeGeoMapItemBase *item) +{ + Q_UNUSED(item) +} + +void QGeoMapPrivate::removeMapItem(QDeclarativeGeoMapItemBase *item) +{ + Q_UNUSED(item) +} + +QGeoMapObjectPrivate *QGeoMapPrivate::createMapObjectImplementation(QGeoMapObject *obj) +{ + Q_UNUSED(obj) + return nullptr; +} + +QList QGeoMapPrivate::mapObjects() const +{ + return QList(); +} + +double QGeoMapPrivate::mapWidth() const +{ + if (m_geoProjection->projectionType() == QGeoProjection::ProjectionWebMercator) + return static_cast(m_geoProjection)->mapWidth(); + return 0; // override this for maps supporting other projections +} + +double QGeoMapPrivate::mapHeight() const +{ + if (m_geoProjection->projectionType() == QGeoProjection::ProjectionWebMercator) + return static_cast(m_geoProjection)->mapHeight(); + return 0; // override this for maps supporting other projections +} + +void QGeoMapPrivate::setCopyrightVisible(bool visible) +{ + m_copyrightVisible = visible; +} + +bool QGeoMapPrivate::copyrightVisible() const +{ + return m_copyrightVisible; +} + +double QGeoMapPrivate::maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const +{ + m_maximumViewportLatitude = m_geoProjection->maximumCenterLatitudeAtZoom(cameraData); + return m_maximumViewportLatitude; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeomap_p.h b/src/location/maps/qgeomap_p.h new file mode 100644 index 0000000..f6be5ca --- /dev/null +++ b/src/location/maps/qgeomap_p.h @@ -0,0 +1,181 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOMAP_P_H +#define QGEOMAP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMappingManagerEngine; +class QGeoMapPrivate; +class QGeoMapController; +class QGeoCoordinate; +class QSGNode; +class QQuickWindow; +class QGeoMapParameter; +class QDeclarativeGeoMapItemBase; +class QGeoMapObject; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMap : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoMap) + + Q_ENUMS(Capability) + Q_FLAGS(Capabilities) +public: + enum ItemType { + NoItem = 0x0000, + MapRectangle = 0x0001, + MapCircle = 0x0002, + MapPolyline = 0x0004, + MapPolygon = 0x0008, + MapQuickItem = 0x0010, + CustomMapItem = 0x8000 + }; + + Q_DECLARE_FLAGS(ItemTypes, ItemType) + + enum Capability { + SupportsNothing = 0x0000, + SupportsVisibleRegion = 0x0001, + SupportsSetBearing = 0x0002, + SupportsAnchoringCoordinate = 0x0004, + SupportsFittingViewportToGeoRectangle = 0x0008 + }; + + Q_DECLARE_FLAGS(Capabilities, Capability) + + virtual ~QGeoMap(); + + // Sets the display size + void setViewportSize(const QSize& viewportSize); + QSize viewportSize() const; + int viewportWidth() const; + int viewportHeight() const; + + + QGeoCameraData cameraData() const; + QGeoCameraCapabilities cameraCapabilities() const; + virtual Capabilities capabilities() const; + + void setActiveMapType(const QGeoMapType mapType); + const QGeoMapType activeMapType() const; + + // returns the minimum zoom at the current viewport size + double minimumZoom() const; + double maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const; + + // returns the size of the underlying map, at the current zoom level. Unrelated to width()/height()/size(). + double mapWidth() const; + double mapHeight() const; + + const QGeoProjection &geoProjection() const; + + virtual void prefetchData(); + virtual void clearData(); + + void addParameter(QGeoMapParameter *param); + void removeParameter(QGeoMapParameter *param); + void clearParameters(); + + ItemTypes supportedMapItemTypes() const; + + void addMapItem(QDeclarativeGeoMapItemBase *item); + void removeMapItem(QDeclarativeGeoMapItemBase *item); + void clearMapItems(); + + virtual bool createMapObjectImplementation(QGeoMapObject *obj); + QList mapObjects() const; + + + virtual QString copyrightsStyleSheet() const; + virtual void setAcceptedGestures(bool pan, bool flick, bool pinch, bool rotate, bool tilt); + virtual bool handleEvent(QEvent *event); + + virtual bool setBearing(qreal bearing, const QGeoCoordinate &coordinate); + virtual QGeoShape visibleRegion() const; + virtual bool anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint); + virtual bool fitViewportToGeoRectangle(const QGeoRectangle &rectangle); + + virtual void setCopyrightVisible(bool visible); + virtual void removeMapObject(QGeoMapObject *obj); + +protected: + QGeoMap(QGeoMapPrivate &dd, QObject *parent = 0); + void setCameraData(const QGeoCameraData &cameraData); + void setCameraCapabilities(const QGeoCameraCapabilities &cameraCapabilities); + virtual QSGNode *updateSceneGraph(QSGNode *node, QQuickWindow *window) = 0; + +Q_SIGNALS: + void cameraDataChanged(const QGeoCameraData &cameraData); + void sgNodeChanged(); + void activeMapTypeChanged(); + void cameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities); + void copyrightsChanged(const QImage ©rightsImage); + void copyrightsChanged(const QString ©rightsHtml); + void copyrightsStyleSheetChanged(const QString &styleSheet); + +private: + Q_DISABLE_COPY(QGeoMap) + friend class QDeclarativeGeoMap; //updateSceneGraph + friend class QGeoMapPrivate; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoMap::ItemTypes) + +QT_END_NAMESPACE + +#endif // QGEOMAP_P_H diff --git a/src/location/maps/qgeomap_p_p.h b/src/location/maps/qgeomap_p_p.h new file mode 100644 index 0000000..47780c4 --- /dev/null +++ b/src/location/maps/qgeomap_p_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOMAP_P_P_H +#define QGEOMAP_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qgeomap_p.h" + + +QT_BEGIN_NAMESPACE + +class QGeoMappingManagerEngine; +class QGeoMap; +class QGeoMapController; +class QGeoMapParameter; +class QDeclarativeGeoMapItemBase; +class QGeoMapObjectPrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGeoMap) +public: + QGeoMapPrivate(QGeoMappingManagerEngine *engine, QGeoProjection *geoProjection); + virtual ~QGeoMapPrivate(); + + const QGeoProjection *geoProjection() const; + void setCameraCapabilities(const QGeoCameraCapabilities &cameraCapabilities); + const QGeoCameraCapabilities &cameraCapabilities() const; + + static const QGeoMapPrivate *get(const QGeoMap &map); + virtual QGeoMapObjectPrivate *createMapObjectImplementation(QGeoMapObject *obj); + +protected: + /* Hooks into the actual map implementations */ + virtual void addParameter(QGeoMapParameter *param); + virtual void removeParameter(QGeoMapParameter *param); + + virtual QGeoMap::ItemTypes supportedMapItemTypes() const; + virtual void addMapItem(QDeclarativeGeoMapItemBase *item); + virtual void removeMapItem(QDeclarativeGeoMapItemBase *item); + + virtual QList mapObjects() const; + + virtual void changeViewportSize(const QSize &size) = 0; // called by QGeoMap::setSize() + virtual void changeCameraData(const QGeoCameraData &oldCameraData) = 0; // called by QGeoMap::setCameraData() + virtual void changeActiveMapType(const QGeoMapType mapType) = 0; // called by QGeoMap::setActiveMapType() + + virtual double mapWidth() const; + virtual double mapHeight() const; + + virtual void setCopyrightVisible(bool visible); + virtual bool copyrightVisible() const; + virtual double maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const; + +protected: + QSize m_viewportSize; + QGeoProjection *m_geoProjection; + QPointer m_engine; + QGeoCameraData m_cameraData; + QGeoMapType m_activeMapType; + QList m_mapParameters; + QList m_mapItems; + QGeoCameraCapabilities m_cameraCapabilities; + bool m_copyrightVisible = true; + mutable double m_maximumViewportLatitude = 0; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAP_P_P_H diff --git a/src/location/maps/qgeomapparameter.cpp b/src/location/maps/qgeomapparameter.cpp new file mode 100644 index 0000000..b1d6f06 --- /dev/null +++ b/src/location/maps/qgeomapparameter.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qgeomapparameter_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoMapParameter::QGeoMapParameter(QObject *parent) : QObject(parent) +{ + +} + +QGeoMapParameter::QGeoMapParameter(const QList > &properties, QObject *parent) : QObject(parent) +{ + for (const auto &p: properties) { + if (p.first == QLatin1String("type")) + setType(p.second.toString()); + else + updateProperty(p.first.data(), p.second); + } +} + +QGeoMapParameter::~QGeoMapParameter() +{ +} + +bool QGeoMapParameter::operator==(const QGeoMapParameter &other) const +{ + return (other.toVariantMap() == toVariantMap()); +} + +QString QGeoMapParameter::type() const +{ + return m_type; +} + +void QGeoMapParameter::setType(const QString &type) +{ + if (m_type.isEmpty()) + m_type = type; +} + +// DO NOT USE to set "type" +void QGeoMapParameter::updateProperty(const char *propertyName, QVariant value) +{ + setProperty(propertyName, value); + // This should technically be emitted only for dynamically added properties. + // Since this object has only type defined as Q_PROPERTY() which is a set-once + // no check is really needed here. + emit propertyUpdated(this, propertyName); +} + +QVariantMap QGeoMapParameter::toVariantMap() const +{ + QVariantMap res; + const QMetaObject *metaObj = metaObject(); + for (int i = 2; i < metaObj->propertyCount(); ++i) { // 0 is objectName, 1 is type, we want to skip both of them here. + const char *propName = metaObj->property(i).name(); + res[QLatin1String(propName)] = property(propName); + } + return res; +} + +QT_END_NAMESPACE + diff --git a/src/location/maps/qgeomapparameter_p.h b/src/location/maps/qgeomapparameter_p.h new file mode 100644 index 0000000..413b420 --- /dev/null +++ b/src/location/maps/qgeomapparameter_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGEOMAPPARAMETER_P_H +#define QGEOMAPPARAMETER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapParameter : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString type READ type WRITE setType) +public: + explicit QGeoMapParameter(QObject *parent = 0); + QGeoMapParameter(const QList> &properties, QObject *parent = 0); + virtual ~QGeoMapParameter(); + + bool operator==(const QGeoMapParameter &other) const; + + virtual QString type() const; + virtual void setType(const QString &type); + + void updateProperty(const char *propertyName, QVariant value); + + QVariantMap toVariantMap() const; + +Q_SIGNALS: + void propertyUpdated(QGeoMapParameter *param, const char *propertyName); + +protected: + QString m_type; + + Q_DISABLE_COPY(QGeoMapParameter) + friend class QGeoMap; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPPARAMETER_P_H diff --git a/src/location/maps/qgeomappingmanager.cpp b/src/location/maps/qgeomappingmanager.cpp new file mode 100644 index 0000000..d73050f --- /dev/null +++ b/src/location/maps/qgeomappingmanager.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomappingmanager_p.h" +#include "qgeomappingmanager_p_p.h" +#include "qgeomappingmanagerengine_p.h" +#include "qgeotiledmapreply_p.h" +#include "qgeocameracapabilities_p.h" + + +#include "qgeomap_p.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoMappingManager + \inmodule QtLocation + \ingroup QtLocation-maps + \since 5.6 + \internal + + \brief The QGeoMappingManager class provides support for displaying + and interacting with maps. +*/ + +/*! + Constructs a new manager with the specified \a parent and with the + implementation provided by \a engine. + + This constructor is used internally by QGeoServiceProviderFactory. Regular + users should acquire instances of QGeoMappingManager with + QGeoServiceProvider::mappingManager() +*/ +QGeoMappingManager::QGeoMappingManager(QGeoMappingManagerEngine *engine, QObject *parent) + : QObject(parent), + d_ptr(new QGeoMappingManagerPrivate) +{ + d_ptr->engine = engine; + if (!d_ptr->engine) { + qFatal("The mapping manager engine that was set for this mapping manager was NULL."); + } + + connect(d_ptr->engine, + SIGNAL(initialized()), + this, + SIGNAL(initialized()), + Qt::QueuedConnection); + + connect(d_ptr->engine, + SIGNAL(supportedMapTypesChanged()), + this, + SIGNAL(supportedMapTypesChanged()), + Qt::QueuedConnection); +} + +/*! + Destroys this mapping manager. +*/ +QGeoMappingManager::~QGeoMappingManager() +{ + delete d_ptr; +} + +/*! + \fn void QGeoMappingManager::initialized() + + This signal is emitted when the mapping manager has been initialized + and is ready to be used. +*/ + +/*! + Returns the name of the engine which implements the behaviour of this + mapping manager. + + The combination of managerName() and managerVersion() should be unique + amongst the plugin implementations. +*/ +QString QGeoMappingManager::managerName() const +{ + return d_ptr->engine->managerName(); +} + +/*! + Returns the version of the engine which implements the behaviour of this + mapping manager. + + The combination of managerName() and managerVersion() should be unique + amongst the plugin implementations. +*/ +int QGeoMappingManager::managerVersion() const +{ + return d_ptr->engine->managerVersion(); +} + +/*! + Returns a new QGeoMap instance which will be managed by this manager. +*/ +QGeoMap *QGeoMappingManager::createMap(QObject *parent) +{ + Q_UNUSED(parent) + return d_ptr->engine->createMap(); +} + +QList QGeoMappingManager::supportedMapTypes() const +{ + return d_ptr->engine->supportedMapTypes(); +} + +/*! + Return whether the manager has been initialized + (will be done automatically but may take some time). + +*/ +bool QGeoMappingManager::isInitialized() const +{ + return d_ptr->engine->isInitialized(); +} + +/*! + Sets the locale to be used by the this manager to \a locale. + + If this mapping manager supports returning map labels + in different languages, they will be returned in the language of \a locale. + + The locale used defaults to the system locale if this is not set. +*/ +void QGeoMappingManager::setLocale(const QLocale &locale) +{ + d_ptr->engine->setLocale(locale); +} + +/*! + Returns the locale used to hint to this mapping manager about what + language to use for map labels. +*/ +QLocale QGeoMappingManager::locale() const +{ + return d_ptr->engine->locale(); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoMappingManagerPrivate::QGeoMappingManagerPrivate() + : engine(0) {} + +QGeoMappingManagerPrivate::~QGeoMappingManagerPrivate() +{ + delete engine; + engine = 0; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeomappingmanager_p.h b/src/location/maps/qgeomappingmanager_p.h new file mode 100644 index 0000000..2f8e528 --- /dev/null +++ b/src/location/maps/qgeomappingmanager_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPPINGMANAGER_H +#define QGEOMAPPINGMANAGER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMap; +class QLocale; +class QGeoRectangle; +class QGeoCoordinate; +class QGeoMappingManagerPrivate; +class QGeoMapRequestOptions; +class QGeoMappingManagerEngine; +class QGeoCameraCapabilities; + + +class Q_LOCATION_PRIVATE_EXPORT QGeoMappingManager : public QObject +{ + Q_OBJECT + +public: + ~QGeoMappingManager(); + + QString managerName() const; + int managerVersion() const; + + QGeoMap *createMap(QObject *parent); + + QList supportedMapTypes() const; + + bool isInitialized() const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + +Q_SIGNALS: + void initialized(); + void supportedMapTypesChanged(); + +protected: + QGeoMappingManager(QGeoMappingManagerEngine *engine, QObject *parent = 0); + +private: + QGeoMappingManagerPrivate *d_ptr; + Q_DISABLE_COPY(QGeoMappingManager) + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeomappingmanager_p_p.h b/src/location/maps/qgeomappingmanager_p_p.h new file mode 100644 index 0000000..65d1b5e --- /dev/null +++ b/src/location/maps/qgeomappingmanager_p_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPPINGMANAGER_P_H +#define QGEOMAPPINGMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMappingManagerPrivate +{ +public: + QGeoMappingManagerPrivate(); + ~QGeoMappingManagerPrivate(); + + QGeoMappingManagerEngine *engine; + +private: + Q_DISABLE_COPY(QGeoMappingManagerPrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeomappingmanagerengine.cpp b/src/location/maps/qgeomappingmanagerengine.cpp new file mode 100644 index 0000000..187b30e --- /dev/null +++ b/src/location/maps/qgeomappingmanagerengine.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomappingmanagerengine_p.h" +#include "qgeomappingmanagerengine_p_p.h" +#include "qgeotiledmapreply_p.h" +#include "qgeotilespec_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoMappingManagerEngine + \inmodule QtLocation + \ingroup QtLocation-impl + \since 5.6 + \internal + + \brief Provides support functionality for map display with QGeoServiceProvider. + + The QGeoMappingManagerEngine class provides an interface and convenience + methods to implementors of QGeoServiceProvider plugins who want to + provide support for displaying and interacting with maps. +*/ + +/*! + Constructs a new engine with the specified \a parent. +*/ +QGeoMappingManagerEngine::QGeoMappingManagerEngine(QObject *parent) + : QObject(parent), + d_ptr(new QGeoMappingManagerEnginePrivate()) {} + +/*! + Destroys this engine. +*/ +QGeoMappingManagerEngine::~QGeoMappingManagerEngine() +{ + Q_D(QGeoMappingManagerEngine); + delete d; +} + +/*! + Marks the engine as initialized. Subclasses of QGeoMappingManagerEngine are to + call this method after performing implementation-specific initializatioin within + the constructor. +*/ +void QGeoMappingManagerEngine::engineInitialized() +{ + Q_D(QGeoMappingManagerEngine); + d->initialized = true; + emit initialized(); +} + +/*! + Sets the name which this engine implementation uses to distinguish itself + from the implementations provided by other plugins to \a managerName. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +void QGeoMappingManagerEngine::setManagerName(const QString &managerName) +{ + d_ptr->managerName = managerName; +} + +/*! + Returns the name which this engine implementation uses to distinguish + itself from the implementations provided by other plugins. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +QString QGeoMappingManagerEngine::managerName() const +{ + return d_ptr->managerName; +} + +/*! + Sets the version of this engine implementation to \a managerVersion. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +void QGeoMappingManagerEngine::setManagerVersion(int managerVersion) +{ + d_ptr->managerVersion = managerVersion; +} + +/*! + Returns the version of this engine implementation. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +int QGeoMappingManagerEngine::managerVersion() const +{ + return d_ptr->managerVersion; +} + +QList QGeoMappingManagerEngine::supportedMapTypes() const +{ + Q_D(const QGeoMappingManagerEngine); + return d->supportedMapTypes; +} + +/*! + Sets the list of map types supported by this engine to \a mapTypes. + + Subclasses of QGeoMappingManagerEngine should use this function to ensure + that supportedMapTypes() provides accurate information. +*/ +void QGeoMappingManagerEngine::setSupportedMapTypes(const QList &supportedMapTypes) +{ + Q_D(QGeoMappingManagerEngine); + d->supportedMapTypes = supportedMapTypes; + emit supportedMapTypesChanged(); +} + +QGeoCameraCapabilities QGeoMappingManagerEngine::cameraCapabilities(int mapId) const +{ + Q_UNUSED(mapId) + Q_D(const QGeoMappingManagerEngine); + + if (mapId == 0) + return d->capabilities_; + int idx = mapId - 1; + if (idx >= supportedMapTypes().size()) + return d->capabilities_; + return supportedMapTypes().at(idx).cameraCapabilities(); +} + +void QGeoMappingManagerEngine::setCameraCapabilities(const QGeoCameraCapabilities &capabilities) +{ + Q_D(QGeoMappingManagerEngine); + d->capabilities_ = capabilities; +} + +/*! + Return whether the engine has been initialized and is ready to be used. +*/ + +bool QGeoMappingManagerEngine::isInitialized() const +{ + Q_D(const QGeoMappingManagerEngine); + return d->initialized; +} + +/*! + Sets the locale to be used by the this manager to \a locale. + + If this mapping manager supports returning map labels + in different languages, they will be returned in the language of \a locale. + + The locale used defaults to the system locale if this is not set. +*/ +void QGeoMappingManagerEngine::setLocale(const QLocale &locale) +{ + d_ptr->locale = locale; +} + +/*! + Returns the locale used to hint to this mapping manager about what + language to use for map labels. +*/ +QLocale QGeoMappingManagerEngine::locale() const +{ + return d_ptr->locale; +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoMappingManagerEnginePrivate::QGeoMappingManagerEnginePrivate() + : managerVersion(-1), + initialized(false) {} + +QGeoMappingManagerEnginePrivate::~QGeoMappingManagerEnginePrivate() {} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeomappingmanagerengine_p.h b/src/location/maps/qgeomappingmanagerengine_p.h new file mode 100644 index 0000000..dd4aa68 --- /dev/null +++ b/src/location/maps/qgeomappingmanagerengine_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPPINGMANAGERENGINE_H +#define QGEOMAPPINGMANAGERENGINE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qgeomaptype_p.h" +#include "qgeomappingmanager_p.h" + +QT_BEGIN_NAMESPACE + +class QLocale; + +class QGeoRectangle; +class QGeoCoordinate; +class QGeoMappingManagerPrivate; +class QGeoMapRequestOptions; + +class QGeoMappingManagerEnginePrivate; +class QGeoMap; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMappingManagerEngine : public QObject +{ + Q_OBJECT + +public: + explicit QGeoMappingManagerEngine(QObject *parent = 0); + virtual ~QGeoMappingManagerEngine(); + + virtual QGeoMap *createMap() = 0; + + QVariantMap parameters() const; + + QString managerName() const; + int managerVersion() const; + + QList supportedMapTypes() const; + + // the class is private, so this can be virtual here for now. + QGeoCameraCapabilities cameraCapabilities(int mapId = 0) const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + + bool isInitialized() const; + +Q_SIGNALS: + void initialized(); + void supportedMapTypesChanged(); + +protected: + void setSupportedMapTypes(const QList &supportedMapTypes); + void setCameraCapabilities(const QGeoCameraCapabilities &capabilities); + + void engineInitialized(); + +private: + QGeoMappingManagerEnginePrivate *d_ptr; + + void setManagerName(const QString &managerName); + void setManagerVersion(int managerVersion); + + Q_DECLARE_PRIVATE(QGeoMappingManagerEngine) + Q_DISABLE_COPY(QGeoMappingManagerEngine) + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeomappingmanagerengine_p_p.h b/src/location/maps/qgeomappingmanagerengine_p_p.h new file mode 100644 index 0000000..5442686 --- /dev/null +++ b/src/location/maps/qgeomappingmanagerengine_p_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPPINGMANAGERENGINE_P_H +#define QGEOMAPPINGMANAGERENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include "qgeomaptype_p.h" +#include "qgeomappingmanager_p.h" +#include "qgeocameracapabilities_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoTileSpec; +class QGeoTiledMapReply; + +class QGeoMappingManagerEnginePrivate +{ +public: + QGeoMappingManagerEnginePrivate(); + ~QGeoMappingManagerEnginePrivate(); + + QString managerName; + int managerVersion; + + QList supportedMapTypes; + QGeoCameraCapabilities capabilities_; + + QLocale locale; + bool initialized; + +private: + Q_DISABLE_COPY(QGeoMappingManagerEnginePrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeomaptype.cpp b/src/location/maps/qgeomaptype.cpp new file mode 100644 index 0000000..c463599 --- /dev/null +++ b/src/location/maps/qgeomaptype.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomaptype_p.h" +#include "qgeomaptype_p_p.h" + +QT_BEGIN_NAMESPACE + +QGeoMapType::QGeoMapType() + : d_ptr(new QGeoMapTypePrivate()) {} + +QGeoMapType::QGeoMapType(const QGeoMapType &other) + : d_ptr(other.d_ptr) {} + +QGeoMapType::QGeoMapType(QGeoMapType::MapStyle style, const QString &name, + const QString &description, bool mobile, bool night, int mapId, + const QByteArray &pluginName, + const QGeoCameraCapabilities &cameraCapabilities, + const QVariantMap &metadata) +: d_ptr(new QGeoMapTypePrivate(style, name, description, mobile, night, mapId, pluginName, cameraCapabilities, metadata)) +{ +} + +QGeoMapType::~QGeoMapType() {} + +QGeoMapType &QGeoMapType::operator = (const QGeoMapType &other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +bool QGeoMapType::operator == (const QGeoMapType &other) const +{ + return (*d_ptr.constData() == *other.d_ptr.constData()); +} + +bool QGeoMapType::operator != (const QGeoMapType &other) const +{ + return !(operator ==(other)); +} + +QGeoMapType::MapStyle QGeoMapType::style() const +{ + return d_ptr->style_; +} + +QString QGeoMapType::name() const +{ + return d_ptr->name_; +} + +QString QGeoMapType::description() const +{ + return d_ptr->description_; +} + +bool QGeoMapType::mobile() const +{ + return d_ptr->mobile_; +} + +bool QGeoMapType::night() const +{ + return d_ptr->night_; +} + +int QGeoMapType::mapId() const +{ + return d_ptr->mapId_; +} + +QByteArray QGeoMapType::pluginName() const +{ + return d_ptr->pluginName_; +} + +QGeoCameraCapabilities QGeoMapType::cameraCapabilities() const +{ + return d_ptr->cameraCapabilities_; +} + +QVariantMap QGeoMapType::metadata() const +{ + return d_ptr->metadata_; +} + +QGeoMapTypePrivate::QGeoMapTypePrivate() +: style_(QGeoMapType::NoMap), mobile_(false), night_(false), mapId_(0) +{ +} + +QGeoMapTypePrivate::QGeoMapTypePrivate(const QGeoMapTypePrivate &other) +: QSharedData(other), style_(other.style_), name_(other.name_), description_(other.description_), + mobile_(other.mobile_), night_(other.night_), mapId_(other.mapId_), pluginName_(other.pluginName_), + cameraCapabilities_(other.cameraCapabilities_), metadata_(other.metadata_) +{ +} + +QGeoMapTypePrivate::QGeoMapTypePrivate(QGeoMapType::MapStyle style, const QString &name, + const QString &description, bool mobile, bool night, + int mapId, const QByteArray &pluginName, + const QGeoCameraCapabilities &cameraCapabilities, + const QVariantMap &metadata) +: style_(style), name_(name), description_(description), mobile_(mobile), night_(night), + mapId_(mapId), pluginName_(pluginName), cameraCapabilities_(cameraCapabilities), metadata_(metadata) +{ +} + +QGeoMapTypePrivate::~QGeoMapTypePrivate() +{ +} + +bool QGeoMapTypePrivate::operator==(const QGeoMapTypePrivate &other) const +{ + return pluginName_ == other.pluginName_ && style_ == other.style_ && name_ == other.name_ && + description_ == other.description_ && mobile_ == other.mobile_ && night_ == other.night_ && + mapId_ == other.mapId_ && cameraCapabilities_ == other.cameraCapabilities_ && + metadata_ == other.metadata_; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeomaptype_p.h b/src/location/maps/qgeomaptype_p.h new file mode 100644 index 0000000..3ce0e95 --- /dev/null +++ b/src/location/maps/qgeomaptype_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPTYPE_H +#define QGEOMAPTYPE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMapTypePrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoMapType +{ +public: + enum MapStyle { // ### Qt6: change this to be a QFlags instead, or remove. + NoMap = 0, + StreetMap, + SatelliteMapDay, + SatelliteMapNight, + TerrainMap, + HybridMap, + TransitMap, + GrayStreetMap, + PedestrianMap, + CarNavigationMap, + CycleMap, + CustomMap = 100 + }; + + QGeoMapType(); + QGeoMapType(const QGeoMapType &other); + QGeoMapType(MapStyle style, const QString &name, const QString &description, bool mobile, + bool night, int mapId, const QByteArray &pluginName, + const QGeoCameraCapabilities &cameraCapabilities, + const QVariantMap &metadata = QVariantMap()); + ~QGeoMapType(); + + QGeoMapType &operator = (const QGeoMapType &other); + + bool operator == (const QGeoMapType &other) const; + bool operator != (const QGeoMapType &other) const; + + MapStyle style() const; + QString name() const; + QString description() const; + bool mobile() const; + bool night() const; + int mapId() const; + QByteArray pluginName() const; + QGeoCameraCapabilities cameraCapabilities() const; + QVariantMap metadata() const; + +private: + QSharedDataPointer d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPTYPE_H diff --git a/src/location/maps/qgeomaptype_p_p.h b/src/location/maps/qgeomaptype_p_p.h new file mode 100644 index 0000000..e66991a --- /dev/null +++ b/src/location/maps/qgeomaptype_p_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPTYPE_P_H +#define QGEOMAPTYPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include "qgeocameracapabilities_p.h" +#include "qgeomaptype_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoMapTypePrivate : public QSharedData +{ +public: + QGeoMapTypePrivate(); + QGeoMapTypePrivate(QGeoMapType::MapStyle style, const QString &name, const QString &description, bool mobile, + bool night, int mapId, const QByteArray &pluginName, + const QGeoCameraCapabilities &cameraCapabilities, + const QVariantMap &metadata); + QGeoMapTypePrivate(const QGeoMapTypePrivate &other); + ~QGeoMapTypePrivate(); + + QGeoMapTypePrivate &operator = (const QGeoMapTypePrivate &other); + + bool operator == (const QGeoMapTypePrivate &other) const; + + QGeoMapType::MapStyle style_; + QString name_; + QString description_; + bool mobile_; + bool night_; + int mapId_; + QByteArray pluginName_; + QGeoCameraCapabilities cameraCapabilities_; + QVariantMap metadata_; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoMapTypePrivate) + +#endif // QGEOMAPTYPE_P_H diff --git a/src/location/maps/qgeoprojection.cpp b/src/location/maps/qgeoprojection.cpp new file mode 100644 index 0000000..74736ce --- /dev/null +++ b/src/location/maps/qgeoprojection.cpp @@ -0,0 +1,776 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoprojection_p.h" +#include +#include +#include +#include +#include +#include +#include + +namespace { + static const double defaultTileSize = 256.0; + static const QDoubleVector3D xyNormal(0.0, 0.0, 1.0); + static const QGeoProjectionWebMercator::Plane xyPlane(QDoubleVector3D(0,0,0), QDoubleVector3D(0,0,1)); + static const QList mercatorGeometry = { + QDoubleVector2D(-1.0,0.0), + QDoubleVector2D( 2.0,0.0), + QDoubleVector2D( 2.0,1.0), + QDoubleVector2D(-1.0,1.0) }; +} + +static QMatrix4x4 toMatrix4x4(const QDoubleMatrix4x4 &m) +{ + return QMatrix4x4(m(0,0), m(0,1), m(0,2), m(0,3), + m(1,0), m(1,1), m(1,2), m(1,3), + m(2,0), m(2,1), m(2,2), m(2,3), + m(3,0), m(3,1), m(3,2), m(3,3)); +} + +QT_BEGIN_NAMESPACE + +QGeoProjection::QGeoProjection() +{ + +} + +QGeoProjection::~QGeoProjection() +{ + +} + +QGeoCoordinate QGeoProjection::anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint) const +{ + Q_UNUSED(coordinate) + Q_UNUSED(anchorPoint) + return QGeoCoordinate(); +} + +QGeoShape QGeoProjection::visibleRegion() const +{ + return QGeoShape(); +} + +bool QGeoProjection::setBearing(qreal bearing, const QGeoCoordinate &coordinate) +{ + Q_UNUSED(bearing) + Q_UNUSED(coordinate) + return false; +} + + +/* + * QGeoProjectionWebMercator implementation +*/ + +QGeoCoordinate QGeoProjectionWebMercator::anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint) const +{ + // Approach: find the displacement in (wrapped) mercator space, and apply that to the center + QDoubleVector2D centerProj = geoToWrappedMapProjection(cameraData().center()); + QDoubleVector2D coordProj = geoToWrappedMapProjection(coordinate); + + QDoubleVector2D anchorProj = itemPositionToWrappedMapProjection(QDoubleVector2D(anchorPoint)); + // Y-clamping done in mercatorToCoord + return wrappedMapProjectionToGeo(centerProj + coordProj - anchorProj); +} + +bool QGeoProjectionWebMercator::setBearing(qreal bearing, const QGeoCoordinate &coordinate) +{ + const QDoubleVector2D coordWrapped = geoToWrappedMapProjection(coordinate); + if (!isProjectable(coordWrapped)) + return false; + const QPointF rotationPoint = wrappedMapProjectionToItemPosition(coordWrapped).toPointF(); + + QGeoCameraData camera = cameraData(); + // first set bearing + camera.setBearing(bearing); + setCameraData(camera); + camera = cameraData(); + + // then reanchor + const QGeoCoordinate center = anchorCoordinateToPoint(coordinate, rotationPoint); + camera.setCenter(center); + setCameraData(camera); + return true; +} + +QGeoProjectionWebMercator::QGeoProjectionWebMercator() + : QGeoProjection(), + m_mapEdgeSize(256), // at zl 0 + m_minimumZoom(0), + m_cameraCenterXMercator(0), + m_cameraCenterYMercator(0), + m_viewportWidth(1), + m_viewportHeight(1), + m_1_viewportWidth(0), + m_1_viewportHeight(0), + m_sideLength(256), + m_aperture(0.0), + m_nearPlane(0.0), + m_farPlane(0.0), + m_halfWidth(0.0), + m_halfHeight(0.0), + m_minimumUnprojectableY(0.0), + m_verticalEstateToSkip(0.0), + m_visibleRegionDirty(false) +{ +} + +QGeoProjectionWebMercator::~QGeoProjectionWebMercator() +{ + +} + +// This method returns the minimum zoom level that this specific qgeomap type allows +// at the current viewport size and for the default tile size of 256^2. +double QGeoProjectionWebMercator::minimumZoom() const +{ + return m_minimumZoom; +} + +// This method recalculates the "no-trespassing" limits for the map center. +// This has to be used when: +// 1) the map is resized, because the meters per pixel remain the same, but +// the amount of pixels between the center and the borders changes +// 2) when the zoom level changes, because the amount of pixels between the center +// and the borders stays the same, but the meters per pixel change +double QGeoProjectionWebMercator::maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const +{ + double mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * defaultTileSize; + + // At init time weird things happen + int clampedWindowHeight = (m_viewportHeight > mapEdgeSize) ? mapEdgeSize : m_viewportHeight; + + // Use the window height divided by 2 as the topmost allowed center, with respect to the map size in pixels + double mercatorTopmost = (clampedWindowHeight * 0.5) / mapEdgeSize ; + QGeoCoordinate topMost = QWebMercator::mercatorToCoord(QDoubleVector2D(0.0, mercatorTopmost)); + + return topMost.latitude(); +} + +double QGeoProjectionWebMercator::mapWidth() const +{ + return m_mapEdgeSize; +} + +double QGeoProjectionWebMercator::mapHeight() const +{ + return m_mapEdgeSize; +} + +void QGeoProjectionWebMercator::setViewportSize(const QSize &size) +{ + if (int(m_viewportWidth) == size.width() && int(m_viewportHeight) == size.height()) + return; + + m_viewportWidth = size.width(); + m_viewportHeight = size.height(); + m_1_viewportWidth = 1.0 / m_viewportWidth; + m_1_viewportHeight = 1.0 / m_viewportHeight; + m_minimumZoom = std::log(qMax(m_viewportWidth, m_viewportHeight) / defaultTileSize) / std::log(2.0); + setupCamera(); +} + +void QGeoProjectionWebMercator::setCameraData(const QGeoCameraData &cameraData, bool force) +{ + if (m_cameraData == cameraData && !force) + return; + + m_cameraData = cameraData; + m_mapEdgeSize = std::pow(2.0, cameraData.zoomLevel()) * defaultTileSize; + setupCamera(); +} + +QDoubleVector2D QGeoProjectionWebMercator::geoToMapProjection(const QGeoCoordinate &coordinate) const +{ + return QWebMercator::coordToMercator(coordinate); +} + +QGeoCoordinate QGeoProjectionWebMercator::mapProjectionToGeo(const QDoubleVector2D &projection) const +{ + return QWebMercator::mercatorToCoord(projection); +} + +//wraps around center +QDoubleVector2D QGeoProjectionWebMercator::wrapMapProjection(const QDoubleVector2D &projection) const +{ + double x = projection.x(); + if (m_cameraCenterXMercator < 0.5) { + if (x - m_cameraCenterXMercator > 0.5 ) + x -= 1.0; + } else if (m_cameraCenterXMercator > 0.5) { + if (x - m_cameraCenterXMercator < -0.5 ) + x += 1.0; + } + + return QDoubleVector2D(x, projection.y()); +} + +QDoubleVector2D QGeoProjectionWebMercator::unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const +{ + double x = wrappedProjection.x(); + if (x > 1.0) + return QDoubleVector2D(x - 1.0, wrappedProjection.y()); + if (x <= 0.0) + return QDoubleVector2D(x + 1.0, wrappedProjection.y()); + return wrappedProjection; +} + +QDoubleVector2D QGeoProjectionWebMercator::wrappedMapProjectionToItemPosition(const QDoubleVector2D &wrappedProjection) const +{ + return (m_transformation * wrappedProjection).toVector2D(); +} + +QDoubleVector2D QGeoProjectionWebMercator::itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const +{ + QDoubleVector2D pos = itemPosition; + pos *= QDoubleVector2D(m_1_viewportWidth, m_1_viewportHeight); + pos *= 2.0; + pos -= QDoubleVector2D(1.0,1.0); + + double s; + QDoubleVector2D res = viewportToWrappedMapProjection(pos, s); + + // a positive s means a point behind the camera. So do it again, after clamping Y. See QTBUG-61813 + if (s > 0.0) { + pos = itemPosition; + // when the camera is tilted, picking a point above the horizon returns a coordinate behind the camera + pos.setY(m_minimumUnprojectableY); + pos *= QDoubleVector2D(m_1_viewportWidth, m_1_viewportHeight); + pos *= 2.0; + pos -= QDoubleVector2D(1.0,1.0); + res = viewportToWrappedMapProjection(pos, s); + } + + return res; +} + +/* Default implementations */ +QGeoCoordinate QGeoProjectionWebMercator::itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport) const +{ + if (qIsNaN(pos.x()) || qIsNaN(pos.y())) + return QGeoCoordinate(); + + if (clipToViewport) { + int w = m_viewportWidth; + int h = m_viewportHeight; + + if ((pos.x() < 0) || (w < pos.x()) || (pos.y() < 0) || (h < pos.y())) + return QGeoCoordinate(); + } + + QDoubleVector2D wrappedMapProjection = itemPositionToWrappedMapProjection(pos); + // With rotation/tilting, a screen position might end up outside the projection space. + if (!isProjectable(wrappedMapProjection)) + return QGeoCoordinate(); + return mapProjectionToGeo(unwrapMapProjection(wrappedMapProjection)); +} + +QDoubleVector2D QGeoProjectionWebMercator::coordinateToItemPosition(const QGeoCoordinate &coordinate, bool clipToViewport) const +{ + if (!coordinate.isValid()) + return QDoubleVector2D(qQNaN(), qQNaN()); + + QDoubleVector2D wrappedProjection = wrapMapProjection(geoToMapProjection(coordinate)); + if (!isProjectable(wrappedProjection)) + return QDoubleVector2D(qQNaN(), qQNaN()); + + QDoubleVector2D pos = wrappedMapProjectionToItemPosition(wrappedProjection); + + if (clipToViewport) { + int w = m_viewportWidth; + int h = m_viewportHeight; + double x = pos.x(); + double y = pos.y(); + if ((x < -0.5) || (x > w + 0.5) || (y < -0.5) || (y > h + 0.5) || qIsNaN(x) || qIsNaN(y)) + return QDoubleVector2D(qQNaN(), qQNaN()); + } + return pos; +} + +QDoubleVector2D QGeoProjectionWebMercator::geoToWrappedMapProjection(const QGeoCoordinate &coordinate) const +{ + return wrapMapProjection(geoToMapProjection(coordinate)); +} + +QGeoCoordinate QGeoProjectionWebMercator::wrappedMapProjectionToGeo(const QDoubleVector2D &wrappedProjection) const +{ + return mapProjectionToGeo(unwrapMapProjection(wrappedProjection)); +} + +QMatrix4x4 QGeoProjectionWebMercator::quickItemTransformation(const QGeoCoordinate &coordinate, const QPointF &anchorPoint, qreal zoomLevel) const +{ + const QDoubleVector2D coordWrapped = geoToWrappedMapProjection(coordinate); + double scale = std::pow(0.5, zoomLevel - m_cameraData.zoomLevel()); + const QDoubleVector2D anchorScaled = QDoubleVector2D(anchorPoint.x(), anchorPoint.y()) * scale; + const QDoubleVector2D anchorMercator = anchorScaled / mapWidth(); + + const QDoubleVector2D coordAnchored = coordWrapped - anchorMercator; + const QDoubleVector2D coordAnchoredScaled = coordAnchored * m_sideLength; + QDoubleMatrix4x4 matTranslateScale; + matTranslateScale.translate(coordAnchoredScaled.x(), coordAnchoredScaled.y(), 0.0); + + scale = std::pow(0.5, (zoomLevel - std::floor(zoomLevel)) + + (std::floor(zoomLevel) - std::floor(m_cameraData.zoomLevel()))); + matTranslateScale.scale(scale); + + /* + * The full transformation chain for quickItemTransformation() would be: + * matScreenShift * m_quickItemTransformation * matTranslate * matScale + * where: + * matScreenShift = translate(-coordOnScreen.x(), -coordOnScreen.y(), 0) + * matTranslate = translate(coordAnchoredScaled.x(), coordAnchoredScaled.y(), 0.0) + * matScale = scale(scale) + * + * However, matScreenShift is removed, as setPosition(0,0) is used in place of setPositionOnScreen. + */ + + return toMatrix4x4(m_quickItemTransformation * matTranslateScale); +} + +bool QGeoProjectionWebMercator::isProjectable(const QDoubleVector2D &wrappedProjection) const +{ + if (m_cameraData.tilt() == 0.0) + return true; + + QDoubleVector3D pos = wrappedProjection * m_sideLength; + // use m_centerNearPlane in order to add an offset to m_eye. + QDoubleVector3D p = m_centerNearPlane - pos; + double dot = QDoubleVector3D::dotProduct(p , m_viewNormalized); + + if (dot < 0.0) // behind the near plane + return false; + return true; +} + +QList QGeoProjectionWebMercator::visibleGeometry() const +{ + if (m_visibleRegionDirty) + const_cast(this)->updateVisibleRegion(); + return m_visibleRegion; +} + +QList QGeoProjectionWebMercator::visibleGeometryExpanded() const +{ + if (m_visibleRegionDirty) + const_cast(this)->updateVisibleRegion(); + return m_visibleRegionExpanded; +} + +QList QGeoProjectionWebMercator::projectableGeometry() const +{ + if (m_visibleRegionDirty) + const_cast(this)->updateVisibleRegion(); + return m_projectableRegion; +} + +QGeoShape QGeoProjectionWebMercator::visibleRegion() const +{ + const QList &visibleRegion = visibleGeometry(); + QGeoPolygon poly; + for (int i = 0; i < visibleRegion.size(); ++i) { + const QDoubleVector2D &c = visibleRegion.at(i); + // If a segment spans more than half of the map longitudinally, split in 2. + if (i && qAbs(visibleRegion.at(i-1).x() - c.x()) >= 0.5) { // This assumes a segment is never >= 1.0 (whole map span) + QDoubleVector2D extraPoint = (visibleRegion.at(i-1) + c) * 0.5; + poly.addCoordinate(wrappedMapProjectionToGeo(extraPoint)); + } + poly.addCoordinate(wrappedMapProjectionToGeo(c)); + } + if (visibleRegion.size() >= 2 && qAbs(visibleRegion.last().x() - visibleRegion.first().x()) >= 0.5) { + QDoubleVector2D extraPoint = (visibleRegion.last() + visibleRegion.first()) * 0.5; + poly.addCoordinate(wrappedMapProjectionToGeo(extraPoint)); + } + + return poly; +} + +QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const +{ + double s; + return viewportToWrappedMapProjection(itemPosition, s); +} + +/* + actual implementation of itemPositionToWrappedMapProjection +*/ +QDoubleVector2D QGeoProjectionWebMercator::viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition, double &s) const +{ + QDoubleVector2D pos = itemPosition; + pos *= QDoubleVector2D(m_halfWidth, m_halfHeight); + + QDoubleVector3D p = m_centerNearPlane; + p += m_up * pos.y(); + p += m_side * pos.x(); + + QDoubleVector3D ray = m_eye - p; + ray.normalize(); + + return (xyPlane.lineIntersection(m_eye, ray, s) / m_sideLength).toVector2D(); +} + +QGeoProjection::ProjectionGroup QGeoProjectionWebMercator::projectionGroup() const +{ + return QGeoProjection::ProjectionCylindrical; +} + +QGeoProjection::Datum QGeoProjectionWebMercator::datum() const +{ + return QGeoProjection::DatumWGS84; +} + +QGeoProjection::ProjectionType QGeoProjectionWebMercator::projectionType() const +{ + return QGeoProjection::ProjectionWebMercator; +} + +void QGeoProjectionWebMercator::setupCamera() +{ + m_centerMercator = geoToMapProjection(m_cameraData.center()); + m_cameraCenterXMercator = m_centerMercator.x(); + m_cameraCenterYMercator = m_centerMercator.y(); + + int intZoomLevel = static_cast(std::floor(m_cameraData.zoomLevel())); + m_sideLength = (1 << intZoomLevel) * defaultTileSize; + m_center = m_centerMercator * m_sideLength; + //aperture(90 / 2) = 1 + m_aperture = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5); + + double f = m_viewportHeight; + double z = std::pow(2.0, m_cameraData.zoomLevel() - intZoomLevel) * defaultTileSize; + double altitude = f / (2.0 * z); + // Also in mercator space + double z_mercator = std::pow(2.0, m_cameraData.zoomLevel()) * defaultTileSize; + double altitude_mercator = f / (2.0 * z_mercator); + + // calculate eye + m_eye = m_center; + m_eye.setZ(altitude * defaultTileSize / m_aperture); + + // And in mercator space + m_eyeMercator = m_centerMercator; + m_eyeMercator.setZ(altitude_mercator / m_aperture); + + m_view = m_eye - m_center; + QDoubleVector3D side = QDoubleVector3D::normal(m_view, QDoubleVector3D(0.0, 1.0, 0.0)); + m_up = QDoubleVector3D::normal(side, m_view); + + // In mercator space too + m_viewMercator = m_eyeMercator - m_centerMercator; + QDoubleVector3D sideMercator = QDoubleVector3D::normal(m_viewMercator, QDoubleVector3D(0.0, 1.0, 0.0)); + m_upMercator = QDoubleVector3D::normal(sideMercator, m_viewMercator); + + if (m_cameraData.bearing() > 0.0) { + QDoubleMatrix4x4 mBearing; + mBearing.rotate(m_cameraData.bearing(), m_view); + m_up = mBearing * m_up; + + // In mercator space too + QDoubleMatrix4x4 mBearingMercator; + mBearingMercator.rotate(m_cameraData.bearing(), m_viewMercator); + m_upMercator = mBearingMercator * m_upMercator; + } + + m_side = QDoubleVector3D::normal(m_up, m_view); + m_sideMercator = QDoubleVector3D::normal(m_upMercator, m_viewMercator); + + if (m_cameraData.tilt() > 0.0) { // tilt has been already thresholded by QGeoCameraData::setTilt + QDoubleMatrix4x4 mTilt; + mTilt.rotate(-m_cameraData.tilt(), m_side); + m_eye = mTilt * m_view + m_center; + + // In mercator space too + QDoubleMatrix4x4 mTiltMercator; + mTiltMercator.rotate(-m_cameraData.tilt(), m_sideMercator); + m_eyeMercator = mTiltMercator * m_viewMercator + m_centerMercator; + } + + m_view = m_eye - m_center; // ToDo: this should be inverted (center - eye), and the rest should follow + m_viewNormalized = m_view.normalized(); + m_up = QDoubleVector3D::normal(m_view, m_side); + + m_nearPlane = 1.0; + // At ZL 20 the map has 2^20 tiles per side. That is 1048576. + // Placing the camera on one corner of the map, rotated toward the opposite corner, and tilted + // at almost 90 degrees would require a frustum that can span the whole size of this map. + // For this reason, the far plane is set to 2 * 2^20 * defaultTileSize. + // That is, in order to make sure that the whole map would fit in the frustum at this ZL. + // Since we are using a double matrix, and since the largest value in the matrix is going to be + // 2 * m_farPlane (as near plane is 1.0), there should be sufficient precision left. + // + // TODO: extend this to support clip distance. + m_farPlane = (altitude + 2097152.0) * defaultTileSize; + + m_viewMercator = m_eyeMercator - m_centerMercator; + m_upMercator = QDoubleVector3D::normal(m_viewMercator, m_sideMercator); + m_nearPlaneMercator = 1.0 / m_sideLength; + + double aspectRatio = 1.0 * m_viewportWidth / m_viewportHeight; + + m_halfWidth = m_aperture * aspectRatio; + m_halfHeight = m_aperture; + + double verticalHalfFOV = QLocationUtils::degrees(atan(m_aperture)); + + QDoubleMatrix4x4 cameraMatrix; + cameraMatrix.lookAt(m_eye, m_center, m_up); + + QDoubleMatrix4x4 projectionMatrix; + projectionMatrix.frustum(-m_halfWidth, m_halfWidth, -m_halfHeight, m_halfHeight, m_nearPlane, m_farPlane); + + /* + * The full transformation chain for m_transformation is: + * matScreen * matScreenFit * matShift * projectionMatrix * cameraMatrix * matZoomLevelScale + * where: + * matZoomLevelScale = scale(m_sideLength, m_sideLength, 1.0) + * matShift = translate(1.0, 1.0, 0.0) + * matScreenFit = scale(0.5, 0.5, 1.0) + * matScreen = scale(m_viewportWidth, m_viewportHeight, 1.0) + */ + + QDoubleMatrix4x4 matScreenTransformation; + matScreenTransformation.scale(0.5 * m_viewportWidth, 0.5 * m_viewportHeight, 1.0); + matScreenTransformation(0,3) = 0.5 * m_viewportWidth; + matScreenTransformation(1,3) = 0.5 * m_viewportHeight; + + m_transformation = matScreenTransformation * projectionMatrix * cameraMatrix; + m_quickItemTransformation = m_transformation; + m_transformation.scale(m_sideLength, m_sideLength, 1.0); + + m_centerNearPlane = m_eye - m_viewNormalized; + m_centerNearPlaneMercator = m_eyeMercator - m_viewNormalized * m_nearPlaneMercator; + + // The method does not support tilting angles >= 90.0 or < 0. + + // The following formula is used to have a growing epsilon with the zoom level, + // in order not to have too large values at low zl, which would overflow when converted to Clipper::cInt. + const double upperBoundEpsilon = 1.0 / std::pow(10, 1.0 + m_cameraData.zoomLevel() / 5.0); + const double elevationUpperBound = 90.0 - upperBoundEpsilon; + const double maxRayElevation = qMin(elevationUpperBound - m_cameraData.tilt(), verticalHalfFOV); + double maxHalfAperture = 0; + m_verticalEstateToSkip = 0; + if (maxRayElevation < verticalHalfFOV) { + maxHalfAperture = tan(QLocationUtils::radians(maxRayElevation)); + m_verticalEstateToSkip = 1.0 - maxHalfAperture / m_aperture; + } + + m_minimumUnprojectableY = m_verticalEstateToSkip * 0.5 * m_viewportHeight; // m_verticalEstateToSkip is relative to half aperture + m_visibleRegionDirty = true; +} + +void QGeoProjectionWebMercator::updateVisibleRegion() +{ + m_visibleRegionDirty = false; + + QDoubleVector2D tl = viewportToWrappedMapProjection(QDoubleVector2D(-1, -1 + m_verticalEstateToSkip )); + QDoubleVector2D tr = viewportToWrappedMapProjection(QDoubleVector2D( 1, -1 + m_verticalEstateToSkip )); + QDoubleVector2D bl = viewportToWrappedMapProjection(QDoubleVector2D(-1, 1 )); + QDoubleVector2D br = viewportToWrappedMapProjection(QDoubleVector2D( 1, 1 )); + + // To make sure that what is returned can be safely converted back to lat/lon without risking overlaps + double mapLeftLongitude = QLocationUtils::mapLeftLongitude(m_cameraData.center().longitude()); + double mapRightLongitude = QLocationUtils::mapRightLongitude(m_cameraData.center().longitude()); + double leftX = geoToWrappedMapProjection(QGeoCoordinate(0, mapLeftLongitude)).x(); + double rightX = geoToWrappedMapProjection(QGeoCoordinate(0, mapRightLongitude)).x(); + + QList mapRect; + mapRect.push_back(QDoubleVector2D(leftX, 1.0)); + mapRect.push_back(QDoubleVector2D(rightX, 1.0)); + mapRect.push_back(QDoubleVector2D(rightX, 0.0)); + mapRect.push_back(QDoubleVector2D(leftX, 0.0)); + + QList viewportRect; + viewportRect.push_back(bl); + viewportRect.push_back(br); + viewportRect.push_back(tr); + viewportRect.push_back(tl); + + c2t::clip2tri clipper; + clipper.clearClipper(); + clipper.addSubjectPath(QClipperUtils::qListToPath(mapRect), true); + clipper.addClipPolygon(QClipperUtils::qListToPath(viewportRect)); + + Paths res = clipper.execute(c2t::clip2tri::Intersection); + m_visibleRegion.clear(); + if (res.size()) + m_visibleRegion = QClipperUtils::pathToQList(res[0]); // Intersection between two convex quadrilaterals should always be a single polygon + + m_projectableRegion.clear(); + mapRect.clear(); + // The full map rectangle in extended mercator space + mapRect.push_back(QDoubleVector2D(-1.0, 1.0)); + mapRect.push_back(QDoubleVector2D( 2.0, 1.0)); + mapRect.push_back(QDoubleVector2D( 2.0, 0.0)); + mapRect.push_back(QDoubleVector2D(-1.0, 0.0)); + if (m_cameraData.tilt() == 0) { + m_projectableRegion = mapRect; + } else { + QGeoProjectionWebMercator::Plane nearPlane(m_centerNearPlaneMercator, m_viewNormalized); + Line2D nearPlaneXYIntersection = nearPlane.planeXYIntersection(); + double squareHalfSide = qMax(5.0, nearPlaneXYIntersection.m_point.length()); + QDoubleVector2D viewDirectionProjected = -m_viewNormalized.toVector2D().normalized(); + + + QDoubleVector2D tl = nearPlaneXYIntersection.m_point + - squareHalfSide * nearPlaneXYIntersection.m_direction + + 2 * squareHalfSide * viewDirectionProjected; + QDoubleVector2D tr = nearPlaneXYIntersection.m_point + + squareHalfSide * nearPlaneXYIntersection.m_direction + + 2 * squareHalfSide * viewDirectionProjected; + QDoubleVector2D bl = nearPlaneXYIntersection.m_point + - squareHalfSide * nearPlaneXYIntersection.m_direction; + QDoubleVector2D br = nearPlaneXYIntersection.m_point + + squareHalfSide * nearPlaneXYIntersection.m_direction; + + QList projectableRect; + projectableRect.push_back(bl); + projectableRect.push_back(br); + projectableRect.push_back(tr); + projectableRect.push_back(tl); + + + c2t::clip2tri clipperProjectable; + clipperProjectable.clearClipper(); + clipperProjectable.addSubjectPath(QClipperUtils::qListToPath(mapRect), true); + clipperProjectable.addClipPolygon(QClipperUtils::qListToPath(projectableRect)); + + Paths resProjectable = clipperProjectable.execute(c2t::clip2tri::Intersection); + if (resProjectable.size()) + m_projectableRegion = QClipperUtils::pathToQList(resProjectable[0]); // Intersection between two convex quadrilaterals should always be a single polygon + else + m_projectableRegion = viewportRect; + } + + // Compute m_visibleRegionExpanded as a clipped expanded version of m_visibleRegion + QDoubleVector2D centroid; + for (const QDoubleVector2D &v: qAsConst(m_visibleRegion)) + centroid += v; + centroid /= m_visibleRegion.size(); + + m_visibleRegionExpanded.clear(); + for (const QDoubleVector2D &v: qAsConst(m_visibleRegion)) { + const QDoubleVector2D vc = v - centroid; + m_visibleRegionExpanded.push_back(centroid + vc * 1.2); // fixing expansion factor to 1.2 + } + + c2t::clip2tri clipperExpanded; + clipperExpanded.clearClipper(); + clipperExpanded.addSubjectPath(QClipperUtils::qListToPath(m_visibleRegionExpanded), true); + clipperExpanded.addClipPolygon(QClipperUtils::qListToPath(m_projectableRegion)); + Paths resVisibleExpanded = clipperExpanded.execute(c2t::clip2tri::Intersection); + if (resVisibleExpanded.size()) + m_visibleRegionExpanded = QClipperUtils::pathToQList(resVisibleExpanded[0]); // Intersection between two convex quadrilaterals should always be a single polygon + else + m_visibleRegionExpanded = m_visibleRegion; +} + +QGeoCameraData QGeoProjectionWebMercator::cameraData() const +{ + return m_cameraData; +} + +/* + * + * Line implementation + * + */ + +QGeoProjectionWebMercator::Line2D::Line2D() +{ + +} + +QGeoProjectionWebMercator::Line2D::Line2D(const QDoubleVector2D &linePoint, const QDoubleVector2D &lineDirection) + : m_point(linePoint), m_direction(lineDirection.normalized()) +{ + +} + +bool QGeoProjectionWebMercator::Line2D::isValid() const +{ + return (m_direction.length() > 0.5); +} + +/* + * + * Plane implementation + * + */ + +QGeoProjectionWebMercator::Plane::Plane() +{ + +} + +QGeoProjectionWebMercator::Plane::Plane(const QDoubleVector3D &planePoint, const QDoubleVector3D &planeNormal) + : m_point(planePoint), m_normal(planeNormal.normalized()) { } + +QDoubleVector3D QGeoProjectionWebMercator::Plane::lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection) const +{ + double s; + return lineIntersection(linePoint, lineDirection, s); +} + +QDoubleVector3D QGeoProjectionWebMercator::Plane::lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection, double &s) const +{ + QDoubleVector3D w = linePoint - m_point; + // s = -n.dot(w) / n.dot(u). p = p0 + su; u is lineDirection + s = QDoubleVector3D::dotProduct(-m_normal, w) / QDoubleVector3D::dotProduct(m_normal, lineDirection); + return linePoint + lineDirection * s; +} + +QGeoProjectionWebMercator::Line2D QGeoProjectionWebMercator::Plane::planeXYIntersection() const +{ + // cross product of the two normals for the line direction + QDoubleVector3D lineDirection = QDoubleVector3D::crossProduct(m_normal, xyNormal); + lineDirection.setZ(0.0); + lineDirection.normalize(); + + // cross product of the line direction and the plane normal to find the direction on the plane + // intersecting the xy plane + QDoubleVector3D directionToXY = QDoubleVector3D::crossProduct(m_normal, lineDirection); + QDoubleVector3D p = xyPlane.lineIntersection(m_point, directionToXY); + return Line2D(p.toVector2D(), lineDirection.toVector2D()); +} + +bool QGeoProjectionWebMercator::Plane::isValid() const +{ + return (m_normal.length() > 0.5); +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeoprojection_p.h b/src/location/maps/qgeoprojection_p.h new file mode 100644 index 0000000..b0fffa2 --- /dev/null +++ b/src/location/maps/qgeoprojection_p.h @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPROJECTION_H +#define QGEOPROJECTION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QGeoProjection +{ +public: + enum ProjectionGroup { + ProjectionOther, + ProjectionCylindrical, + ProjectionPseudocylindrical, + ProjectionAzimuthal, + ProjectionPseudoazimuthal, + ProjectionConic, + ProjectionPseudoconic + //Polyhedral + //Retroazimuthal + }; + + enum Datum { + DatumUnknown, + DatumWGS84, + DatumSphere + }; + + enum ProjectionType { + ProjectionUnknown, + ProjectionGeneralPerspective, + ProjectionWebMercator + }; + + QGeoProjection(); + virtual ~QGeoProjection(); + + virtual void setViewportSize(const QSize &size) = 0; + virtual void setCameraData(const QGeoCameraData &cameraData, bool force = true) = 0; + virtual QGeoCameraData cameraData() const = 0; + + // returns the minimum zoom at the current viewport size + virtual double minimumZoom() const = 0; + virtual double maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const = 0; + + virtual QGeoCoordinate itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport = true) const = 0; + virtual QDoubleVector2D coordinateToItemPosition(const QGeoCoordinate &coordinate, bool clipToViewport = true) const = 0; + + virtual ProjectionGroup projectionGroup() const = 0; + virtual Datum datum() const = 0; + virtual ProjectionType projectionType() const = 0; + + // Returns the new map center after anchoring coordinate to anchorPoint on the screen + virtual QGeoCoordinate anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint) const; + + virtual QGeoShape visibleRegion() const; + virtual bool setBearing(qreal bearing, const QGeoCoordinate &coordinate); +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoProjectionWebMercator : public QGeoProjection +{ +public: + QGeoProjectionWebMercator(); + ~QGeoProjectionWebMercator(); + + // From QGeoProjection + double minimumZoom() const override; + double maximumCenterLatitudeAtZoom(const QGeoCameraData &cameraData) const override; + + void setViewportSize(const QSize &size) override; + void setCameraData(const QGeoCameraData &cameraData, bool force = true) override; + QGeoCameraData cameraData() const override; + + QGeoCoordinate itemPositionToCoordinate(const QDoubleVector2D &pos, bool clipToViewport = true) const override; + QDoubleVector2D coordinateToItemPosition(const QGeoCoordinate &coordinate, bool clipToViewport = true) const override; + + QGeoProjection::ProjectionGroup projectionGroup() const override; + QGeoProjection::Datum datum() const override; + QGeoProjection::ProjectionType projectionType() const override; + + QGeoCoordinate anchorCoordinateToPoint(const QGeoCoordinate &coordinate, const QPointF &anchorPoint) const override; + bool setBearing(qreal bearing, const QGeoCoordinate &coordinate) override; + + QGeoShape visibleRegion() const override; + + // Specific to QGeoProjectionWebMercator + double mapWidth() const; // The size of the underlying map, at the current zoom level. + double mapHeight() const; + + QDoubleVector2D geoToMapProjection(const QGeoCoordinate &coordinate) const; + QGeoCoordinate mapProjectionToGeo(const QDoubleVector2D &projection) const; + + QDoubleVector2D wrapMapProjection(const QDoubleVector2D &projection) const; + QDoubleVector2D unwrapMapProjection(const QDoubleVector2D &wrappedProjection) const; + + QDoubleVector2D wrappedMapProjectionToItemPosition(const QDoubleVector2D &wrappedProjection) const; + QDoubleVector2D itemPositionToWrappedMapProjection(const QDoubleVector2D &itemPosition) const; + + QDoubleVector2D geoToWrappedMapProjection(const QGeoCoordinate &coordinate) const; + QGeoCoordinate wrappedMapProjectionToGeo(const QDoubleVector2D &wrappedProjection) const; + QMatrix4x4 quickItemTransformation(const QGeoCoordinate &coordinate, const QPointF &anchorPoint, qreal zoomLevel) const; + + bool isProjectable(const QDoubleVector2D &wrappedProjection) const; + QList visibleGeometry() const; + QList visibleGeometryExpanded() const; + QList projectableGeometry() const; + + inline QDoubleVector2D viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition) const; + inline QDoubleVector2D viewportToWrappedMapProjection(const QDoubleVector2D &itemPosition, double &s) const; + +private: + void setupCamera(); + void updateVisibleRegion(); + +public: + struct Line2D + { + Line2D(); + Line2D(const QDoubleVector2D &linePoint, const QDoubleVector2D &lineDirection); + + bool isValid() const; + + QDoubleVector2D m_point; + QDoubleVector2D m_direction; + }; + + struct Plane + { + Plane(); + Plane(const QDoubleVector3D &planePoint, const QDoubleVector3D &planeNormal); + + QDoubleVector3D lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection) const; + inline QDoubleVector3D lineIntersection(const QDoubleVector3D &linePoint, const QDoubleVector3D &lineDirection, double &s) const; + Line2D planeXYIntersection() const; + bool isValid() const; + + QDoubleVector3D m_point; + QDoubleVector3D m_normal; + }; + +private: + QGeoCameraData m_cameraData; + double m_mapEdgeSize; + double m_minimumZoom; + // mercator to camera transform for coordinates (not tiles!) + double m_cameraCenterXMercator; + double m_cameraCenterYMercator; + + // cameraToScreen transform + double m_viewportWidth; // in pixels + double m_viewportHeight; // in pixels + double m_1_viewportWidth; + double m_1_viewportHeight; + + QDoubleMatrix4x4 m_transformation; + QDoubleMatrix4x4 m_quickItemTransformation; + QDoubleVector3D m_eye; + QDoubleVector3D m_up; + QDoubleVector3D m_center; + QDoubleVector3D m_view; + QDoubleVector3D m_viewNormalized; + QDoubleVector3D m_side; + QDoubleVector3D m_centerNearPlane; + double m_sideLength; // map edge size at integer zoom level + double m_aperture; + double m_nearPlane; + double m_farPlane; + double m_halfWidth; + double m_halfHeight; + double m_minimumUnprojectableY; + double m_verticalEstateToSkip; + + // For the clipping region + QDoubleVector3D m_centerMercator; + QDoubleVector3D m_eyeMercator; + QDoubleVector3D m_viewMercator; + QDoubleVector3D m_upMercator; + QDoubleVector3D m_sideMercator; + QDoubleVector3D m_centerNearPlaneMercator; + double m_nearPlaneMercator; + Line2D m_nearPlaneMapIntersection; + + QList m_visibleRegion; + QList m_visibleRegionExpanded; + QList m_projectableRegion; + bool m_visibleRegionDirty; + + Q_DISABLE_COPY(QGeoProjectionWebMercator) +}; + +QT_END_NAMESPACE + +#endif // QGEOPROJECTION_H diff --git a/src/location/maps/qgeoroute.cpp b/src/location/maps/qgeoroute.cpp new file mode 100644 index 0000000..8d62dce --- /dev/null +++ b/src/location/maps/qgeoroute.cpp @@ -0,0 +1,568 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroute.h" +#include "qgeoroute_p.h" + +#include "qgeorectangle.h" +#include "qgeoroutesegment.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +template<> +QGeoRoutePrivate *QExplicitlySharedDataPointer::clone() +{ + return d->clone(); +} + +/*! + \class QGeoRoute + \inmodule QtLocation + \ingroup QtLocation-routing + \since 5.6 + + \brief The QGeoRoute class represents a route between two points. + + A QGeoRoute object contains high level information about a route, such + as the length the route, the estimated travel time for the route, + and enough information to render a basic image of the route on a map. + + The QGeoRoute object also contains a list of QGeoRouteSegment objecs which + describe subsections of the route in greater detail. + + Routing information is normally requested using + QGeoRoutingManager::calculateRoute(), which returns a QGeoRouteReply + instance. If the operation is completed successfully the routing + information can be accessed with QGeoRouteReply::routes() + + \sa QGeoRoutingManager +*/ + +/*! + Constructs a route object. +*/ +QGeoRoute::QGeoRoute() + : d_ptr(new QGeoRoutePrivateDefault()) {} + +/*! + Constructs a route object using \a dd as private implementation. +*/ +QGeoRoute::QGeoRoute(const QExplicitlySharedDataPointer &dd): d_ptr(dd) +{ +} + +/*! + Returns the private implementation. +*/ +QExplicitlySharedDataPointer &QGeoRoute::d() +{ + return d_ptr; +} + +/*! + Constructs a route object from the contents of \a other. +*/ +QGeoRoute::QGeoRoute(const QGeoRoute &other) + : d_ptr(other.d_ptr) {} + +/*! + Destroys this route object. +*/ +QGeoRoute::~QGeoRoute() +{ +} + +/*! + Assigns the contents of \a other to this route and returns a reference to + this route. +*/ +QGeoRoute &QGeoRoute::operator= (const QGeoRoute & other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns whether this route and \a other are equal. +*/ +bool QGeoRoute::operator ==(const QGeoRoute &other) const +{ + return ( (d_ptr.constData() == other.d_ptr.constData()) + || (*d_ptr) == (*other.d_ptr)); +} + +/*! + Returns whether this route and \a other are not equal. +*/ +bool QGeoRoute::operator !=(const QGeoRoute &other) const +{ + return !(operator==(other)); +} + +/*! + Sets the identifier of this route to \a id. + + Service providers which support the updating of routes commonly assign + identifiers to routes. If this route came from such a service provider changing + the identifier will probably cause route updates to stop working. +*/ +void QGeoRoute::setRouteId(const QString &id) +{ + d_ptr->setId(id); +} + +/*! + Returns the identifier of this route. + + Service providers which support the updating of routes commonly assign + identifiers to routes. If this route did not come from such a service provider + the function will return an empty string. +*/ +QString QGeoRoute::routeId() const +{ + return d_ptr->id(); +} + +/*! + Sets the route request which describes the criteria used in the + calculcation of this route to \a request. +*/ +void QGeoRoute::setRequest(const QGeoRouteRequest &request) +{ + d_ptr->setRequest(request); +} + +/*! + Returns the route request which describes the criteria used in + the calculation of this route. +*/ +QGeoRouteRequest QGeoRoute::request() const +{ + return d_ptr->request(); +} + +/*! + Sets the bounding box which encompasses the entire route to \a bounds. +*/ +void QGeoRoute::setBounds(const QGeoRectangle &bounds) +{ + d_ptr->setBounds(bounds); +} + +/*! + Returns a bounding box which encompasses the entire route. +*/ +QGeoRectangle QGeoRoute::bounds() const +{ + return d_ptr->bounds(); +} + +/*! + Sets the first route segment in the route to \a routeSegment. +*/ +void QGeoRoute::setFirstRouteSegment(const QGeoRouteSegment &routeSegment) +{ + d_ptr->setFirstSegment(routeSegment); +} + +/*! + Returns the first route segment in the route. + + Will return an invalid route segment if there are no route segments + associated with the route. + + The remaining route segments can be accessed sequentially with + QGeoRouteSegment::nextRouteSegment. +*/ +QGeoRouteSegment QGeoRoute::firstRouteSegment() const +{ + return d_ptr->firstSegment(); +} + +/*! + Sets the estimated amount of time it will take to traverse this route, + in seconds, to \a secs. +*/ +void QGeoRoute::setTravelTime(int secs) +{ + d_ptr->setTravelTime(secs); +} + +/*! + Returns the estimated amount of time it will take to traverse this route, + in seconds. +*/ +int QGeoRoute::travelTime() const +{ + return d_ptr->travelTime(); +} + +/*! + Sets the distance covered by this route, in meters, to \a distance. +*/ +void QGeoRoute::setDistance(qreal distance) +{ + d_ptr->setDistance(distance); +} + +/*! + Returns the distance covered by this route, in meters. +*/ +qreal QGeoRoute::distance() const +{ + return d_ptr->distance(); +} + +/*! + Sets the travel mode for this route to \a mode. + + This should be one of the travel modes returned by request().travelModes(). +*/ +void QGeoRoute::setTravelMode(QGeoRouteRequest::TravelMode mode) +{ + d_ptr->setTravelMode(mode); +} + +/*! + Returns the travel mode for the this route. + + This should be one of the travel modes returned by request().travelModes(). +*/ +QGeoRouteRequest::TravelMode QGeoRoute::travelMode() const +{ + return d_ptr->travelMode(); +} + +/*! + Sets the geometric shape of the route to \a path. + + The coordinates in \a path should be listed in the order in which they + would be traversed by someone traveling along this segment of the route. +*/ +void QGeoRoute::setPath(const QList &path) +{ + d_ptr->setPath(path); +} + +/*! + Returns the geometric shape of the route. + + The coordinates should be listed in the order in which they + would be traversed by someone traveling along this segment of the route. +*/ +QList QGeoRoute::path() const +{ + return d_ptr->path(); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoRoutePrivate::QGeoRoutePrivate() +{ + +} + +QGeoRoutePrivate::QGeoRoutePrivate(const QGeoRoutePrivate &other) : QSharedData(other) +{ + +} + +QGeoRoutePrivate::~QGeoRoutePrivate() {} + +bool QGeoRoutePrivate::operator ==(const QGeoRoutePrivate &other) const +{ + return equals(other); +} + +bool QGeoRoutePrivate::equals(const QGeoRoutePrivate &other) const +{ + if (!other.engineName().isEmpty()) // only way to know if other comes from an engine without dynamic_cast + return false; + + // here both routes are of type QGeoRoutePrivateDefault + QGeoRouteSegment s1 = firstSegment(); + QGeoRouteSegment s2 = other.firstSegment(); + + while (true) { + if (s1.isValid() != s2.isValid()) + return false; + if (!s1.isValid()) + break; + if (s1 != s2) + return false; + s1 = s1.nextRouteSegment(); + s2 = s2.nextRouteSegment(); + } + + return ((id() == other.id()) + && (request() == other.request()) + && (bounds() == other.bounds()) + && (travelTime() == other.travelTime()) + && (distance() == other.distance()) + && (travelMode() == other.travelMode()) + && (path() == other.path()) + && (metadata() == other.metadata())); +} + +void QGeoRoutePrivate::setId(const QString &id) +{ + Q_UNUSED(id) +} + +QString QGeoRoutePrivate::id() const +{ + return QString(); +} + +void QGeoRoutePrivate::setRequest(const QGeoRouteRequest &request) +{ + Q_UNUSED(request) +} + +QGeoRouteRequest QGeoRoutePrivate::request() const +{ + return QGeoRouteRequest(); +} + +void QGeoRoutePrivate::setBounds(const QGeoRectangle &bounds) +{ + Q_UNUSED(bounds) +} + +QGeoRectangle QGeoRoutePrivate::bounds() const +{ + return QGeoRectangle(); +} + +void QGeoRoutePrivate::setTravelTime(int travelTime) +{ + Q_UNUSED(travelTime) +} + +int QGeoRoutePrivate::travelTime() const +{ + return 0; +} + +void QGeoRoutePrivate::setDistance(qreal distance) +{ + Q_UNUSED(distance) +} + +qreal QGeoRoutePrivate::distance() const +{ + return 0; +} + +void QGeoRoutePrivate::setTravelMode(QGeoRouteRequest::TravelMode mode) +{ + Q_UNUSED(mode) +} + +QGeoRouteRequest::TravelMode QGeoRoutePrivate::travelMode() const +{ + return QGeoRouteRequest::CarTravel; +} + +void QGeoRoutePrivate::setPath(const QList &path) +{ + Q_UNUSED(path) +} + +QList QGeoRoutePrivate::path() const +{ + return QList(); +} + +void QGeoRoutePrivate::setFirstSegment(const QGeoRouteSegment &firstSegment) +{ + Q_UNUSED(firstSegment) +} + +QGeoRouteSegment QGeoRoutePrivate::firstSegment() const +{ + return QGeoRouteSegment(); +} + +const QGeoRoutePrivate *QGeoRoutePrivate::routePrivateData(const QGeoRoute &route) +{ + return route.d_ptr.data(); +} + +QVariantMap QGeoRoutePrivate::metadata() const +{ + return QVariantMap(); +} + +/******************************************************************************* +*******************************************************************************/ + + +QGeoRoutePrivateDefault::QGeoRoutePrivateDefault() + : m_travelTime(0), + m_distance(0.0), + m_travelMode(QGeoRouteRequest::CarTravel), + m_numSegments(-1) {} + +QGeoRoutePrivateDefault::QGeoRoutePrivateDefault(const QGeoRoutePrivateDefault &other) + : QGeoRoutePrivate(other), + m_id(other.m_id), + m_request(other.m_request), + m_bounds(other.m_bounds), + m_routeSegments(other.m_routeSegments), + m_travelTime(other.m_travelTime), + m_distance(other.m_distance), + m_travelMode(other.m_travelMode), + m_path(other.m_path), + m_firstSegment(other.m_firstSegment), + m_numSegments(other.m_numSegments){} + + +QGeoRoutePrivateDefault::~QGeoRoutePrivateDefault() {} + +QGeoRoutePrivate *QGeoRoutePrivateDefault::clone() +{ + return new QGeoRoutePrivateDefault(*this); +} + +void QGeoRoutePrivateDefault::setId(const QString &id) +{ + m_id = id; +} + +QString QGeoRoutePrivateDefault::id() const +{ + return m_id; +} + +void QGeoRoutePrivateDefault::setRequest(const QGeoRouteRequest &request) +{ + m_request = request; +} + +QGeoRouteRequest QGeoRoutePrivateDefault::request() const +{ + return m_request; +} + +void QGeoRoutePrivateDefault::setBounds(const QGeoRectangle &bounds) +{ + m_bounds = bounds; +} + +QGeoRectangle QGeoRoutePrivateDefault::bounds() const +{ + return m_bounds; +} + +void QGeoRoutePrivateDefault::setTravelTime(int travelTime) +{ + m_travelTime = travelTime; +} + +int QGeoRoutePrivateDefault::travelTime() const +{ + return m_travelTime; +} + +void QGeoRoutePrivateDefault::setDistance(qreal distance) +{ + m_distance = distance; +} + +qreal QGeoRoutePrivateDefault::distance() const +{ + return m_distance; +} + +void QGeoRoutePrivateDefault::setTravelMode(QGeoRouteRequest::TravelMode mode) +{ + m_travelMode = mode; +} + +QGeoRouteRequest::TravelMode QGeoRoutePrivateDefault::travelMode() const +{ + return m_travelMode; +} + +void QGeoRoutePrivateDefault::setPath(const QList &path) +{ + m_path = path; +} + +QList QGeoRoutePrivateDefault::path() const +{ + return m_path; +} + +void QGeoRoutePrivateDefault::setFirstSegment(const QGeoRouteSegment &firstSegment) +{ + m_firstSegment = firstSegment; +} + +QGeoRouteSegment QGeoRoutePrivateDefault::firstSegment() const +{ + return m_firstSegment; +} + +QString QGeoRoutePrivateDefault::engineName() const +{ + return QString(); +} + +int QGeoRoutePrivateDefault::segmentsCount() const +{ + if (m_numSegments >= 0) + return m_numSegments; + + int count = 0; + QGeoRouteSegment segment = m_firstSegment; + while (segment.isValid()) { + ++count; + segment = segment.nextRouteSegment(); + } + m_numSegments = count; + return count; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeoroute.h b/src/location/maps/qgeoroute.h new file mode 100644 index 0000000..ef1f756 --- /dev/null +++ b/src/location/maps/qgeoroute.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTE_H +#define QGEOROUTE_H + +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRectangle; +class QGeoRouteSegment; + +class QGeoRoutePrivate; + +class Q_LOCATION_EXPORT QGeoRoute +{ +public: + QGeoRoute(); + QGeoRoute(const QGeoRoute &other); + ~QGeoRoute(); + + QGeoRoute &operator = (const QGeoRoute &other); + + bool operator == (const QGeoRoute &other) const; + bool operator != (const QGeoRoute &other) const; + + void setRouteId(const QString &id); + QString routeId() const; + + void setRequest(const QGeoRouteRequest &request); + QGeoRouteRequest request() const; + + void setBounds(const QGeoRectangle &bounds); + QGeoRectangle bounds() const; + + void setFirstRouteSegment(const QGeoRouteSegment &routeSegment); + QGeoRouteSegment firstRouteSegment() const; + + void setTravelTime(int secs); + int travelTime() const; + + void setDistance(qreal distance); + qreal distance() const; + + void setTravelMode(QGeoRouteRequest::TravelMode mode); + QGeoRouteRequest::TravelMode travelMode() const; + + void setPath(const QList &path); + QList path() const; + +protected: + QGeoRoute(const QExplicitlySharedDataPointer &dd); + QExplicitlySharedDataPointer &d(); + +private: + QExplicitlySharedDataPointer d_ptr; + friend class QDeclarativeGeoRoute; + friend class QGeoRoutePrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroute_p.h b/src/location/maps/qgeoroute_p.h new file mode 100644 index 0000000..384802e --- /dev/null +++ b/src/location/maps/qgeoroute_p.h @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTE_P_H +#define QGEOROUTE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qgeoroute.h" +#include "qgeorouterequest.h" +#include "qgeorectangle.h" +#include "qgeoroutesegment.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGeoCoordinate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoRoutePrivate : public QSharedData +{ +public: + QGeoRoutePrivate(); + QGeoRoutePrivate(const QGeoRoutePrivate &other); + virtual ~QGeoRoutePrivate(); + virtual QGeoRoutePrivate *clone() = 0; + + bool operator == (const QGeoRoutePrivate &other) const; + + virtual void setId(const QString &id); + virtual QString id() const; + + virtual void setRequest(const QGeoRouteRequest &request); + virtual QGeoRouteRequest request() const; + + virtual void setBounds(const QGeoRectangle &bounds); + virtual QGeoRectangle bounds() const; + + virtual void setTravelTime(int travelTime); + virtual int travelTime() const; + + virtual void setDistance(qreal distance); + virtual qreal distance() const; + + virtual void setTravelMode(QGeoRouteRequest::TravelMode mode); + virtual QGeoRouteRequest::TravelMode travelMode() const; + + virtual void setPath(const QList &path); + virtual QList path() const; + + virtual void setFirstSegment(const QGeoRouteSegment &firstSegment); + virtual QGeoRouteSegment firstSegment() const; + + virtual QVariantMap metadata() const; + + virtual QString engineName() const = 0; + virtual int segmentsCount() const = 0; + + static const QGeoRoutePrivate *routePrivateData(const QGeoRoute &route); + +protected: + virtual bool equals(const QGeoRoutePrivate &other) const; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoRoutePrivateDefault : public QGeoRoutePrivate +{ +public: + QGeoRoutePrivateDefault(); + QGeoRoutePrivateDefault(const QGeoRoutePrivateDefault &other); + ~QGeoRoutePrivateDefault(); + virtual QGeoRoutePrivate *clone() override; + + virtual void setId(const QString &id) override; + virtual QString id() const override; + + virtual void setRequest(const QGeoRouteRequest &request) override; + virtual QGeoRouteRequest request() const override; + + virtual void setBounds(const QGeoRectangle &bounds) override; + virtual QGeoRectangle bounds() const override; + + virtual void setTravelTime(int travelTime) override; + virtual int travelTime() const override; + + virtual void setDistance(qreal distance) override; + virtual qreal distance() const override; + + virtual void setTravelMode(QGeoRouteRequest::TravelMode mode) override; + virtual QGeoRouteRequest::TravelMode travelMode() const override; + + virtual void setPath(const QList &path) override; + virtual QList path() const override; + + virtual void setFirstSegment(const QGeoRouteSegment &firstSegment) override; + virtual QGeoRouteSegment firstSegment() const override; + + virtual QString engineName() const override; + virtual int segmentsCount() const override; + + + QString m_id; + QGeoRouteRequest m_request; + + QGeoRectangle m_bounds; + mutable QList m_routeSegments; + + int m_travelTime; + qreal m_distance; + + QGeoRouteRequest::TravelMode m_travelMode; + + QList m_path; + + QGeoRouteSegment m_firstSegment; + mutable int m_numSegments; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeorouteparser.cpp b/src/location/maps/qgeorouteparser.cpp new file mode 100644 index 0000000..646902e --- /dev/null +++ b/src/location/maps/qgeorouteparser.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeorouteparser_p.h" +#include "qgeorouteparser_p_p.h" +#include "qgeoroutesegment.h" +#include "qgeomaneuver.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* + Private class implementations +*/ + +QGeoRouteParserPrivate::QGeoRouteParserPrivate() : QObjectPrivate() +{ +} + +QGeoRouteParserPrivate::~QGeoRouteParserPrivate() +{ +} + +/* + Public class implementations +*/ + +QGeoRouteParser::~QGeoRouteParser() +{ + +} + +QGeoRouteParser::QGeoRouteParser(QGeoRouteParserPrivate &dd, QObject *parent) : QObject(dd, parent) +{ + +} + +QGeoRouteReply::Error QGeoRouteParser::parseReply(QList &routes, QString &errorString, const QByteArray &reply) const +{ + Q_D(const QGeoRouteParser); + return d->parseReply(routes, errorString, reply); +} + +QUrl QGeoRouteParser::requestUrl(const QGeoRouteRequest &request, const QString &prefix) const +{ + Q_D(const QGeoRouteParser); + return d->requestUrl(request, prefix); +} + +QT_END_NAMESPACE + + diff --git a/src/location/maps/qgeorouteparser_p.h b/src/location/maps/qgeorouteparser_p.h new file mode 100644 index 0000000..33c3a4d --- /dev/null +++ b/src/location/maps/qgeorouteparser_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QOSRMROUTEPARSER_P_H +#define QOSRMROUTEPARSER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteParserPrivate; +class Q_LOCATION_PRIVATE_EXPORT QGeoRouteParser : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoRouteParser) + +public: + virtual ~QGeoRouteParser(); + QGeoRouteReply::Error parseReply(QList &routes, QString &errorString, const QByteArray &reply) const; + QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const; + +protected: + QGeoRouteParser(QGeoRouteParserPrivate &dd, QObject *parent = nullptr); + +private: + Q_DISABLE_COPY(QGeoRouteParser) +}; + +QT_END_NAMESPACE + +#endif // QOSRMROUTEPARSER_P_H diff --git a/src/location/maps/qgeorouteparser_p_p.h b/src/location/maps/qgeorouteparser_p_p.h new file mode 100644 index 0000000..63c773e --- /dev/null +++ b/src/location/maps/qgeorouteparser_p_p.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEPARSER_P_P_H +#define QGEOROUTEPARSER_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteParserPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGeoRouteParser) +public: + QGeoRouteParserPrivate(); + virtual ~QGeoRouteParserPrivate(); + + virtual QGeoRouteReply::Error parseReply(QList &routes, QString &errorString, const QByteArray &reply) const = 0; + virtual QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const = 0; +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTEPARSER_P_P_H diff --git a/src/location/maps/qgeorouteparserosrmv4.cpp b/src/location/maps/qgeorouteparserosrmv4.cpp new file mode 100644 index 0000000..bd36e7f --- /dev/null +++ b/src/location/maps/qgeorouteparserosrmv4.cpp @@ -0,0 +1,404 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeorouteparserosrmv4_p.h" +#include "qgeorouteparser_p_p.h" +#include "qgeoroutesegment.h" +#include "qgeomaneuver.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static QList parsePolyline(const QByteArray &data) +{ + QList path; + + bool parsingLatitude = true; + + int shift = 0; + int value = 0; + + QGeoCoordinate coord(0, 0); + + for (int i = 0; i < data.length(); ++i) { + unsigned char c = data.at(i) - 63; + + value |= (c & 0x1f) << shift; + shift += 5; + + // another chunk + if (c & 0x20) + continue; + + int diff = (value & 1) ? ~(value >> 1) : (value >> 1); + + if (parsingLatitude) { + coord.setLatitude(coord.latitude() + (double)diff/1e6); + } else { + coord.setLongitude(coord.longitude() + (double)diff/1e6); + path.append(coord); + } + + parsingLatitude = !parsingLatitude; + + value = 0; + shift = 0; + } + + return path; +} + +static QGeoManeuver::InstructionDirection osrmInstructionDirection(const QString &instructionCode) +{ + if (instructionCode == QLatin1String("0")) + return QGeoManeuver::NoDirection; + else if (instructionCode == QLatin1String("1")) + return QGeoManeuver::DirectionForward; + else if (instructionCode == QLatin1String("2")) + return QGeoManeuver::DirectionBearRight; + else if (instructionCode == QLatin1String("3")) + return QGeoManeuver::DirectionRight; + else if (instructionCode == QLatin1String("4")) + return QGeoManeuver::DirectionHardRight; + else if (instructionCode == QLatin1String("5")) + return QGeoManeuver::DirectionUTurnLeft; + else if (instructionCode == QLatin1String("6")) + return QGeoManeuver::DirectionHardLeft; + else if (instructionCode == QLatin1String("7")) + return QGeoManeuver::DirectionLeft; + else if (instructionCode == QLatin1String("8")) + return QGeoManeuver::DirectionBearLeft; + else if (instructionCode == QLatin1String("9")) + return QGeoManeuver::NoDirection; + else if (instructionCode == QLatin1String("10")) + return QGeoManeuver::DirectionForward; + else if (instructionCode == QLatin1String("11")) + return QGeoManeuver::NoDirection; + else if (instructionCode == QLatin1String("12")) + return QGeoManeuver::NoDirection; + else if (instructionCode == QLatin1String("13")) + return QGeoManeuver::NoDirection; + else if (instructionCode == QLatin1String("14")) + return QGeoManeuver::NoDirection; + else if (instructionCode == QLatin1String("15")) + return QGeoManeuver::NoDirection; + else + return QGeoManeuver::NoDirection; +} + +static QString osrmInstructionText(const QString &instructionCode, const QString &wayname) +{ + if (instructionCode == QLatin1String("0")) { + return QString(); + } else if (instructionCode == QLatin1String("1")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Go straight."); + else + return QGeoRouteParserOsrmV4::tr("Go straight onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("2")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Turn slightly right."); + else + return QGeoRouteParserOsrmV4::tr("Turn slightly right onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("3")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Turn right."); + else + return QGeoRouteParserOsrmV4::tr("Turn right onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("4")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Make a sharp right."); + else + return QGeoRouteParserOsrmV4::tr("Make a sharp right onto %1.").arg(wayname); + } + else if (instructionCode == QLatin1String("5")) { + return QGeoRouteParserOsrmV4::tr("When it is safe to do so, perform a U-turn."); + } else if (instructionCode == QLatin1String("6")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Make a sharp left."); + else + return QGeoRouteParserOsrmV4::tr("Make a sharp left onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("7")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Turn left."); + else + return QGeoRouteParserOsrmV4::tr("Turn left onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("8")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Turn slightly left."); + else + return QGeoRouteParserOsrmV4::tr("Turn slightly left onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("9")) { + return QGeoRouteParserOsrmV4::tr("Reached waypoint."); + } else if (instructionCode == QLatin1String("10")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Head on."); + else + return QGeoRouteParserOsrmV4::tr("Head onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11")) { + return QGeoRouteParserOsrmV4::tr("Enter the roundabout."); + } else if (instructionCode == QLatin1String("11-1")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the first exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the first exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-2")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the second exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the second exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-3")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the third exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the third exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-4")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the fourth exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the fourth exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-5")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the fifth exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the fifth exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-6")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the sixth exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the sixth exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-7")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the seventh exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the seventh exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-8")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the eighth exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the eighth exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("11-9")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("At the roundabout take the ninth exit."); + else + return QGeoRouteParserOsrmV4::tr("At the roundabout take the ninth exit onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("12")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Leave the roundabout."); + else + return QGeoRouteParserOsrmV4::tr("Leave the roundabout onto %1.").arg(wayname); + } else if (instructionCode == QLatin1String("13")) { + return QGeoRouteParserOsrmV4::tr("Stay on the roundabout."); + } else if (instructionCode == QLatin1String("14")) { + if (wayname.isEmpty()) + return QGeoRouteParserOsrmV4::tr("Start at the end of the street."); + else + return QGeoRouteParserOsrmV4::tr("Start at the end of %1.").arg(wayname); + } else if (instructionCode == QLatin1String("15")) { + return QGeoRouteParserOsrmV4::tr("You have reached your destination."); + } else { + return QGeoRouteParserOsrmV4::tr("Don't know what to say for '%1'").arg(instructionCode); + } +} + +static QGeoRoute constructRoute(const QByteArray &geometry, const QJsonArray &instructions, + const QJsonObject &summary) +{ + QGeoRoute route; + + QList path = parsePolyline(geometry); + + QGeoRouteSegment firstSegment; + int firstPosition = -1; + + int segmentPathLengthCount = 0; + + for (int i = instructions.count() - 1; i >= 0; --i) { + QJsonArray instruction = instructions.at(i).toArray(); + + if (instruction.count() < 8) { + qWarning("Instruction does not contain enough fields."); + continue; + } + + const QString instructionCode = instruction.at(0).toString(); + const QString wayname = instruction.at(1).toString(); + double segmentLength = instruction.at(2).toDouble(); + int position = instruction.at(3).toDouble(); + int time = instruction.at(4).toDouble(); + //const QString segmentLengthString = instruction.at(5).toString(); + //const QString direction = instruction.at(6).toString(); + //double azimuth = instruction.at(7).toDouble(); + + QGeoRouteSegment segment; + segment.setDistance(segmentLength); + + QGeoManeuver maneuver; + maneuver.setDirection(osrmInstructionDirection(instructionCode)); + maneuver.setDistanceToNextInstruction(segmentLength); + maneuver.setInstructionText(osrmInstructionText(instructionCode, wayname)); + maneuver.setPosition(path.at(position)); + maneuver.setTimeToNextInstruction(time); + + segment.setManeuver(maneuver); + + if (firstPosition == -1) + segment.setPath(path.mid(position)); + else + segment.setPath(path.mid(position, firstPosition - position)); + + segmentPathLengthCount += segment.path().length(); + + segment.setTravelTime(time); + + segment.setNextRouteSegment(firstSegment); + + firstSegment = segment; + firstPosition = position; + } + + route.setDistance(summary.value(QStringLiteral("total_distance")).toDouble()); + route.setTravelTime(summary.value(QStringLiteral("total_time")).toDouble()); + route.setFirstRouteSegment(firstSegment); + route.setPath(path); + + return route; +} + +class QGeoRouteParserOsrmV4Private : public QGeoRouteParserPrivate +{ + Q_DECLARE_PUBLIC(QGeoRouteParserOsrmV4) +public: + QGeoRouteParserOsrmV4Private(); + virtual ~QGeoRouteParserOsrmV4Private(); + + QGeoRouteReply::Error parseReply(QList &routes, QString &errorString, const QByteArray &reply) const override; + QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const override; +}; + +QGeoRouteParserOsrmV4Private::QGeoRouteParserOsrmV4Private() : QGeoRouteParserPrivate() +{ +} + +QGeoRouteParserOsrmV4Private::~QGeoRouteParserOsrmV4Private() +{ +} + +QGeoRouteReply::Error QGeoRouteParserOsrmV4Private::parseReply(QList &routes, QString &errorString, const QByteArray &reply) const +{ + // OSRM v4 specs: https://github.com/Project-OSRM/osrm-backend/wiki/Server-API---v4,-old + QJsonDocument document = QJsonDocument::fromJson(reply); + + if (document.isObject()) { + QJsonObject object = document.object(); + + //double version = object.value(QStringLiteral("version")).toDouble(); + int status = object.value(QStringLiteral("status")).toDouble(); + QString statusMessage = object.value(QStringLiteral("status_message")).toString(); + + // status code 0 or 200 are case of success + // status code is 207 if no route was found + // an error occurred when trying to find a route + if (0 != status && 200 != status) { + errorString = statusMessage; + return QGeoRouteReply::UnknownError; + } + + QJsonObject routeSummary = object.value(QStringLiteral("route_summary")).toObject(); + + QByteArray routeGeometry = + object.value(QStringLiteral("route_geometry")).toString().toLatin1(); + + QJsonArray routeInstructions = object.value(QStringLiteral("route_instructions")).toArray(); + + QGeoRoute route = constructRoute(routeGeometry, routeInstructions, routeSummary); + + routes.append(route); + + QJsonArray alternativeSummaries = + object.value(QStringLiteral("alternative_summaries")).toArray(); + QJsonArray alternativeGeometries = + object.value(QStringLiteral("alternative_geometries")).toArray(); + QJsonArray alternativeInstructions = + object.value(QStringLiteral("alternative_instructions")).toArray(); + + if (alternativeSummaries.count() == alternativeGeometries.count() && + alternativeSummaries.count() == alternativeInstructions.count()) { + for (int i = 0; i < alternativeSummaries.count(); ++i) { + route = constructRoute(alternativeGeometries.at(i).toString().toLatin1(), + alternativeInstructions.at(i).toArray(), + alternativeSummaries.at(i).toObject()); + //routes.append(route); + } + } + + return QGeoRouteReply::NoError; + } else { + errorString = QStringLiteral("Couldn't parse json."); + return QGeoRouteReply::ParseError; + } +} + +QUrl QGeoRouteParserOsrmV4Private::requestUrl(const QGeoRouteRequest &request, const QString &prefix) const +{ + QUrl url(prefix); + QUrlQuery query; + + query.addQueryItem(QStringLiteral("instructions"), QStringLiteral("true")); + + foreach (const QGeoCoordinate &c, request.waypoints()) { + query.addQueryItem(QStringLiteral("loc"), QString::number(c.latitude()) + QLatin1Char(',') + + QString::number(c.longitude())); + } + + url.setQuery(query); + return url; +} + +QGeoRouteParserOsrmV4::QGeoRouteParserOsrmV4(QObject *parent) : QGeoRouteParser(*new QGeoRouteParserOsrmV4Private(), parent) +{ +} + +QGeoRouteParserOsrmV4::~QGeoRouteParserOsrmV4() +{ +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeorouteparserosrmv4_p.h b/src/location/maps/qgeorouteparserosrmv4_p.h new file mode 100644 index 0000000..d58c054 --- /dev/null +++ b/src/location/maps/qgeorouteparserosrmv4_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEPARSEROSRMV4_H +#define QGEOROUTEPARSEROSRMV4_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteParserOsrmV4Private; +class Q_LOCATION_PRIVATE_EXPORT QGeoRouteParserOsrmV4 : public QGeoRouteParser +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoRouteParserOsrmV4) + +public: + QGeoRouteParserOsrmV4(QObject *parent = nullptr); + virtual ~QGeoRouteParserOsrmV4(); + +private: + Q_DISABLE_COPY(QGeoRouteParserOsrmV4) +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTEPARSEROSRMV4_H diff --git a/src/location/maps/qgeorouteparserosrmv5.cpp b/src/location/maps/qgeorouteparserosrmv5.cpp new file mode 100644 index 0000000..58299d0 --- /dev/null +++ b/src/location/maps/qgeorouteparserosrmv5.cpp @@ -0,0 +1,1042 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeorouteparserosrmv5_p.h" +#include "qgeorouteparser_p_p.h" +#include "qgeoroutesegment.h" +#include "qgeomaneuver.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static QList decodePolyline(const QString &polylineString) +{ + QList path; + if (polylineString.isEmpty()) + return path; + + QByteArray data = polylineString.toLatin1(); + + bool parsingLatitude = true; + + int shift = 0; + int value = 0; + + QGeoCoordinate coord(0, 0); + + for (int i = 0; i < data.length(); ++i) { + unsigned char c = data.at(i) - 63; + + value |= (c & 0x1f) << shift; + shift += 5; + + // another chunk + if (c & 0x20) + continue; + + int diff = (value & 1) ? ~(value >> 1) : (value >> 1); + + if (parsingLatitude) { + coord.setLatitude(coord.latitude() + (double)diff/1e6); + } else { + coord.setLongitude(coord.longitude() + (double)diff/1e6); + path.append(coord); + } + + parsingLatitude = !parsingLatitude; + + value = 0; + shift = 0; + } + + return path; +} + +static QString cardinalDirection4(QLocationUtils::CardinalDirection direction) +{ + switch (direction) { + case QLocationUtils::CardinalN: + //: Translations exist at https://github.com/Project-OSRM/osrm-text-instructions. + //: Always used in "Head %1 [onto ]" + return QGeoRouteParserOsrmV5::tr("North"); + case QLocationUtils::CardinalE: + return QGeoRouteParserOsrmV5::tr("East"); + case QLocationUtils::CardinalS: + return QGeoRouteParserOsrmV5::tr("South"); + case QLocationUtils::CardinalW: + return QGeoRouteParserOsrmV5::tr("West"); + default: + return QString(); + } +} + +static QString exitOrdinal(int exit) +{ + static QList ordinals; + + if (!ordinals.size()) { + ordinals.append(QLatin1String("")); + //: always used in " and take the %1 exit [onto ]" + ordinals.append(QGeoRouteParserOsrmV5::tr("first", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("second", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("third", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("fourth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("fifth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("sixth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("seventh", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("eighth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("ninth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("tenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("eleventh", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("twelfth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("thirteenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("fourteenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("fifteenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("sixteenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("seventeenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("eighteenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("nineteenth", "roundabout exit")); + ordinals.append(QGeoRouteParserOsrmV5::tr("twentieth", "roundabout exit")); + }; + + if (exit < 1 || exit > ordinals.size()) + return QString(); + return ordinals[exit]; +} + +static QString exitDirection(int exit, const QString &wayName) +{ + /*: Always appended to one of the following strings: + - "Enter the roundabout" + - "Enter the rotary" + - "Enter the rotary " + */ + static QString directionExit = QGeoRouteParserOsrmV5::tr(" and take the %1 exit"); + static QString directionExitOnto = QGeoRouteParserOsrmV5::tr(" and take the %1 exit onto %2"); + + if (exit < 1 || exit > 20) + return QString(); + if (wayName.isEmpty()) + return directionExit.arg(exitOrdinal(exit)); + else + return directionExitOnto.arg(exitOrdinal(exit), wayName); +} + +static QString instructionArrive(QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionForward: + return QGeoRouteParserOsrmV5::tr("You have arrived at your destination, straight ahead"); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionHardLeft: + case QGeoManeuver::DirectionLeft: + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + return QGeoRouteParserOsrmV5::tr("You have arrived at your destination, on the left"); + case QGeoManeuver::DirectionUTurnRight: + case QGeoManeuver::DirectionHardRight: + case QGeoManeuver::DirectionRight: + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + return QGeoRouteParserOsrmV5::tr("You have arrived at your destination, on the right"); + default: + return QGeoRouteParserOsrmV5::tr("You have arrived at your destination"); + } +} + +static QString instructionContinue(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue straight"); + else + return QGeoRouteParserOsrmV5::tr("Continue straight on %1").arg(wayName); + case QGeoManeuver::DirectionHardLeft: + case QGeoManeuver::DirectionLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue left"); + else + return QGeoRouteParserOsrmV5::tr("Continue left onto %1").arg(wayName); + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue slightly left"); + else + return QGeoRouteParserOsrmV5::tr("Continue slightly left on %1").arg(wayName); + case QGeoManeuver::DirectionHardRight: + case QGeoManeuver::DirectionRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue right"); + else + return QGeoRouteParserOsrmV5::tr("Continue right onto %1").arg(wayName); + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue slightly right"); + else + return QGeoRouteParserOsrmV5::tr("Continue slightly right on %1").arg(wayName); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionUTurnRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Make a U-turn"); + else + return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName); + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue"); + else + return QGeoRouteParserOsrmV5::tr("Continue on %1").arg(wayName); + } +} + +static QString instructionDepart(const QJsonObject &maneuver, const QString &wayName) +{ + double bearing = maneuver.value(QLatin1String("bearing_after")).toDouble(-1.0); + if (bearing >= 0.0) { + if (wayName.isEmpty()) + //: %1 is "North", "South", "East" or "West" + return QGeoRouteParserOsrmV5::tr("Head %1").arg(cardinalDirection4(QLocationUtils::azimuthToCardinalDirection4(bearing))); + else + return QGeoRouteParserOsrmV5::tr("Head %1 onto %2").arg(cardinalDirection4(QLocationUtils::azimuthToCardinalDirection4(bearing)), wayName); + } else { + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Depart"); + else + return QGeoRouteParserOsrmV5::tr("Depart onto %1").arg(wayName); + } +} + +static QString instructionEndOfRoad(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionHardLeft: + case QGeoManeuver::DirectionLeft: + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the end of the road, turn left"); + else + return QGeoRouteParserOsrmV5::tr("At the end of the road, turn left onto %1").arg(wayName); + case QGeoManeuver::DirectionHardRight: + case QGeoManeuver::DirectionRight: + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the end of the road, turn right"); + else + return QGeoRouteParserOsrmV5::tr("At the end of the road, turn right onto %1").arg(wayName); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionUTurnRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the end of the road, make a U-turn"); + else + return QGeoRouteParserOsrmV5::tr("At the end of the road, make a U-turn onto %1").arg(wayName); + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the end of the road, continue straight"); + else + return QGeoRouteParserOsrmV5::tr("At the end of the road, continue straight onto %1").arg(wayName); + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the end of the road, continue"); + else + return QGeoRouteParserOsrmV5::tr("At the end of the road, continue onto %1").arg(wayName); + } +} + +static QString instructionFerry(const QString &wayName) +{ + QString instruction = QGeoRouteParserOsrmV5::tr("Take the ferry"); + if (!wayName.isEmpty()) + instruction += QLatin1String(" [") + wayName + QLatin1Char(']'); + + return instruction; +} + +static QString instructionFork(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionHardLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp left"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp left onto %1").arg(wayName); + case QGeoManeuver::DirectionLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, turn left"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, turn left onto %1").arg(wayName); + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, keep left"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, keep left onto %1").arg(wayName); + case QGeoManeuver::DirectionHardRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp right"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, take a sharp right onto %1").arg(wayName); + case QGeoManeuver::DirectionRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, turn right"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, turn right onto %1").arg(wayName); + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, keep right"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, keep right onto %1").arg(wayName); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionUTurnRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Make a U-turn"); + else + return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName); + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, continue straight ahead"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, continue straight ahead onto %1").arg(wayName); + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the fork, continue"); + else + return QGeoRouteParserOsrmV5::tr("At the fork, continue onto %1").arg(wayName); + } +} + +static QString instructionMerge(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionHardLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge sharply left"); + else + return QGeoRouteParserOsrmV5::tr("Merge sharply left onto %1").arg(wayName); + case QGeoManeuver::DirectionLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge left"); + else + return QGeoRouteParserOsrmV5::tr("Merge left onto %1").arg(wayName); + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge slightly left"); + else + return QGeoRouteParserOsrmV5::tr("Merge slightly left on %1").arg(wayName); + case QGeoManeuver::DirectionUTurnRight: + case QGeoManeuver::DirectionHardRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge sharply right"); + else + return QGeoRouteParserOsrmV5::tr("Merge sharply right onto %1").arg(wayName); + case QGeoManeuver::DirectionRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge right"); + else + return QGeoRouteParserOsrmV5::tr("Merge right onto %1").arg(wayName); + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge slightly right"); + else + return QGeoRouteParserOsrmV5::tr("Merge slightly right on %1").arg(wayName); + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge straight"); + else + return QGeoRouteParserOsrmV5::tr("Merge straight on %1").arg(wayName); + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Merge"); + else + return QGeoRouteParserOsrmV5::tr("Merge onto %1").arg(wayName); + } +} + +static QString instructionNewName(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionHardLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Take a sharp left"); + else + return QGeoRouteParserOsrmV5::tr("Take a sharp left onto %1").arg(wayName); + case QGeoManeuver::DirectionLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Turn left"); + else + return QGeoRouteParserOsrmV5::tr("Turn left onto %1").arg(wayName); + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue slightly left"); + else + return QGeoRouteParserOsrmV5::tr("Continue slightly left onto %1").arg(wayName); + case QGeoManeuver::DirectionHardRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Take a sharp right"); + else + return QGeoRouteParserOsrmV5::tr("Take a sharp right onto %1").arg(wayName); + case QGeoManeuver::DirectionRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Turn right"); + else + return QGeoRouteParserOsrmV5::tr("Turn right onto %1").arg(wayName); + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue slightly right"); + else + return QGeoRouteParserOsrmV5::tr("Continue slightly right onto %1").arg(wayName); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionUTurnRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Make a U-turn"); + else + return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName); + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue straight"); + else + return QGeoRouteParserOsrmV5::tr("Continue straight onto %1").arg(wayName); + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue"); + else + return QGeoRouteParserOsrmV5::tr("Continue onto %1").arg(wayName); + } +} + +static QString instructionNotification(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionHardLeft: + case QGeoManeuver::DirectionLeft: + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue on the left"); + else + return QGeoRouteParserOsrmV5::tr("Continue on the left on %1").arg(wayName); + case QGeoManeuver::DirectionUTurnRight: + case QGeoManeuver::DirectionHardRight: + case QGeoManeuver::DirectionRight: + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue on the right"); + else + return QGeoRouteParserOsrmV5::tr("Continue on the right on %1").arg(wayName); + case QGeoManeuver::DirectionForward: + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue"); + else + return QGeoRouteParserOsrmV5::tr("Continue on %1").arg(wayName); + } +} + +static QString instructionOffRamp(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionHardLeft: + case QGeoManeuver::DirectionLeft: + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Take the ramp on the left"); + else + return QGeoRouteParserOsrmV5::tr("Take the ramp on the left onto %1").arg(wayName); + case QGeoManeuver::DirectionUTurnRight: + case QGeoManeuver::DirectionHardRight: + case QGeoManeuver::DirectionRight: + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Take the ramp on the right"); + else + return QGeoRouteParserOsrmV5::tr("Take the ramp on the right onto %1").arg(wayName); + case QGeoManeuver::DirectionForward: + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Take the ramp"); + else + return QGeoRouteParserOsrmV5::tr("Take the ramp onto %1").arg(wayName); + } +} + +static QString instructionOnRamp(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + return instructionOffRamp(wayName, direction); +} + +static QString instructionPushingBike(const QString &wayName) +{ + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Get off the bike and push"); + else + return QGeoRouteParserOsrmV5::tr("Get off the bike and push onto %1").arg(wayName); +} + +static QString instructionRotary(const QJsonObject &step, const QJsonObject &maneuver, const QString &wayName) +{ + QString instruction; + QString rotaryName = step.value(QLatin1String("rotary_name")).toString(); + //QString modifier = maneuver.value(QLatin1String("modifier")).toString(); // Apparently not used for rotaries + int exit = maneuver.value(QLatin1String("exit")).toInt(0); + + //: This string will be prepended to " and take the exit [onto ] + instruction += QGeoRouteParserOsrmV5::tr("Enter the rotary"); + if (!rotaryName.isEmpty()) + instruction += QLatin1Char(' ') + rotaryName; + instruction += exitDirection(exit, wayName); + return instruction; +} + +static QString instructionRoundabout(const QJsonObject &maneuver, const QString &wayName) +{ + QString instruction; + //QString modifier = maneuver.value(QLatin1String("modifier")).toString(); // Apparently not used for rotaries + int exit = maneuver.value(QLatin1String("exit")).toInt(0); + + //: This string will be prepended to " and take the exit [onto ] + instruction += QGeoRouteParserOsrmV5::tr("Enter the roundabout"); + instruction += exitDirection(exit, wayName); + return instruction; +} + +static QString instructionRoundaboutTurn(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the roundabout, continue straight"); + else + return QGeoRouteParserOsrmV5::tr("At the roundabout, continue straight on %1").arg(wayName); + case QGeoManeuver::DirectionHardLeft: + case QGeoManeuver::DirectionLeft: + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the roundabout, turn left"); + else + return QGeoRouteParserOsrmV5::tr("At the roundabout, turn left onto %1").arg(wayName); + case QGeoManeuver::DirectionHardRight: + case QGeoManeuver::DirectionRight: + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the roundabout, turn right"); + else + return QGeoRouteParserOsrmV5::tr("At the roundabout, turn right onto %1").arg(wayName); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionUTurnRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the roundabout, turn around"); + else + return QGeoRouteParserOsrmV5::tr("At the roundabout, turn around onto %1").arg(wayName); + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("At the roundabout, continue"); + else + return QGeoRouteParserOsrmV5::tr("At the roundabout, continue onto %1").arg(wayName); + } +} + +static QString instructionTrain(const QString &wayName) +{ + return wayName.isEmpty() + ? QGeoRouteParserOsrmV5::tr("Take the train") + : QGeoRouteParserOsrmV5::tr("Take the train [%1]").arg(wayName); +} + +static QString instructionTurn(const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + switch (direction) { + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Go straight"); + else + return QGeoRouteParserOsrmV5::tr("Go straight onto %1").arg(wayName); + case QGeoManeuver::DirectionHardLeft: + case QGeoManeuver::DirectionLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Turn left"); + else + return QGeoRouteParserOsrmV5::tr("Turn left onto %1").arg(wayName); + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Turn slightly left"); + else + return QGeoRouteParserOsrmV5::tr("Turn slightly left onto %1").arg(wayName); + case QGeoManeuver::DirectionHardRight: + case QGeoManeuver::DirectionRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Turn right"); + else + return QGeoRouteParserOsrmV5::tr("Turn right onto %1").arg(wayName); + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Turn slightly right"); + else + return QGeoRouteParserOsrmV5::tr("Turn slightly right onto %1").arg(wayName); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionUTurnRight: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Make a U-turn"); + else + return QGeoRouteParserOsrmV5::tr("Make a U-turn onto %1").arg(wayName); + default: + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Turn"); + else + return QGeoRouteParserOsrmV5::tr("Turn onto %1").arg(wayName); + } +} + +static QString instructionUseLane(const QJsonObject &maneuver, const QString &wayName, QGeoManeuver::InstructionDirection direction) +{ + QString laneTypes = maneuver.value(QLatin1String("laneTypes")).toString(); + QString laneInstruction; + if (laneTypes == QLatin1String("xo") || laneTypes == QLatin1String("xoo") || laneTypes == QLatin1String("xxo")) + //: "and [onto ] will be appended to this string. E.g., "Keep right and make a sharp left" + laneInstruction = QLatin1String("Keep right"); + else if (laneTypes == QLatin1String("ox") || laneTypes == QLatin1String("oox") || laneTypes == QLatin1String("oxx")) + laneInstruction = QLatin1String("Keep left"); + else if (laneTypes == QLatin1String("xox")) + laneInstruction = QLatin1String("Use the middle lane"); + else if (laneTypes == QLatin1String("oxo")) + laneInstruction = QLatin1String("Use the left or the right lane"); + + if (laneInstruction.isEmpty()) { + if (wayName.isEmpty()) + return QGeoRouteParserOsrmV5::tr("Continue straight"); + else + return QGeoRouteParserOsrmV5::tr("Continue straight onto %1").arg(wayName); + } + + switch (direction) { + case QGeoManeuver::DirectionForward: + if (wayName.isEmpty()) + //: This string will be prepended with lane instructions. E.g., "Use the left or the right lane and continue straight" + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and continue straight"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and continue straight onto %1").arg(wayName); + case QGeoManeuver::DirectionHardLeft: + if (wayName.isEmpty()) + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp left"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp left onto %1").arg(wayName); + case QGeoManeuver::DirectionLeft: + if (wayName.isEmpty()) + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn left"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn left onto %1").arg(wayName); + case QGeoManeuver::DirectionLightLeft: + case QGeoManeuver::DirectionBearLeft: + if (wayName.isEmpty()) + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight left"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight left onto %1").arg(wayName); + case QGeoManeuver::DirectionHardRight: + if (wayName.isEmpty()) + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp right"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a sharp right onto %1").arg(wayName); + case QGeoManeuver::DirectionRight: + if (wayName.isEmpty()) + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn right"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and turn right onto %1").arg(wayName); + case QGeoManeuver::DirectionLightRight: + case QGeoManeuver::DirectionBearRight: + if (wayName.isEmpty()) + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight right"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a slight right onto %1").arg(wayName); + case QGeoManeuver::DirectionUTurnLeft: + case QGeoManeuver::DirectionUTurnRight: + if (wayName.isEmpty()) + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a U-turn"); + else + return laneInstruction + QGeoRouteParserOsrmV5::tr(" and make a U-turn onto %1").arg(wayName); + default: + return laneInstruction; + } +} + +static QString instructionText(const QJsonObject &step, const QJsonObject &maneuver, QGeoManeuver::InstructionDirection direction) { + QString modifier; + if (maneuver.value(QLatin1String("modifier")).isString()) + modifier = maneuver.value(QLatin1String("modifier")).toString(); + QString maneuverType; + if (maneuver.value(QLatin1String("type")).isString()) + maneuverType = maneuver.value(QLatin1String("type")).toString(); + QString wayName = QLatin1String("unknown street"); + if (step.value(QLatin1String("name")).isString()) + wayName = step.value(QLatin1String("name")).toString(); + + + if (maneuverType == QLatin1String("arrive")) + return instructionArrive(direction); + else if (maneuverType == QLatin1String("continue")) + return instructionContinue(wayName, direction); + else if (maneuverType == QLatin1String("depart")) + return instructionDepart(maneuver, wayName); + else if (maneuverType == QLatin1String("end of road")) + return instructionEndOfRoad(wayName, direction); + else if (maneuverType == QLatin1String("ferry")) + return instructionFerry(wayName); + else if (maneuverType == QLatin1String("fork")) + return instructionFork(wayName, direction); + else if (maneuverType == QLatin1String("merge")) + return instructionMerge(wayName, direction); + else if (maneuverType == QLatin1String("new name")) + return instructionNewName(wayName, direction); + else if (maneuverType == QLatin1String("notification")) + return instructionNotification(wayName, direction); + else if (maneuverType == QLatin1String("off ramp")) + return instructionOffRamp(wayName, direction); + else if (maneuverType == QLatin1String("on ramp")) + return instructionOnRamp(wayName, direction); + else if (maneuverType == QLatin1String("pushing bike")) + return instructionPushingBike(wayName); + else if (maneuverType == QLatin1String("rotary")) + return instructionRotary(step, maneuver, wayName); + else if (maneuverType == QLatin1String("roundabout")) + return instructionRoundabout(maneuver, wayName); + else if (maneuverType == QLatin1String("roundabout turn")) + return instructionRoundaboutTurn(wayName, direction); + else if (maneuverType == QLatin1String("train")) + return instructionTrain(wayName); + else if (maneuverType == QLatin1String("turn")) + return instructionTurn(wayName, direction); + else if (maneuverType == QLatin1String("use lane")) + return instructionUseLane(maneuver, wayName, direction); + else + return maneuverType + QLatin1String(" to/onto ") + wayName; +} + +static QGeoManeuver::InstructionDirection instructionDirection(const QJsonObject &maneuver) +{ + QString modifier; + if (maneuver.value(QLatin1String("modifier")).isString()) + modifier = maneuver.value(QLatin1String("modifier")).toString(); + + if (modifier.isEmpty()) + return QGeoManeuver::NoDirection; + else if (modifier == QLatin1String("straight")) + return QGeoManeuver::DirectionForward; + else if (modifier == QLatin1String("right")) + return QGeoManeuver::DirectionRight; + else if (modifier == QLatin1String("sharp right")) + return QGeoManeuver::DirectionHardRight; + else if (modifier == QLatin1String("slight right")) + return QGeoManeuver::DirectionLightRight; + else if (modifier == QLatin1String("uturn")) + return QGeoManeuver::DirectionUTurnLeft; // This should rather be country-specific. In UK, f.ex. one should rather UTurn Right + else if (modifier == QLatin1String("left")) + return QGeoManeuver::DirectionLeft; + else if (modifier == QLatin1String("sharp left")) + return QGeoManeuver::DirectionHardLeft; + else if (modifier == QLatin1String("slight left")) + return QGeoManeuver::DirectionLightLeft; + else + return QGeoManeuver::NoDirection; +} + +class QGeoRouteParserOsrmV5Private : public QGeoRouteParserPrivate +{ + Q_DECLARE_PUBLIC(QGeoRouteParserOsrmV5) +public: + QGeoRouteParserOsrmV5Private(); + virtual ~QGeoRouteParserOsrmV5Private(); + + QGeoRouteSegment parseStep(const QJsonObject &step, int legIndex, int stepIndex) const; + + // QGeoRouteParserPrivate + + QGeoRouteReply::Error parseReply(QList &routes, QString &errorString, const QByteArray &reply) const override; + QUrl requestUrl(const QGeoRouteRequest &request, const QString &prefix) const override; + + QVariantMap m_vendorParams; + const QGeoRouteParserOsrmV5Extension *m_extension = nullptr; +}; + +QGeoRouteParserOsrmV5Private::QGeoRouteParserOsrmV5Private() + : QGeoRouteParserPrivate() +{ +} + +QGeoRouteParserOsrmV5Private::~QGeoRouteParserOsrmV5Private() +{ + delete m_extension; +} + +QGeoRouteSegment QGeoRouteParserOsrmV5Private::parseStep(const QJsonObject &step, int legIndex, int stepIndex) const { + // OSRM Instructions documentation: https://github.com/Project-OSRM/osrm-text-instructions + // This goes on top of OSRM: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md + // Mapbox however, includes this in the reply, under "instruction". + QGeoRouteSegment segment; + if (!step.value(QLatin1String("maneuver")).isObject()) + return segment; + QJsonObject maneuver = step.value(QLatin1String("maneuver")).toObject(); + if (!step.value(QLatin1String("duration")).isDouble()) + return segment; + if (!step.value(QLatin1String("distance")).isDouble()) + return segment; + if (!step.value(QLatin1String("intersections")).isArray()) + return segment; + if (!maneuver.value(QLatin1String("location")).isArray()) + return segment; + + double time = step.value(QLatin1String("duration")).toDouble(); + double distance = step.value(QLatin1String("distance")).toDouble(); + + QJsonArray position = maneuver.value(QLatin1String("location")).toArray(); + if (position.isEmpty()) + return segment; + double latitude = position[1].toDouble(); + double longitude = position[0].toDouble(); + QGeoCoordinate coord(latitude, longitude); + + QString geometry = step.value(QLatin1String("geometry")).toString(); + QList path = decodePolyline(geometry); + + QGeoManeuver::InstructionDirection maneuverInstructionDirection = instructionDirection(maneuver); + + QString maneuverInstructionText = instructionText(step, maneuver, maneuverInstructionDirection); + + QGeoManeuver geoManeuver; + geoManeuver.setDirection(maneuverInstructionDirection); + geoManeuver.setDistanceToNextInstruction(distance); + geoManeuver.setTimeToNextInstruction(time); + geoManeuver.setInstructionText(maneuverInstructionText); + geoManeuver.setPosition(coord); + geoManeuver.setWaypoint(coord); + + QVariantMap extraAttributes; + static const QStringList extras { + QLatin1String("bearing_before"), + QLatin1String("bearing_after"), + QLatin1String("instruction"), + QLatin1String("type"), + QLatin1String("modifier") }; + for (const QString &e: extras) { + if (maneuver.find(e) != maneuver.end()) + extraAttributes.insert(e, maneuver.value(e).toVariant()); + } + // These should be removed as soon as route leg support is introduced. + // Ref: http://project-osrm.org/docs/v5.15.2/api/#routeleg-object + extraAttributes.insert(QLatin1String("leg_index"), legIndex); + extraAttributes.insert(QLatin1String("step_index"), stepIndex); + + geoManeuver.setExtendedAttributes(extraAttributes); + + segment.setDistance(distance); + segment.setPath(path); + segment.setTravelTime(time); + segment.setManeuver(geoManeuver); + if (m_extension) + m_extension->updateSegment(segment, step, maneuver); + return segment; +} + +QGeoRouteReply::Error QGeoRouteParserOsrmV5Private::parseReply(QList &routes, QString &errorString, const QByteArray &reply) const +{ + // OSRM v5 specs: https://github.com/Project-OSRM/osrm-backend/blob/master/docs/http.md + // Mapbox Directions API spec: https://www.mapbox.com/api-documentation/#directions + QJsonDocument document = QJsonDocument::fromJson(reply); + if (document.isObject()) { + QJsonObject object = document.object(); + + QString status = object.value(QLatin1String("code")).toString(); + if (status != QLatin1String("Ok")) { + errorString = status; + return QGeoRouteReply::UnknownError; + } + if (!object.value(QLatin1String("routes")).isArray()) { + errorString = QLatin1String("No routes found"); + return QGeoRouteReply::ParseError; + } + + QJsonArray osrmRoutes = object.value(QLatin1String("routes")).toArray(); + foreach (const QJsonValue &r, osrmRoutes) { + if (!r.isObject()) + continue; + QJsonObject route = r.toObject(); + if (!route.value(QLatin1String("legs")).isArray()) + continue; + if (!route.value(QLatin1String("duration")).isDouble()) + continue; + if (!route.value(QLatin1String("distance")).isDouble()) + continue; + + double distance = route.value(QLatin1String("distance")).toDouble(); + double travelTime = route.value(QLatin1String("duration")).toDouble(); + bool error = false; + QList segments; + + QJsonArray legs = route.value(QLatin1String("legs")).toArray(); + for (int legIndex = 0; legIndex < legs.size(); ++legIndex) { + const QJsonValue &l = legs.at(legIndex); + if (!l.isObject()) { // invalid leg record + error = true; + break; + } + QJsonObject leg = l.toObject(); + if (!leg.value(QLatin1String("steps")).isArray()) { // Invalid steps field + error = true; + break; + } + QJsonArray steps = leg.value(QLatin1String("steps")).toArray(); + for (int stepIndex = 0; stepIndex < steps.size(); ++stepIndex) { + const QJsonValue &s = steps.at(stepIndex); + if (!s.isObject()) { + error = true; + break; + } + QGeoRouteSegment segment = parseStep(s.toObject(), legIndex, stepIndex); + if (segment.isValid()) { + segments.append(segment); + } else { + error = true; + break; + } + } + if (error) + break; + } + + if (!error) { + QList path; + foreach (const QGeoRouteSegment &s, segments) + path.append(s.path()); + + for (int i = segments.size() - 1; i > 0; --i) + segments[i-1].setNextRouteSegment(segments[i]); + + QGeoRoute r; + r.setDistance(distance); + r.setTravelTime(travelTime); + if (!path.isEmpty()) { + r.setPath(path); + r.setFirstRouteSegment(segments.first()); + } + //r.setTravelMode(QGeoRouteRequest::CarTravel); // The only one supported by OSRM demo service, but other OSRM servers might do cycle or pedestrian too + routes.append(r); + } + } + + // setError(QGeoRouteReply::NoError, status); // can't do this, or NoError is emitted and does damages + return QGeoRouteReply::NoError; + } else { + errorString = QLatin1String("Couldn't parse json."); + return QGeoRouteReply::ParseError; + } +} + +QUrl QGeoRouteParserOsrmV5Private::requestUrl(const QGeoRouteRequest &request, const QString &prefix) const +{ + QString routingUrl = prefix; + int notFirst = 0; + QString bearings; + const QList metadata = request.waypointsMetadata(); + const QList waypoints = request.waypoints(); + for (int i = 0; i < waypoints.size(); i++) { + const QGeoCoordinate &c = waypoints.at(i); + if (notFirst) { + routingUrl.append(QLatin1Char(';')); + bearings.append(QLatin1Char(';')); + } + routingUrl.append(QString::number(c.longitude(), 'f', 7)).append(QLatin1Char(',')).append(QString::number(c.latitude(), 'f', 7)); + if (metadata.size() > i) { + const QVariantMap &meta = metadata.at(i); + if (meta.contains(QLatin1String("bearing"))) { + qreal bearing = meta.value(QLatin1String("bearing")).toDouble(); + bearings.append(QString::number(int(bearing))).append(QLatin1Char(',')).append(QLatin1String("90")); // 90 is the angle of maneuver allowed. + } else { + bearings.append(QLatin1String("0,180")); // 180 here means anywhere + } + } + ++notFirst; + } + + QUrl url(routingUrl); + QUrlQuery query; + query.addQueryItem(QLatin1String("overview"), QLatin1String("full")); + query.addQueryItem(QLatin1String("steps"), QLatin1String("true")); + query.addQueryItem(QLatin1String("geometries"), QLatin1String("polyline6")); + query.addQueryItem(QLatin1String("alternatives"), QLatin1String("true")); + query.addQueryItem(QLatin1String("bearings"), bearings); + if (m_extension) + m_extension->updateQuery(query); + url.setQuery(query); + return url; +} + +QGeoRouteParserOsrmV5::QGeoRouteParserOsrmV5(QObject *parent) + : QGeoRouteParser(*new QGeoRouteParserOsrmV5Private(), parent) +{ +} + +QGeoRouteParserOsrmV5::~QGeoRouteParserOsrmV5() +{ +} + +void QGeoRouteParserOsrmV5::setExtension(const QGeoRouteParserOsrmV5Extension *extension) +{ + Q_D(QGeoRouteParserOsrmV5); + if (extension) + d->m_extension = extension; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeorouteparserosrmv5_p.h b/src/location/maps/qgeorouteparserosrmv5_p.h new file mode 100644 index 0000000..7f33af5 --- /dev/null +++ b/src/location/maps/qgeorouteparserosrmv5_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEPARSEROSRMV5_H +#define QGEOROUTEPARSEROSRMV5_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteParserOsrmV5Private; + +class Q_LOCATION_PRIVATE_EXPORT QGeoRouteParserOsrmV5Extension +{ +public: + QGeoRouteParserOsrmV5Extension() + { + } + + virtual ~QGeoRouteParserOsrmV5Extension() + { + } + + virtual void updateQuery(QUrlQuery &query) const = 0; + virtual void updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const = 0; +}; + +class Q_LOCATION_PRIVATE_EXPORT QGeoRouteParserOsrmV5 : public QGeoRouteParser +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoRouteParserOsrmV5) + +public: + QGeoRouteParserOsrmV5(QObject *parent = nullptr); + virtual ~QGeoRouteParserOsrmV5(); + + void setExtension(const QGeoRouteParserOsrmV5Extension *extension); + +private: + Q_DISABLE_COPY(QGeoRouteParserOsrmV5) +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTEPARSEROSRMV5_H diff --git a/src/location/maps/qgeoroutereply.cpp b/src/location/maps/qgeoroutereply.cpp new file mode 100644 index 0000000..48450bd --- /dev/null +++ b/src/location/maps/qgeoroutereply.cpp @@ -0,0 +1,277 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutereply.h" +#include "qgeoroutereply_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoRouteReply + \inmodule QtLocation + \ingroup QtLocation-routing + \since 5.6 + + \brief The QGeoRouteReply class manages an operation started by an instance + of QGeoRoutingManager. + + Instances of QGeoRouteReply manage the state and results of these + operations. + + The isFinished(), error() and errorString() methods provide information + on whether the operation has completed and if it completed successfully. + + The finished() and error(QGeoRouteReply::Error,QString) + signals can be used to monitor the progress of the operation. + + It is possible that a newly created QGeoRouteReply may be in a finished + state, most commonly because an error has occurred. Since such an instance + will never emit the finished() or + error(QGeoRouteReply::Error,QString) signals, it is + important to check the result of isFinished() before making the connections + to the signals. The documentation for QGeoRoutingManager demonstrates how + this might be carried out. + + If the operation completes successfully the results will be able to be + accessed with routes(). +*/ + +/*! + \enum QGeoRouteReply::Error + + Describes an error which prevented the completion of the operation. + + \value NoError + No error has occurred. + \value EngineNotSetError + The routing manager that was used did not have a QGeoRoutingManagerEngine instance associated with it. + \value CommunicationError + An error occurred while communicating with the service provider. + \value ParseError + The response from the service provider was in an unrecognizable format. + \value UnsupportedOptionError + The requested operation or one of the options for the operation are not + supported by the service provider. + \value UnknownError + An error occurred which does not fit into any of the other categories. +*/ + +/*! + Constructs a route reply object based on \a request, with the specified \a parent. +*/ +QGeoRouteReply::QGeoRouteReply(const QGeoRouteRequest &request, QObject *parent) + : QObject(parent), + d_ptr(new QGeoRouteReplyPrivate(request)) +{ +} + +/*! + Constructs a route reply with a given \a error and \a errorString and the specified \a parent. +*/ +QGeoRouteReply::QGeoRouteReply(Error error, const QString &errorString, QObject *parent) + : QObject(parent), + d_ptr(new QGeoRouteReplyPrivate(error, errorString)) {} + +/*! + Destroys this route reply object. +*/ +QGeoRouteReply::~QGeoRouteReply() +{ + delete d_ptr; +} + +/*! + Sets whether or not this reply has finished to \a finished. + + If \a finished is true, this will cause the finished() signal to be + emitted. + + If the operation completed successfully, QGeoRouteReply::setRoutes() should + be called before this function. If an error occurred, + QGeoRouteReply::setError() should be used instead. +*/ +void QGeoRouteReply::setFinished(bool finished) +{ + d_ptr->isFinished = finished; + if (d_ptr->isFinished) + emit this->finished(); +} + +/*! + Return true if the operation completed successfully or encountered an + error which cause the operation to come to a halt. +*/ +bool QGeoRouteReply::isFinished() const +{ + return d_ptr->isFinished; +} + +/*! + Sets the error state of this reply to \a error and the textual + representation of the error to \a errorString. + + This will also cause error() and finished() signals to be emitted, in that + order. +*/ +void QGeoRouteReply::setError(QGeoRouteReply::Error error, const QString &errorString) +{ + d_ptr->error = error; + d_ptr->errorString = errorString; + emit this->error(error, errorString); + setFinished(true); +} + +/*! + Returns the error state of this reply. + + If the result is QGeoRouteReply::NoError then no error has occurred. +*/ +QGeoRouteReply::Error QGeoRouteReply::error() const +{ + return d_ptr->error; +} + +/*! + Returns the textual representation of the error state of this reply. + + If no error has occurred this will return an empty string. It is possible + that an error occurred which has no associated textual representation, in + which case this will also return an empty string. + + To determine whether an error has occurred, check to see if + QGeoRouteReply::error() is equal to QGeoRouteReply::NoError. +*/ +QString QGeoRouteReply::errorString() const +{ + return d_ptr->errorString; +} + +/*! + Returns the route request which specified the route. +*/ +QGeoRouteRequest QGeoRouteReply::request() const +{ + return d_ptr->request; +} + +/*! + Returns the list of routes which were requested. +*/ +QList QGeoRouteReply::routes() const +{ + return d_ptr->routes; +} + +/*! + Sets the list of routes in the reply to \a routes. +*/ +void QGeoRouteReply::setRoutes(const QList &routes) +{ + d_ptr->routes = routes; +} + +/*! + Appends the list of \a routes to the existing list. +*/ +void QGeoRouteReply::addRoutes(const QList &routes) +{ + d_ptr->routes.append(routes); +} + +/*! + \fn void QGeoRouteReply::aborted() + \since 5.9 + + This signal is emitted when the operation has been cancelled. + + \sa abort() +*/ + +/*! + Cancels the operation immediately. + + This will do nothing if the reply is finished. +*/ +void QGeoRouteReply::abort() +{ + emit aborted(); +} + +/*! + \fn void QGeoRouteReply::finished() + + This signal is emitted when this reply has finished processing. + + If error() equals QGeoRouteReply::NoError then the processing + finished successfully. + + This signal and QGeoRoutingManager::finished() will be + emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ +/*! + \fn void QGeoRouteReply::error(QGeoRouteReply::Error error, const QString &errorString) + + This signal is emitted when an error has been detected in the processing of + this reply. The finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error. + + This signal and QGeoRoutingManager::error() will be emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/******************************************************************************* +*******************************************************************************/ + +QGeoRouteReplyPrivate::QGeoRouteReplyPrivate(const QGeoRouteRequest &request) + : error(QGeoRouteReply::NoError), + isFinished(false), + request(request) {} + +QGeoRouteReplyPrivate::QGeoRouteReplyPrivate(QGeoRouteReply::Error error, QString errorString) + : error(error), + errorString(errorString), + isFinished(true) {} + +QGeoRouteReplyPrivate::~QGeoRouteReplyPrivate() {} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeoroutereply.h b/src/location/maps/qgeoroutereply.h new file mode 100644 index 0000000..f873b99 --- /dev/null +++ b/src/location/maps/qgeoroutereply.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREPLY_H +#define QGEOROUTEREPLY_H + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteRequest; +class QGeoRouteReplyPrivate; + +class Q_LOCATION_EXPORT QGeoRouteReply : public QObject +{ + Q_OBJECT +public: + enum Error { + NoError, + EngineNotSetError, + CommunicationError, + ParseError, + UnsupportedOptionError, + UnknownError + }; + + explicit QGeoRouteReply(Error error, const QString &errorString, QObject *parent = nullptr); + virtual ~QGeoRouteReply(); + + bool isFinished() const; + Error error() const; + QString errorString() const; + + QGeoRouteRequest request() const; + QList routes() const; + + virtual void abort(); + +Q_SIGNALS: + void finished(); + void aborted(); + void error(QGeoRouteReply::Error error, const QString &errorString = QString()); + +protected: + explicit QGeoRouteReply(const QGeoRouteRequest &request, QObject *parent = nullptr); + + void setError(Error error, const QString &errorString); + void setFinished(bool finished); + + void setRoutes(const QList &routes); + void addRoutes(const QList &routes); + +private: + QGeoRouteReplyPrivate *d_ptr; + Q_DISABLE_COPY(QGeoRouteReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroutereply_p.h b/src/location/maps/qgeoroutereply_p.h new file mode 100644 index 0000000..496a616 --- /dev/null +++ b/src/location/maps/qgeoroutereply_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREPLY_P_H +#define QGEOROUTEREPLY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeorouterequest.h" +#include "qgeoroutereply.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGeoRoute; + +class QGeoRouteReplyPrivate +{ +public: + explicit QGeoRouteReplyPrivate(const QGeoRouteRequest &request); + QGeoRouteReplyPrivate(QGeoRouteReply::Error error, QString errorString); + ~QGeoRouteReplyPrivate(); + + QGeoRouteReply::Error error; + QString errorString; + bool isFinished; + + QGeoRouteRequest request; + QList routes; + +private: + Q_DISABLE_COPY(QGeoRouteReplyPrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeorouterequest.cpp b/src/location/maps/qgeorouterequest.cpp new file mode 100644 index 0000000..57fbe20 --- /dev/null +++ b/src/location/maps/qgeorouterequest.cpp @@ -0,0 +1,532 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeorouterequest.h" +#include "qgeorouterequest_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoRouteRequest + \inmodule QtLocation + \ingroup QtLocation-routing + \since 5.6 + + \brief The QGeoRouteRequest class represents the parameters and restrictions + which define a request for routing information. + + The default state of a QGeoRouteRequest instance will result in a request + for basic route segment and navigation maneuvers describing the fastest + route by car which covers the given waypoints. + + There may be significant variation in the features supported by different + providers of routing information, or even in the features supported by + the same provider if different levels of authorization are used. + + There are several functions in QGeoRoutingManager which can be used to + check which features are supported with the current provider and + authorization level. + \sa QGeoRoutingManager +*/ + +/* + DESIGN NOTE + + There are plans to make this extensible by allowing the user to set a + list of QGeoTransportOptions (or QGeoTransitOptions). We don't have any + subclasses for that just yet, otherwise they'd be present. + + A QGeoPublicTransportOption subclass would allow users to specify things + like cost limits, the maximum number of changes of vehicle, the maximum + walking time / distance between changes of vehicle. + + There's Nokia / Navteq support for specifying things like Truck attributes + so you can route around various road restrictions and conditions which + effect trucks. + + A QGeoTrafficAwareTransportOption is probably also a good place to put the + inputs for the various traffic / time aware bits of information. A + departure / arrrival time could be set, and the presence of this transport + options subclass (and the fact that user auth said that the user had + support) would mean we could provide better values for the estimated + travel times and so on. + + This all relies on at least one service making this data available to us, + which would probably be tied to token based authorization. It could be + some time before this becomes available. +*/ + +/*! + \enum QGeoRouteRequest::TravelMode + + Defines modes of travel to be used for a route. + + \value CarTravel + The route will be optimized for someone who is driving a car. + \value PedestrianTravel + The route will be optimized for someone who is walking. + \value BicycleTravel + The route will be optimized for someone who is riding a bicycle. + \value PublicTransitTravel + The route will be optimized for someone who is making use of public transit. + \value TruckTravel + The route will be optimized for someone who is driving a truck. +*/ + +/*! + \enum QGeoRouteRequest::FeatureType + + Defines a feature which is important to the planning of a route. + + These values will be used in combination with + QGeoRouteRequest::FeatureWeight to determine if they should or should + not be part of the route. + + \value NoFeature + Used by QGeoRoutingManager::supportedFeatureTypes() to indicate that + no features will be taken into account when planning the route. + \value TollFeature + Consdier tollways when planning the route. + \value HighwayFeature + Consider highways when planning the route. + \value PublicTransitFeature + Consider public transit when planning the route. + \value FerryFeature + Consider ferries when planning the route. + \value TunnelFeature + Consider tunnels when planning the route. + \value DirtRoadFeature + Consider dirt roads when planning the route. + \value ParksFeature + Consider parks when planning the route. + \value MotorPoolLaneFeature + Consider motor pool lanes when planning the route. + \value TrafficFeature + Consider the current traffic situation when planning the route. Since QtLocation 5.10 +*/ + +/*! + \enum QGeoRouteRequest::FeatureWeight + + Defines the weight to associate with a feature during the + planning of a route. + + These values will be used in combination with + QGeoRouteRequest::Feature to determine if they should or should + not be part of the route. + + \value NeutralFeatureWeight + The presence or absence of the feature will not affect the + planning of the route. + \value PreferFeatureWeight + Routes which contain the feature will be preferred over those that do + not. + \value RequireFeatureWeight + Only routes which contain the feature will be considered, otherwise + no route will be returned. + \value AvoidFeatureWeight + Routes which do not contain the feature will be preferred over those + that do. + \value DisallowFeatureWeight + Only routes which do not contain the feature will be considered, + otherwise no route will be returned. +*/ + +// TODO improve description of MostScenicRoute +/*! + \enum QGeoRouteRequest::RouteOptimization + + Defines the type of optimization which is applied to the planning of the route. + + \value ShortestRoute + Minimize the length of the journey. + \value FastestRoute + Minimize the traveling time for the journey. + \value MostEconomicRoute + Minimize the cost of the journey. + \value MostScenicRoute + Maximize the scenic potential of the journey. +*/ + +/*! + \enum QGeoRouteRequest::SegmentDetail + + Defines the amount of route segment information that should be included + with the route. + + \value NoSegmentData + No segment data should be included with the route. A route requested + with this level of segment detail will initialize + QGeoRouteSegment::path() as a straight line between the positions of + the previous and next QGeoManeuver instances. + + \value BasicSegmentData + Basic segment data will be included with the route. This will include + QGeoRouteSegment::path(). +*/ + +/*! + \enum QGeoRouteRequest::ManeuverDetail + + Defines the amount of maneuver information that should be included with + the route. + + \value NoManeuvers + No maneuvers should be included with the route. + + \value BasicManeuvers + Basic manevuers will be included with the route. This will + include QGeoManeuver::instructionText(). +*/ + +/*! + Constructs a request to calculate a route through the coordinates \a waypoints. + + The route will traverse the objects of \a waypoints in order. +*/ +QGeoRouteRequest::QGeoRouteRequest(const QList &waypoints) + : d_ptr(new QGeoRouteRequestPrivate()) +{ + d_ptr->waypoints = waypoints; +} + +/*! + Constructs a request to calculate a route between \a origin and + \a destination. +*/ +QGeoRouteRequest::QGeoRouteRequest(const QGeoCoordinate &origin, const QGeoCoordinate &destination) + : d_ptr(new QGeoRouteRequestPrivate()) +{ + d_ptr->waypoints.append(origin); + d_ptr->waypoints.append(destination); +} + +/*! + Constructs a route request object from the contents of \a other. +*/ +QGeoRouteRequest::QGeoRouteRequest(const QGeoRouteRequest &other) + : d_ptr(other.d_ptr) {} + +/*! + Destroys the request. +*/ +QGeoRouteRequest::~QGeoRouteRequest() {} + +/*! + Assigns \a other to this route request object and then returns a reference + to this route request object. +*/ +QGeoRouteRequest &QGeoRouteRequest::operator= (const QGeoRouteRequest & other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns whether this route request and \a other are equal. +*/ +bool QGeoRouteRequest::operator ==(const QGeoRouteRequest &other) const +{ + return ( (d_ptr.constData() == other.d_ptr.constData()) + || (*d_ptr) == (*other.d_ptr)); +} + +/*! + Returns whether this route request and \a other are equal. +*/ +bool QGeoRouteRequest::operator !=(const QGeoRouteRequest &other) const +{ + return !(operator==(other)); +} + +/*! + Sets \a waypoints as the waypoints that the route should pass through. + + The waypoints should be given in order from origin to destination. + + This request will be invalid until the waypoints have been set to a + list containing two or more coordinates. +*/ +void QGeoRouteRequest::setWaypoints(const QList &waypoints) +{ + d_ptr->waypoints = waypoints; +} + +/*! + Returns the waypoints that the route will pass through. +*/ +QList QGeoRouteRequest::waypoints() const +{ + return d_ptr->waypoints; +} + +/*! + Sets \a waypointMetadata as the metadata for the waypoints set in this request. + The metadata are intended as one QVariantMap per waypoint, given in the same order as + the waypoints. + + The content of the QVariantMap is somehow backend-specific, but properties that can be specified using + \l Waypoint elements in QML can be assumed to be named and to work the same way across plugins, where supported. +*/ +void QGeoRouteRequest::setWaypointsMetadata(const QList &waypointMetadata) +{ + d_ptr->waypointMetadata = waypointMetadata; +} + +/*! + Returns the metadata for the waypoints in this request. +*/ +QList QGeoRouteRequest::waypointsMetadata() const +{ + return d_ptr->waypointMetadata; +} + +/*! + Sets \a areas as excluded areas that the route must not cross. +*/ +void QGeoRouteRequest::setExcludeAreas(const QList &areas) +{ + d_ptr->excludeAreas = areas; +} + +/*! + Returns areas the route must not cross. +*/ +QList QGeoRouteRequest::excludeAreas() const +{ + return d_ptr->excludeAreas; +} + +/*! + Sets the number of alternative routes to request to \a alternatives. If \a alternatives is + negative the number of alternative routes is set to 0. + + The default value is 0. +*/ +void QGeoRouteRequest::setNumberAlternativeRoutes(int alternatives) +{ + d_ptr->numberAlternativeRoutes = qMax(0, alternatives); +} + +/*! + Returns the number of alternative routes which will be requested. +*/ +int QGeoRouteRequest::numberAlternativeRoutes() const +{ + return d_ptr->numberAlternativeRoutes; +} + +/*! + Sets the travel modes which should be considered during the planning of the + route to \a travelModes. + + The default value is QGeoRouteRequest::CarTravel. +*/ +void QGeoRouteRequest::setTravelModes(QGeoRouteRequest::TravelModes travelModes) +{ + d_ptr->travelModes = travelModes; +} + +/*! + Returns the travel modes which this request specifies should be considered + during the planning of the route. +*/ +QGeoRouteRequest::TravelModes QGeoRouteRequest::travelModes() const +{ + return d_ptr->travelModes; +} + +/*! + Assigns the weight \a featureWeight to the feature \a featureType during + the planning of the route. + + By default all features are assigned a weight of NeutralFeatureWeight. + + It is impossible to assign a weight to QGeoRouteRequest::NoFeature. +*/ +void QGeoRouteRequest::setFeatureWeight(QGeoRouteRequest::FeatureType featureType, QGeoRouteRequest::FeatureWeight featureWeight) +{ + if (featureWeight != QGeoRouteRequest::NeutralFeatureWeight) { + if (featureType != QGeoRouteRequest::NoFeature) + d_ptr->featureWeights[featureType] = featureWeight; + } else { + d_ptr->featureWeights.remove(featureType); + } +} + +/*! + Returns the weight assigned to \a featureType in the planning of the route. + + If no feature weight has been specified for \a featureType then + NeutralFeatureWeight will be returned. +*/ +QGeoRouteRequest::FeatureWeight QGeoRouteRequest::featureWeight(QGeoRouteRequest::FeatureType featureType) const +{ + return d_ptr->featureWeights.value(featureType, QGeoRouteRequest::NeutralFeatureWeight); +} + +/*! + Returns the list of features that will be considered when planning the + route. Features with a weight of NeutralFeatureWeight will not be returned. +*/ +QList QGeoRouteRequest::featureTypes() const +{ + return d_ptr->featureWeights.keys(); +} + +/*! + Sets the optimization criteria to use while planning the route to + \a optimization. + + The default value is QGeoRouteRequest::FastestRoute. +*/ +void QGeoRouteRequest::setRouteOptimization(QGeoRouteRequest::RouteOptimizations optimization) +{ + d_ptr->routeOptimization = optimization; +} + +/*! + Returns the optimization criteria which this request specifies should be + used while planning the route. +*/ +QGeoRouteRequest::RouteOptimizations QGeoRouteRequest::routeOptimization() const +{ + return d_ptr->routeOptimization; +} + +/*! + Sets the level of detail to use when representing routing segments to + \a segmentDetail. +*/ +void QGeoRouteRequest::setSegmentDetail(QGeoRouteRequest::SegmentDetail segmentDetail) +{ + d_ptr->segmentDetail = segmentDetail; +} + +/*! + Returns the level of detail which will be used in the representation of + routing segments. +*/ +QGeoRouteRequest::SegmentDetail QGeoRouteRequest::segmentDetail() const +{ + return d_ptr->segmentDetail; +} + +/*! + Sets the level of detail to use when representing routing maneuvers to + \a maneuverDetail. + + The default value is QGeoRouteRequest::BasicManeuvers. +*/ +void QGeoRouteRequest::setManeuverDetail(QGeoRouteRequest::ManeuverDetail maneuverDetail) +{ + d_ptr->maneuverDetail = maneuverDetail; +} + +/*! + Returns the level of detail which will be used in the representation of + routing maneuvers. +*/ +QGeoRouteRequest::ManeuverDetail QGeoRouteRequest::maneuverDetail() const +{ + return d_ptr->maneuverDetail; +} + +/*! + Sets the extra parameters \a extraParameters for the route request. + The format of the extra parameters is plugin specific, and documented per plugin. + + \since 5.11 +*/ +void QGeoRouteRequest::setExtraParameters(const QVariantMap &extraParameters) +{ + d_ptr->extraParameters = extraParameters; +} + +/*! + Returns the extra parameters set for this route request. + + \since 5.11 +*/ +QVariantMap QGeoRouteRequest::extraParameters() const +{ + return d_ptr->extraParameters; +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoRouteRequestPrivate::QGeoRouteRequestPrivate() + : QSharedData(), + numberAlternativeRoutes(0), + travelModes(QGeoRouteRequest::CarTravel), + routeOptimization(QGeoRouteRequest::FastestRoute), + segmentDetail(QGeoRouteRequest::BasicSegmentData), + maneuverDetail(QGeoRouteRequest::BasicManeuvers) {} + +QGeoRouteRequestPrivate::QGeoRouteRequestPrivate(const QGeoRouteRequestPrivate &other) + : QSharedData(other), + waypoints(other.waypoints), + waypointMetadata(other.waypointMetadata), + excludeAreas(other.excludeAreas), + numberAlternativeRoutes(other.numberAlternativeRoutes), + travelModes(other.travelModes), + featureWeights(other.featureWeights), + routeOptimization(other.routeOptimization), + segmentDetail(other.segmentDetail), + maneuverDetail(other.maneuverDetail), + extraParameters(other.extraParameters) {} + +QGeoRouteRequestPrivate::~QGeoRouteRequestPrivate() {} + +bool QGeoRouteRequestPrivate::operator ==(const QGeoRouteRequestPrivate &other) const +{ + return ((waypoints == other.waypoints) + && (waypointMetadata == other.waypointMetadata) + && (excludeAreas == other.excludeAreas) + && (numberAlternativeRoutes == other.numberAlternativeRoutes) + && (travelModes == other.travelModes) + && (featureWeights == other.featureWeights) + && (routeOptimization == other.routeOptimization) + && (segmentDetail == other.segmentDetail) + && (maneuverDetail == other.maneuverDetail) + && (extraParameters == other.extraParameters)); +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeorouterequest.h b/src/location/maps/qgeorouterequest.h new file mode 100644 index 0000000..5a4bc61 --- /dev/null +++ b/src/location/maps/qgeorouterequest.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREQUEST_H +#define QGEOROUTEREQUEST_H + +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteRequestPrivate; + +class Q_LOCATION_EXPORT QGeoRouteRequest +{ +public: + enum TravelMode { + CarTravel = 0x0001, + PedestrianTravel = 0x0002, + BicycleTravel = 0x0004, + PublicTransitTravel = 0x0008, + TruckTravel = 0x0010 + }; + Q_DECLARE_FLAGS(TravelModes, TravelMode) + + enum FeatureType { + NoFeature = 0x00000000, + TollFeature = 0x00000001, + HighwayFeature = 0x00000002, + PublicTransitFeature = 0x00000004, + FerryFeature = 0x00000008, + TunnelFeature = 0x00000010, + DirtRoadFeature = 0x00000020, + ParksFeature = 0x00000040, + MotorPoolLaneFeature = 0x00000080, + TrafficFeature = 0x00000100 + }; + Q_DECLARE_FLAGS(FeatureTypes, FeatureType) + + enum FeatureWeight { + NeutralFeatureWeight = 0x00000000, + PreferFeatureWeight = 0x00000001, + RequireFeatureWeight = 0x00000002, + AvoidFeatureWeight = 0x00000004, + DisallowFeatureWeight = 0x00000008 + }; + Q_DECLARE_FLAGS(FeatureWeights, FeatureWeight) + + enum RouteOptimization { + ShortestRoute = 0x0001, + FastestRoute = 0x0002, + MostEconomicRoute = 0x0004, + MostScenicRoute = 0x0008 + }; + Q_DECLARE_FLAGS(RouteOptimizations, RouteOptimization) + + enum SegmentDetail { + NoSegmentData = 0x0000, + BasicSegmentData = 0x0001 + }; + Q_DECLARE_FLAGS(SegmentDetails, SegmentDetail) + + enum ManeuverDetail { + NoManeuvers = 0x0000, + BasicManeuvers = 0x0001 + }; + Q_DECLARE_FLAGS(ManeuverDetails, ManeuverDetail) + + explicit QGeoRouteRequest(const QList &waypoints = QList()); + QGeoRouteRequest(const QGeoCoordinate &origin, + const QGeoCoordinate &destination); + QGeoRouteRequest(const QGeoRouteRequest &other); + + ~QGeoRouteRequest(); + + QGeoRouteRequest &operator= (const QGeoRouteRequest &other); + + bool operator == (const QGeoRouteRequest &other) const; + bool operator != (const QGeoRouteRequest &other) const; + + void setWaypoints(const QList &waypoints); + QList waypoints() const; + + void setWaypointsMetadata(const QList &waypointMetadata); + QList waypointsMetadata() const; + + void setExcludeAreas(const QList &areas); + QList excludeAreas() const; + + // defaults to 0 + void setNumberAlternativeRoutes(int alternatives); + int numberAlternativeRoutes() const; + + // defaults to TravelByCar + void setTravelModes(TravelModes travelModes); + TravelModes travelModes() const; + + void setFeatureWeight(FeatureType featureType, FeatureWeight featureWeight); + FeatureWeight featureWeight(FeatureType featureType) const; + QList featureTypes() const; + + // defaults to OptimizeFastest + void setRouteOptimization(RouteOptimizations optimization); + RouteOptimizations routeOptimization() const; + + // defaults to BasicSegmentData + void setSegmentDetail(SegmentDetail segmentDetail); + SegmentDetail segmentDetail() const; + + // defaults to BasicManeuvers + void setManeuverDetail(ManeuverDetail maneuverDetail); + ManeuverDetail maneuverDetail() const; + + void setExtraParameters(const QVariantMap &extraParameters); + QVariantMap extraParameters() const; + +private: + QExplicitlySharedDataPointer d_ptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoRouteRequest::TravelModes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoRouteRequest::FeatureTypes) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoRouteRequest::FeatureWeights) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoRouteRequest::RouteOptimizations) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoRouteRequest::SegmentDetails) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoRouteRequest::ManeuverDetails) + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeorouterequest_p.h b/src/location/maps/qgeorouterequest_p.h new file mode 100644 index 0000000..df0cd62 --- /dev/null +++ b/src/location/maps/qgeorouterequest_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREQUEST_P_H +#define QGEOROUTEREQUEST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeorouterequest.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteRequestPrivate : public QSharedData +{ +public: + QGeoRouteRequestPrivate(); + QGeoRouteRequestPrivate(const QGeoRouteRequestPrivate &other); + ~QGeoRouteRequestPrivate(); + + bool operator ==(const QGeoRouteRequestPrivate &other) const; + + QList waypoints; + QList waypointMetadata; + QList excludeAreas; + int numberAlternativeRoutes; + QGeoRouteRequest::TravelModes travelModes; + QMap < QGeoRouteRequest::FeatureType, + QGeoRouteRequest::FeatureWeight > featureWeights; + QGeoRouteRequest::RouteOptimizations routeOptimization; + QGeoRouteRequest::SegmentDetail segmentDetail; + QGeoRouteRequest::ManeuverDetail maneuverDetail; + QVariantMap extraParameters; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroutesegment.cpp b/src/location/maps/qgeoroutesegment.cpp new file mode 100644 index 0000000..5bfb4f6 --- /dev/null +++ b/src/location/maps/qgeoroutesegment.cpp @@ -0,0 +1,429 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutesegment.h" +#include "qgeoroutesegment_p.h" + +#include "qgeocoordinate.h" +#include + +QT_BEGIN_NAMESPACE + +template<> +QGeoRouteSegmentPrivate *QExplicitlySharedDataPointer::clone() +{ + return d->clone(); +} + +/*! + \class QGeoRouteSegment + \inmodule QtLocation + \ingroup QtLocation-routing + \since 5.6 + + \brief The QGeoRouteSegment class represents a segment of a route. + + A QGeoRouteSegment instance has information about the physical layout + of the route segment, the length of the route and estimated time required + to traverse the route segment and an optional QGeoManeuver associated with + the end of the route segment. + + QGeoRouteSegment instances can be thought of as edges on a routing + graph, with QGeoManeuver instances as optional labels attached to the + vertices of the graph. +*/ + +/*! + Constructs an invalid route segment object. + + The route segment will remain invalid until one of setNextRouteSegment(), + setTravelTime(), setDistance(), setPath() or setManeuver() is called. +*/ +QGeoRouteSegment::QGeoRouteSegment() + : d_ptr(new QGeoRouteSegmentPrivateDefault()) {} + +/*! + Constructs a route segment object from the contents of \a other. +*/ +QGeoRouteSegment::QGeoRouteSegment(const QGeoRouteSegment &other) + : d_ptr(other.d_ptr) {} + +/*! + \internal +*/ +QGeoRouteSegment::QGeoRouteSegment(const QExplicitlySharedDataPointer &dd) + : d_ptr(dd) {} + +/*! + Returns the private implementation. +*/ +QExplicitlySharedDataPointer &QGeoRouteSegment::d() +{ + return d_ptr; +} + +/*! + Destroys this route segment object. +*/ +QGeoRouteSegment::~QGeoRouteSegment() {} + + +/*! + Assigns \a other to this route segment object and then returns a + reference to this route segment object. +*/ +QGeoRouteSegment &QGeoRouteSegment::operator= (const QGeoRouteSegment & other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns whether this route segment and \a other are equal. + + The value of nextRouteSegment() is not considered in the comparison. +*/ +bool QGeoRouteSegment::operator ==(const QGeoRouteSegment &other) const +{ + return ( (d_ptr.constData() == other.d_ptr.constData()) + || (*d_ptr) == (*other.d_ptr)); +} + +/*! + Returns whether this route segment and \a other are not equal. + + The value of nextRouteSegment() is not considered in the comparison. +*/ +bool QGeoRouteSegment::operator !=(const QGeoRouteSegment &other) const +{ + return !(operator==(other)); +} + +/*! + Returns whether this route segment is valid or not. + + If nextRouteSegment() is called on the last route segment of a route, the + returned value will be an invalid route segment. +*/ +bool QGeoRouteSegment::isValid() const +{ + return d_ptr->valid(); +} + +/*! + Sets the next route segment in the route to \a routeSegment. +*/ +void QGeoRouteSegment::setNextRouteSegment(const QGeoRouteSegment &routeSegment) +{ + d_ptr->setValid(true); + d_ptr->setNextRouteSegment(routeSegment.d_ptr); +} + +/*! + Returns the next route segment in the route. + + Will return an invalid route segment if this is the last route + segment in the route. +*/ +QGeoRouteSegment QGeoRouteSegment::nextRouteSegment() const +{ + if (d_ptr->valid() && d_ptr->m_nextSegment) + return QGeoRouteSegment(d_ptr->m_nextSegment); + + return QGeoRouteSegment(); +} + +/*! + Sets the estimated amount of time it will take to traverse this segment of + the route, in seconds, to \a secs. +*/ +void QGeoRouteSegment::setTravelTime(int secs) +{ + d_ptr->setValid(true); + d_ptr->setTravelTime(secs); +} + +/*! + Returns the estimated amount of time it will take to traverse this segment + of the route, in seconds. +*/ +int QGeoRouteSegment::travelTime() const +{ + return d_ptr->travelTime(); +} + +/*! + Sets the distance covered by this segment of the route, in meters, to \a distance. +*/ +void QGeoRouteSegment::setDistance(qreal distance) +{ + d_ptr->setValid(true); + d_ptr->setDistance(distance); +} + +/*! + Returns the distance covered by this segment of the route, in meters. +*/ +qreal QGeoRouteSegment::distance() const +{ + return d_ptr->distance(); +} + +/*! + Sets the geometric shape of this segment of the route to \a path. + + The coordinates in \a path should be listed in the order in which they + would be traversed by someone traveling along this segment of the route. +*/ +void QGeoRouteSegment::setPath(const QList &path) +{ + d_ptr->setValid(true); + d_ptr->setPath(path); +} + +/*! + Returns the geometric shape of this route segment of the route. + + The coordinates should be listed in the order in which they + would be traversed by someone traveling along this segment of the route. +*/ + +QList QGeoRouteSegment::path() const +{ + return d_ptr->path(); +} + +/*! + Sets the maneuver for this route segment to \a maneuver. +*/ +void QGeoRouteSegment::setManeuver(const QGeoManeuver &maneuver) +{ + d_ptr->setValid(true); + d_ptr->setManeuver(maneuver); +} + +/*! + Returns the maneuver for this route segment. + + Will return an invalid QGeoManeuver if no information has been attached + to the endpoint of this route segment. +*/ +QGeoManeuver QGeoRouteSegment::maneuver() const +{ + return d_ptr->maneuver(); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoRouteSegmentPrivate::QGeoRouteSegmentPrivate() {} + +QGeoRouteSegmentPrivate::QGeoRouteSegmentPrivate(const QGeoRouteSegmentPrivate &other) + : QSharedData(other), m_nextSegment(other.m_nextSegment) {} + +QGeoRouteSegmentPrivate::~QGeoRouteSegmentPrivate() +{ + m_nextSegment.reset(); +} + +bool QGeoRouteSegmentPrivate::operator ==(const QGeoRouteSegmentPrivate &other) const +{ + return equals(other); +} + +bool QGeoRouteSegmentPrivate::equals(const QGeoRouteSegmentPrivate &other) const +{ + return ((valid() == other.valid()) + && (travelTime() == other.travelTime()) + && (distance() == other.distance()) + && (path() == other.path()) + && (maneuver() == other.maneuver())); +} + +bool QGeoRouteSegmentPrivate::valid() const +{ + return false; +} + +void QGeoRouteSegmentPrivate::setValid(bool valid) +{ + Q_UNUSED(valid) +} + +int QGeoRouteSegmentPrivate::travelTime() const +{ + return 0; +} + +void QGeoRouteSegmentPrivate::setTravelTime(int travelTime) +{ + Q_UNUSED(travelTime) +} + +qreal QGeoRouteSegmentPrivate::distance() const +{ + return 0; +} + +void QGeoRouteSegmentPrivate::setDistance(qreal distance) +{ + Q_UNUSED(distance) +} + +QList QGeoRouteSegmentPrivate::path() const +{ + return QList(); +} + +void QGeoRouteSegmentPrivate::setPath(const QList &path) +{ + Q_UNUSED(path) +} + +QGeoManeuver QGeoRouteSegmentPrivate::maneuver() const +{ + return QGeoManeuver(); +} + +void QGeoRouteSegmentPrivate::setManeuver(const QGeoManeuver &maneuver) +{ + Q_UNUSED(maneuver) +} + +QExplicitlySharedDataPointer QGeoRouteSegmentPrivate::nextRouteSegment() const +{ + return m_nextSegment; +} + +void QGeoRouteSegmentPrivate::setNextRouteSegment(const QExplicitlySharedDataPointer &next) +{ + m_nextSegment = next; +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoRouteSegmentPrivateDefault::QGeoRouteSegmentPrivateDefault() + : m_valid(false), + m_travelTime(0), + m_distance(0.0) +{ + +} + +QGeoRouteSegmentPrivateDefault::QGeoRouteSegmentPrivateDefault(const QGeoRouteSegmentPrivateDefault &other) + : QGeoRouteSegmentPrivate(other), + m_valid(other.m_valid), + m_travelTime(other.m_travelTime), + m_distance(other.m_distance), + m_path(other.m_path), + m_maneuver(other.m_maneuver) +{ + +} + +QGeoRouteSegmentPrivateDefault::~QGeoRouteSegmentPrivateDefault() +{ + +} + +QGeoRouteSegmentPrivate *QGeoRouteSegmentPrivateDefault::clone() +{ + return new QGeoRouteSegmentPrivateDefault(*this); +} + +bool QGeoRouteSegmentPrivateDefault::operator ==(const QGeoRouteSegmentPrivateDefault &other) const +{ + return QGeoRouteSegmentPrivate::operator ==(other); +} + +bool QGeoRouteSegmentPrivateDefault::valid() const +{ + return m_valid; +} + +void QGeoRouteSegmentPrivateDefault::setValid(bool valid) +{ + m_valid = valid; +} + +int QGeoRouteSegmentPrivateDefault::travelTime() const +{ + return m_travelTime; +} + +void QGeoRouteSegmentPrivateDefault::setTravelTime(int travelTime) +{ + m_travelTime = travelTime; +} + +qreal QGeoRouteSegmentPrivateDefault::distance() const +{ + return m_distance; +} + +void QGeoRouteSegmentPrivateDefault::setDistance(qreal distance) +{ + m_distance = distance; +} + +QList QGeoRouteSegmentPrivateDefault::path() const +{ + return m_path; +} + +void QGeoRouteSegmentPrivateDefault::setPath(const QList &path) +{ + m_path = path; +} + +QGeoManeuver QGeoRouteSegmentPrivateDefault::maneuver() const +{ + return m_maneuver; +} + +void QGeoRouteSegmentPrivateDefault::setManeuver(const QGeoManeuver &maneuver) +{ + m_maneuver = maneuver; +} + +/******************************************************************************* +*******************************************************************************/ + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeoroutesegment.h b/src/location/maps/qgeoroutesegment.h new file mode 100644 index 0000000..3ce0115 --- /dev/null +++ b/src/location/maps/qgeoroutesegment.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTESEGMENT_H +#define QGEOROUTESEGMENT_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCoordinate; +class QGeoManeuver; +class QGeoRouteSegmentPrivate; + +class Q_LOCATION_EXPORT QGeoRouteSegment +{ + +public: + QGeoRouteSegment(); + QGeoRouteSegment(const QGeoRouteSegment &other); + ~QGeoRouteSegment(); + + QGeoRouteSegment &operator= (const QGeoRouteSegment &other); + + bool operator ==(const QGeoRouteSegment &other) const; + bool operator !=(const QGeoRouteSegment &other) const; + + bool isValid() const; + + void setNextRouteSegment(const QGeoRouteSegment &routeSegment); + QGeoRouteSegment nextRouteSegment() const; + + void setTravelTime(int secs); + int travelTime() const; + + void setDistance(qreal distance); + qreal distance() const; + + void setPath(const QList &path); + QList path() const; + + void setManeuver(const QGeoManeuver &maneuver); + QGeoManeuver maneuver() const; + +protected: + QGeoRouteSegment(const QExplicitlySharedDataPointer &dd); + QExplicitlySharedDataPointer &d(); + +private: + QExplicitlySharedDataPointer d_ptr; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroutesegment_p.h b/src/location/maps/qgeoroutesegment_p.h new file mode 100644 index 0000000..f0b180d --- /dev/null +++ b/src/location/maps/qgeoroutesegment_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTESEGMENT_P_H +#define QGEOROUTESEGMENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCoordinate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoRouteSegmentPrivate : public QSharedData +{ +public: + QGeoRouteSegmentPrivate(); + QGeoRouteSegmentPrivate(const QGeoRouteSegmentPrivate &other); + virtual ~QGeoRouteSegmentPrivate(); + virtual QGeoRouteSegmentPrivate *clone() = 0; + + bool operator ==(const QGeoRouteSegmentPrivate &other) const; + + virtual bool valid() const; + virtual void setValid(bool valid); + + virtual int travelTime() const; + virtual void setTravelTime(int travelTime); + + virtual qreal distance() const; + virtual void setDistance(qreal distance); + + virtual QList path() const; + virtual void setPath(const QList &path); + + virtual QGeoManeuver maneuver() const; + virtual void setManeuver(const QGeoManeuver &maneuver); + + virtual QExplicitlySharedDataPointer nextRouteSegment() const; + virtual void setNextRouteSegment(const QExplicitlySharedDataPointer &next); + + QExplicitlySharedDataPointer m_nextSegment; + +protected: + virtual bool equals(const QGeoRouteSegmentPrivate &other) const; +}; + + + +class Q_LOCATION_PRIVATE_EXPORT QGeoRouteSegmentPrivateDefault : public QGeoRouteSegmentPrivate +{ +public: + QGeoRouteSegmentPrivateDefault(); + QGeoRouteSegmentPrivateDefault(const QGeoRouteSegmentPrivateDefault &other); + ~QGeoRouteSegmentPrivateDefault(); + virtual QGeoRouteSegmentPrivate *clone() override; + + bool operator ==(const QGeoRouteSegmentPrivateDefault &other) const; + + virtual bool valid() const override; + virtual void setValid(bool valid) override; + + virtual int travelTime() const override; + virtual void setTravelTime(int travelTime) override; + + virtual qreal distance() const override; + virtual void setDistance(qreal distance) override; + + virtual QList path() const override; + virtual void setPath(const QList &path) override; + + virtual QGeoManeuver maneuver() const override; + virtual void setManeuver(const QGeoManeuver &maneuver) override; + + + bool m_valid; + int m_travelTime; + qreal m_distance; + QList m_path; + QGeoManeuver m_maneuver; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroutingmanager.cpp b/src/location/maps/qgeoroutingmanager.cpp new file mode 100644 index 0000000..195c285 --- /dev/null +++ b/src/location/maps/qgeoroutingmanager.cpp @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutingmanager.h" +#include "qgeoroutingmanager_p.h" +#include "qgeoroutingmanagerengine.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoRoutingManager + \inmodule QtLocation + \ingroup QtLocation-routing + \since 5.6 + + \brief The QGeoRoutingManager class provides support for geographic routing + operations. + + The calculateRoute() and updateRoute() methods function QGeoRouteReply + objects, which manage these operations and report on the result of the + operations and any errors which may have occurred. + + The calculateRoute() function is used to find a route (or routes) that + follows a set of waypoints and matches various other criteria. The + QGeoRouteRequest class is used to specify this information. + + If supportsRouteUpdates() returns true then the QGeoRoutingManager + supports updating route information based on position updates. This + will cause the travel time and distance estimates to be updated, and + any QGeoRouteSegments already traversed to be removed from the route. + + The updates can be triggered with the updateRoute() function, which makes + use of the QGeoPositionInfo instances emitted as position updates by + QGeoPositionInfoSource. + + Instances of QGeoRoutingManager can be accessed with + QGeoServiceProvider::routingManager(). + + A small example of the usage of QGeoRoutingManager and QGeoRouteRequests + follows: + + \code +class MyRouteHandler : public QObject +{ + Q_OBJECT +public: + MyRouteHandler(QGeoRoutingManager *routingManager, + const QGeoCoordinate &origin, + const QGeoCoordinate &destination) { + + QGeoRouteRequest request(origin, destination); + + // The request defaults to the fastest route by car, which is + // equivalent to: + // request.setTravelMode(QGeoRouteRequest::CarTravel); + // request.setRouteOptimization(QGeoRouteRequest::FastestRoute); + + request.setAvoidFeatureTypes(QGeoRouteRequest::AvoidTolls); + request.setAvoidFeatureTypes(QGeoRouteRequest::AvoidMotorPoolLanes); + + QGeoRouteRequest::AvoidFeaturesTypes avoidableFeatures = routingManager->supportedAvoidFeatureTypes(); + + if (!(avoidableFeatures & request.avoidFeatureTypes())) { + // ... inform the user that the routing manager does not + // provide support for avoiding tolls and/or motor pool lanes ... + return; + } + + QGeoRouteReply *reply = routingManager->calculateRoute(request); + + if (reply->isFinished()) { + if (reply->error() == QGeoRouteReply::NoError) { + routeCalculated(reply); + } else { + routeError(reply, reply->error(), reply->errorString()); + } + return; + } + + connect(routingManager, + SIGNAL(finished(QGeoRouteReply*)), + this, + SLOT(routeCalculated(QGeoRouteReply*))); + + connect(routingManager, + SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)), + this, + SLOT(routeError(QGeoRouteReply*,QGeoRouteReply::Error,QString))); + } + +private slots: + void routeCalculated(QGeoRouteReply *reply) + { + // A route request can ask for several alternative routes ... + if (reply->routes().size() != 0) { + + // ... but by default it will only get a single route + QGeoRoute route = reply->routes().at(0); + + //... now we have to make use of the route ... + } + + reply->deleteLater(); + } + + void routeError(QGeoRouteReply *reply, QGeoRouteReply:Error error, const QString &errorString) + { + // ... inform the user that an error has occurred ... + reply->deleteLater(); + } +}; + \endcode +*/ + +/*! + Constructs a new manager with the specified \a parent and with the + implementation provided by \a engine. + + This constructor is used internally by QGeoServiceProviderFactory. Regular + users should acquire instances of QGeoRoutingManager with + QGeoServiceProvider::routingManager(); +*/ +QGeoRoutingManager::QGeoRoutingManager(QGeoRoutingManagerEngine *engine, QObject *parent) + : QObject(parent), + d_ptr(new QGeoRoutingManagerPrivate()) +{ + d_ptr->engine = engine; + if (d_ptr->engine) { + d_ptr->engine->setParent(this); + + connect(d_ptr->engine, + SIGNAL(finished(QGeoRouteReply*)), + this, + SIGNAL(finished(QGeoRouteReply*))); + + connect(d_ptr->engine, + SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)), + this, + SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString))); + } else { + qFatal("The routing manager engine that was set for this routing manager was NULL."); + } +} + +/*! + Destroys this manager. +*/ +QGeoRoutingManager::~QGeoRoutingManager() +{ + delete d_ptr; +} + +/*! + Returns the name of the engine which implements the behaviour of this + routing manager. + + The combination of managerName() and managerVersion() should be unique + amongst the plugin implementations. +*/ +QString QGeoRoutingManager::managerName() const +{ + return d_ptr->engine->managerName(); +} + +/*! + Returns the version of the engine which implements the behaviour of this + routin manager. + + The combination of managerName() and managerVersion() should be unique + amongst the plugin implementations. +*/ +int QGeoRoutingManager::managerVersion() const +{ + return d_ptr->engine->managerVersion(); +} + +/*! + Begins the calculation of the route specified by \a request. + + A QGeoRouteReply object will be returned, which can be used to manage the + routing operation and to return the results of the operation. + + This manager and the returned QGeoRouteReply object will emit signals + indicating if the operation completes or if errors occur. + + Once the operation has completed, QGeoRouteReply::routes can be used to + retrieve the calculated route or routes. + + If \a request includes features which are not supported by this manager, as + reported by the methods in this manager, then a + QGeoRouteReply::UnsupportedOptionError will occur. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoRoutingManager::finished(), + QGeoRoutingManager::error(), QGeoRouteReply::finished() or + QGeoRouteReply::error() with deleteLater(). +*/ +QGeoRouteReply *QGeoRoutingManager::calculateRoute(const QGeoRouteRequest &request) +{ + return d_ptr->engine->calculateRoute(request); +} + +/*! + Begins the process of updating \a route based on the current position \a + position. + + A QGeoRouteReply object will be returned, which can be used to manage the + routing operation and to return the results of the operation. + + This manager and the returned QGeoRouteReply object will emit signals + indicating if the operation completes or if errors occur. + + If supportsRouteUpdates() returns false an + QGeoRouteReply::UnsupportedOptionError will occur. + + Once the operation has completed, QGeoRouteReply::routes can be used to + retrieve the updated route. + + The returned route could be entirely different to the original route, + especially if \a position is far away from the initial route. + Otherwise the route will be similar, although the remaining time and + distance will be updated and any segments of the original route which + have been traversed will be removed. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoRoutingManager::finished(), + QGeoRoutingManager::error(), QGeoRouteReply::finished() or + QGeoRouteReply::error() with deleteLater(). +*/ +QGeoRouteReply *QGeoRoutingManager::updateRoute(const QGeoRoute &route, const QGeoCoordinate &position) +{ + return d_ptr->engine->updateRoute(route, position); +} + +/*! + Returns the travel modes supported by this manager. +*/ +QGeoRouteRequest::TravelModes QGeoRoutingManager::supportedTravelModes() const +{ + return d_ptr->engine->supportedTravelModes(); +} + +/*! + Returns the types of features that this manager can take into account + during route planning. +*/ +QGeoRouteRequest::FeatureTypes QGeoRoutingManager::supportedFeatureTypes() const +{ + return d_ptr->engine->supportedFeatureTypes(); +} + +/*! + Returns the weightings which this manager can apply to different features + during route planning. +*/ +QGeoRouteRequest::FeatureWeights QGeoRoutingManager::supportedFeatureWeights() const +{ + return d_ptr->engine->supportedFeatureWeights(); +} + +/*! + Returns the route optimizations supported by this manager. +*/ +QGeoRouteRequest::RouteOptimizations QGeoRoutingManager::supportedRouteOptimizations() const +{ + return d_ptr->engine->supportedRouteOptimizations(); +} + +/*! + Returns the levels of detail for routing segments which can be requested + with this manager. +*/ +QGeoRouteRequest::SegmentDetails QGeoRoutingManager::supportedSegmentDetails() const +{ + return d_ptr->engine->supportedSegmentDetails(); +} + +/*! + Returns the levels of detail for navigation maneuvers which can be + requested by this manager. +*/ +QGeoRouteRequest::ManeuverDetails QGeoRoutingManager::supportedManeuverDetails() const +{ + return d_ptr->engine->supportedManeuverDetails(); +} + +/*! + Sets the locale to be used by this manager to \a locale. + + If this routing manager supports returning addresses and instructions + in different languages, they will be returned in the language of \a locale. + + The locale used defaults to the system locale if this is not set. +*/ +void QGeoRoutingManager::setLocale(const QLocale &locale) +{ + d_ptr->engine->setLocale(locale); +} + +/*! + Returns the locale used to hint to this routing manager about what + language to use for addresses and instructions. +*/ +QLocale QGeoRoutingManager::locale() const +{ + return d_ptr->engine->locale(); +} + +/*! + Sets the measurement system used by this manager to \a system. + + The measurement system can be set independently of the locale. Both setLocale() and this + function set the measurement system. The value set by the last function called will be used. + + \sa measurementSystem(), locale(), setLocale() +*/ +void QGeoRoutingManager::setMeasurementSystem(QLocale::MeasurementSystem system) +{ + d_ptr->engine->setMeasurementSystem(system); +} + +/*! + Returns the measurement system used by this manager. + + If setMeasurementSystem() has been called then the value returned by this function may be + different to that returned by locale().\l {QLocale::measurementSystem()}{measurementSystem()}. + In which case the value returned by this function is what will be used by the manager. + + \sa setMeasurementSystem(), setLocale() +*/ +QLocale::MeasurementSystem QGeoRoutingManager::measurementSystem() const +{ + return d_ptr->engine->measurementSystem(); +} + +/*! +\fn void QGeoRoutingManager::finished(QGeoRouteReply *reply) + +This signal is emitted when \a reply has finished processing. + +If reply::error() equals QGeoRouteReply::NoError then the processing +finished successfully. + +This signal and QGeoRouteReply::finished() will be emitted at the same time. + +\note Do not delete the \a reply object in the slot connected to this signal. +Use deleteLater() instead. +*/ + +/*! +\fn void QGeoRoutingManager::error(QGeoRouteReply *reply, QGeoRouteReply::Error error, QString errorString) + +This signal is emitted when an error has been detected in the processing of +\a reply. The QGeoRoutingManager::finished() signal will probably follow. + +The error will be described by the error code \a error. If \a errorString is +not empty it will contain a textual description of the error. + +This signal and QGeoRouteReply::error() will be emitted at the same time. + +\note Do not delete the \a reply object in the slot connected to this signal. +Use deleteLater() instead. +*/ + +/******************************************************************************* +*******************************************************************************/ + +QGeoRoutingManagerPrivate::QGeoRoutingManagerPrivate() + : engine(0) {} + +QGeoRoutingManagerPrivate::~QGeoRoutingManagerPrivate() +{ + delete engine; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeoroutingmanager.h b/src/location/maps/qgeoroutingmanager.h new file mode 100644 index 0000000..dd62238 --- /dev/null +++ b/src/location/maps/qgeoroutingmanager.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGER_H +#define QGEOROUTINGMANAGER_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRoutingManagerEngine; +class QGeoRoutingManagerPrivate; + +class Q_LOCATION_EXPORT QGeoRoutingManager : public QObject +{ + Q_OBJECT + +public: + ~QGeoRoutingManager(); + + QString managerName() const; + int managerVersion() const; + + QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request); + QGeoRouteReply *updateRoute(const QGeoRoute &route, const QGeoCoordinate &position); + + QGeoRouteRequest::TravelModes supportedTravelModes() const; + QGeoRouteRequest::FeatureTypes supportedFeatureTypes() const; + QGeoRouteRequest::FeatureWeights supportedFeatureWeights() const; + QGeoRouteRequest::RouteOptimizations supportedRouteOptimizations() const; + QGeoRouteRequest::SegmentDetails supportedSegmentDetails() const; + QGeoRouteRequest::ManeuverDetails supportedManeuverDetails() const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + void setMeasurementSystem(QLocale::MeasurementSystem system); + QLocale::MeasurementSystem measurementSystem() const; + +Q_SIGNALS: + void finished(QGeoRouteReply *reply); + void error(QGeoRouteReply *reply, QGeoRouteReply::Error error, QString errorString = QString()); + +private: + explicit QGeoRoutingManager(QGeoRoutingManagerEngine *engine, QObject *parent = nullptr); + + QGeoRoutingManagerPrivate *d_ptr; + Q_DISABLE_COPY(QGeoRoutingManager) + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroutingmanager_p.h b/src/location/maps/qgeoroutingmanager_p.h new file mode 100644 index 0000000..b3a9f2f --- /dev/null +++ b/src/location/maps/qgeoroutingmanager_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGER_P_H +#define QGEOROUTINGMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QGeoRoutingManagerEngine; + +class QGeoRoutingManagerPrivate +{ +public: + QGeoRoutingManagerPrivate(); + ~QGeoRoutingManagerPrivate(); + + QGeoRoutingManagerEngine *engine; + +private: + Q_DISABLE_COPY(QGeoRoutingManagerPrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroutingmanagerengine.cpp b/src/location/maps/qgeoroutingmanagerengine.cpp new file mode 100644 index 0000000..aa30705 --- /dev/null +++ b/src/location/maps/qgeoroutingmanagerengine.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutingmanagerengine.h" +#include "qgeoroutingmanagerengine_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoRoutingManagerEngine + \inmodule QtLocation + \ingroup QtLocation-impl + \since 5.6 + + \brief The QGeoRoutingManagerEngine class provides an interface and + convenience methods to implementers of QGeoServiceProvider plugins who want + to provide access to geographic routing information. + + Subclasses of QGeoRoutingManagerEngine need to provide an implementation of + calculateRoute(). + + In the default implementation, supportsRouteUpdates() returns false and + updateRoute() returns a QGeoRouteReply object containing a + QGeoRouteReply::UnsupportedOptionError. + + If the routing service supports updating routes as they are being + traveled, the subclass should provide an implementation of updateRoute() + and call setSupportsRouteUpdates(true) at some point in time before + updateRoute() is called. + + The function setSupportsRouteUpdates() is one of several functions which + configure the reported capabilities of the engine. If the capabilities + of an engine differ from the default values these functions should be + used so that the reported capabilities are accurate. + + It is important that this is done before calculateRoute(), updateRoute() + or any of the capability reporting functions are used to prevent + incorrect or inconsistent behavior. + + A subclass of QGeoRouteManagerEngine will often make use of a subclass + fo QGeoRouteReply internally, in order to add any engine-specific + data (such as a QNetworkReply object for network-based services) to the + QGeoRouteReply instances used by the engine. + + \sa QGeoRoutingManager +*/ + +/*! + Constructs a new engine with the specified \a parent, using \a parameters + to pass any implementation specific data to the engine. +*/ +QGeoRoutingManagerEngine::QGeoRoutingManagerEngine(const QVariantMap ¶meters, QObject *parent) + : QObject(parent), + d_ptr(new QGeoRoutingManagerEnginePrivate()) +{ + Q_UNUSED(parameters) +} + +/*! + Destroys this engine. +*/ +QGeoRoutingManagerEngine::~QGeoRoutingManagerEngine() +{ + delete d_ptr; +} + +/*! + Sets the name which this engine implementation uses to distinguish itself + from the implementations provided by other plugins to \a managerName. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +void QGeoRoutingManagerEngine::setManagerName(const QString &managerName) +{ + d_ptr->managerName = managerName; +} + +/*! + Returns the name which this engine implementation uses to distinguish + itself from the implementations provided by other plugins. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +QString QGeoRoutingManagerEngine::managerName() const +{ + return d_ptr->managerName; +} + +/*! + Sets the version of this engine implementation to \a managerVersion. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +void QGeoRoutingManagerEngine::setManagerVersion(int managerVersion) +{ + d_ptr->managerVersion = managerVersion; +} + +/*! + Returns the version of this engine implementation. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +int QGeoRoutingManagerEngine::managerVersion() const +{ + return d_ptr->managerVersion; +} + +/*! +\fn QGeoRouteReply *QGeoRoutingManagerEngine::calculateRoute(const QGeoRouteRequest &request) + + Begins the calculation of the route specified by \a request. + + A QGeoRouteReply object will be returned, which can be used to manage the + routing operation and to return the results of the operation. + + This engine and the returned QGeoRouteReply object will emit signals + indicating if the operation completes or if errors occur. + + Once the operation has completed, QGeoRouteReply::routes can be used to + retrieve the calculated route or routes. + + If \a request includes features which are not supported by this engine, as + reported by the methods in this engine, then a + QGeoRouteReply::UnsupportedOptionError will occur. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoRoutingManagerEngine::finished(), + QGeoRoutingManagerEngine::error(), QGeoRouteReply::finished() or + QGeoRouteReply::error() with deleteLater(). +*/ + +/*! + Begins the process of updating \a route based on the current position \a + position. + + A QGeoRouteReply object will be returned, which can be used to manage the + routing operation and to return the results of the operation. + + This engine and the returned QGeoRouteReply object will emit signals + indicating if the operation completes or if errors occur. + + If supportsRouteUpdates() returns false an + QGeoRouteReply::UnsupportedOptionError will occur. + + Once the operation has completed, QGeoRouteReply::routes can be used to + retrieve the updated route. + + The returned route could be entirely different to the original route, + especially if \a position is far enough away from the initial route. + Otherwise the route will be similar, although the remaining time and + distance will be updated and any segments of the original route which + have been traversed will be removed. + + The user is responsible for deleting the returned reply object, although + this can be done in the slot connected to QGeoRoutingManagerEngine::finished(), + QGeoRoutingManagerEngine::error(), QGeoRouteReply::finished() or + QGeoRouteReply::error() with deleteLater(). +*/ +QGeoRouteReply *QGeoRoutingManagerEngine::updateRoute(const QGeoRoute &route, const QGeoCoordinate &position) +{ + Q_UNUSED(route) + Q_UNUSED(position) + return new QGeoRouteReply(QGeoRouteReply::UnsupportedOptionError, + QLatin1String("The updating of routes is not supported by this service provider."), this); +} + +/*! + Sets the travel modes supported by this engine to \a travelModes. + + It is important that subclasses use this method to ensure that the engine + reports its capabilities correctly. If this function is not used the + engine will report that it supports no travel modes at all. +*/ +void QGeoRoutingManagerEngine::setSupportedTravelModes(QGeoRouteRequest::TravelModes travelModes) +{ + d_ptr->supportedTravelModes = travelModes; +} + +/*! + Returns the travel modes supported by this engine. +*/ +QGeoRouteRequest::TravelModes QGeoRoutingManagerEngine::supportedTravelModes() const +{ + return d_ptr->supportedTravelModes; +} + +/*! + Sets the types of features that this engine can take into account + during route planning to \a featureTypes. + + It is important that subclasses use this method to ensure that the engine + reports its capabilities correctly. If this function is not used the + engine will report that it supports no feature types at all. +*/ +void QGeoRoutingManagerEngine::setSupportedFeatureTypes(QGeoRouteRequest::FeatureTypes featureTypes) +{ + d_ptr->supportedFeatureTypes = featureTypes; +} + +/*! + Returns the types of features that this engine can take into account + during route planning. +*/ +QGeoRouteRequest::FeatureTypes QGeoRoutingManagerEngine::supportedFeatureTypes() const +{ + return d_ptr->supportedFeatureTypes; +} + +/*! + Sets the weightings which this engine can apply to different features + during route planning to \a featureWeights. + + It is important that subclasses use this method to ensure that the engine + reports its capabilities correctly. If this function is not used the + engine will report that it supports no feature weights at all. +*/ +void QGeoRoutingManagerEngine::setSupportedFeatureWeights(QGeoRouteRequest::FeatureWeights featureWeights) +{ + d_ptr->supportedFeatureWeights = featureWeights; + d_ptr->supportedFeatureWeights |= QGeoRouteRequest::NeutralFeatureWeight; +} + +/*! + Returns the weightings which this engine can apply to different features + during route planning. +*/ +QGeoRouteRequest::FeatureWeights QGeoRoutingManagerEngine::supportedFeatureWeights() const +{ + return d_ptr->supportedFeatureWeights; +} + +/*! + Sets the route optimizations supported by this engine to \a optimizations. + + It is important that subclasses use this method to ensure that the engine + reports its capabilities correctly. If this function is not used the + engine will report that it supports no route optimizations at all. +*/ +void QGeoRoutingManagerEngine::setSupportedRouteOptimizations(QGeoRouteRequest::RouteOptimizations optimizations) +{ + d_ptr->supportedRouteOptimizations = optimizations; +} + +/*! + Returns the route optimizations supported by this engine. +*/ +QGeoRouteRequest::RouteOptimizations QGeoRoutingManagerEngine::supportedRouteOptimizations() const +{ + return d_ptr->supportedRouteOptimizations; +} + +/*! + Sets the levels of detail for routing segments which can be + requested by this engine to \a segmentDetails. + + It is important that subclasses use this method to ensure that the engine + reports its capabilities correctly. If this function is not used the + engine will report that it supports no segment detail at all. +*/ +void QGeoRoutingManagerEngine::setSupportedSegmentDetails(QGeoRouteRequest::SegmentDetails segmentDetails) +{ + d_ptr->supportedSegmentDetails = segmentDetails; +} + +/*! + Returns the levels of detail for routing segments which can be + requested by this engine. +*/ +QGeoRouteRequest::SegmentDetails QGeoRoutingManagerEngine::supportedSegmentDetails() const +{ + return d_ptr->supportedSegmentDetails; +} + +/*! + Sets the levels of detail for navigation maneuvers which can be + requested by this engine to \a maneuverDetails. + + It is important that subclasses use this method to ensure that the engine + reports its capabilities correctly. If this function is not used the + engine will report that it supports no maneuver details at all. +*/ +void QGeoRoutingManagerEngine::setSupportedManeuverDetails(QGeoRouteRequest::ManeuverDetails maneuverDetails) +{ + d_ptr->supportedManeuverDetails = maneuverDetails; +} + +/*! + Returns the levels of detail for navigation maneuvers which can be + requested by this engine. +*/ +QGeoRouteRequest::ManeuverDetails QGeoRoutingManagerEngine::supportedManeuverDetails() const +{ + return d_ptr->supportedManeuverDetails; +} + +/*! + Sets the locale to be used by this manager to \a locale. + + If this routing manager supports returning addresses and instructions + in different languages, they will be returned in the language of \a locale. + + The locale used defaults to the system locale if this is not set. +*/ +void QGeoRoutingManagerEngine::setLocale(const QLocale &locale) +{ + d_ptr->locale = locale; + d_ptr->measurementSystem = locale.measurementSystem(); +} + +/*! + Returns the locale used to hint to this routing manager about what + language to use for addresses and instructions. +*/ +QLocale QGeoRoutingManagerEngine::locale() const +{ + return d_ptr->locale; +} + +/*! + Sets the measurement system used by this manager to \a system. + + The measurement system can be set independently of the locale. Both setLocale() and this + function set the measurement system. The value set by the last function called will be used. + + \sa measurementSystem(), locale(), setLocale() +*/ +void QGeoRoutingManagerEngine::setMeasurementSystem(QLocale::MeasurementSystem system) +{ + d_ptr->measurementSystem = system; +} + +/*! + Returns the measurement system used by this manager. + + If setMeasurementSystem() has been called then the value returned by this function may be + different to that returned by locale().\l {QLocale::measurementSystem()}{measurementSystem()}. + In which case the value returned by this function is what will be used by the manager. + + \sa setMeasurementSystem(), setLocale() +*/ +QLocale::MeasurementSystem QGeoRoutingManagerEngine::measurementSystem() const +{ + return d_ptr->measurementSystem; +} + +/*! +\fn void QGeoRoutingManagerEngine::finished(QGeoRouteReply *reply) + +This signal is emitted when \a reply has finished processing. + +If reply::error() equals QGeoRouteReply::NoError then the processing +finished successfully. + +This signal and QGeoRouteReply::finished() will be emitted at the same time. + +\note Do not delete the \a reply object in the slot connected to this signal. +Use deleteLater() instead. +*/ + +/*! +\fn void QGeoRoutingManagerEngine::error(QGeoRouteReply *reply, QGeoRouteReply::Error error, QString errorString) + +This signal is emitted when an error has been detected in the processing of +\a reply. The QGeoRoutingManagerEngine::finished() signal will probably follow. + +The error will be described by the error code \a error. If \a errorString is +not empty it will contain a textual description of the error. + +This signal and QGeoRouteReply::error() will be emitted at the same time. + +\note Do not delete the \a reply object in the slot connected to this signal. +Use deleteLater() instead. +*/ + +/******************************************************************************* +*******************************************************************************/ + +QGeoRoutingManagerEnginePrivate::QGeoRoutingManagerEnginePrivate() +: managerVersion(-1), measurementSystem(locale.measurementSystem()) +{ +} + +QGeoRoutingManagerEnginePrivate::~QGeoRoutingManagerEnginePrivate() {} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeoroutingmanagerengine.h b/src/location/maps/qgeoroutingmanagerengine.h new file mode 100644 index 0000000..18356b7 --- /dev/null +++ b/src/location/maps/qgeoroutingmanagerengine.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGERENGINE_H +#define QGEOROUTINGMANAGERENGINE_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRoutingManagerEnginePrivate; + +class Q_LOCATION_EXPORT QGeoRoutingManagerEngine : public QObject +{ + Q_OBJECT +public: + explicit QGeoRoutingManagerEngine(const QVariantMap ¶meters, QObject *parent = nullptr); + virtual ~QGeoRoutingManagerEngine(); + + QString managerName() const; + int managerVersion() const; + + virtual QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request) = 0; + virtual QGeoRouteReply *updateRoute(const QGeoRoute &route, const QGeoCoordinate &position); + + QGeoRouteRequest::TravelModes supportedTravelModes() const; + QGeoRouteRequest::FeatureTypes supportedFeatureTypes() const; + QGeoRouteRequest::FeatureWeights supportedFeatureWeights() const; + QGeoRouteRequest::RouteOptimizations supportedRouteOptimizations() const; + QGeoRouteRequest::SegmentDetails supportedSegmentDetails() const; + QGeoRouteRequest::ManeuverDetails supportedManeuverDetails() const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + void setMeasurementSystem(QLocale::MeasurementSystem system); + QLocale::MeasurementSystem measurementSystem() const; + +Q_SIGNALS: + void finished(QGeoRouteReply *reply); + void error(QGeoRouteReply *reply, QGeoRouteReply::Error error, QString errorString = QString()); + +protected: + void setSupportedTravelModes(QGeoRouteRequest::TravelModes travelModes); + void setSupportedFeatureTypes(QGeoRouteRequest::FeatureTypes featureTypes); + void setSupportedFeatureWeights(QGeoRouteRequest::FeatureWeights featureWeights); + void setSupportedRouteOptimizations(QGeoRouteRequest::RouteOptimizations optimizations); + void setSupportedSegmentDetails(QGeoRouteRequest::SegmentDetails segmentDetails); + void setSupportedManeuverDetails(QGeoRouteRequest::ManeuverDetails maneuverDetails); + +private: + void setManagerName(const QString &managerName); + void setManagerVersion(int managerVersion); + + QGeoRoutingManagerEnginePrivate *d_ptr; + Q_DISABLE_COPY(QGeoRoutingManagerEngine) + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoroutingmanagerengine_p.h b/src/location/maps/qgeoroutingmanagerengine_p.h new file mode 100644 index 0000000..7ba6c3d --- /dev/null +++ b/src/location/maps/qgeoroutingmanagerengine_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGERENGINE_P_H +#define QGEOROUTINGMANAGERENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeorouterequest.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRoutingManagerEnginePrivate +{ +public: + QGeoRoutingManagerEnginePrivate(); + ~QGeoRoutingManagerEnginePrivate(); + + QString managerName; + int managerVersion; + + QGeoRouteRequest::TravelModes supportedTravelModes; + QGeoRouteRequest::FeatureTypes supportedFeatureTypes; + QGeoRouteRequest::FeatureWeights supportedFeatureWeights; + QGeoRouteRequest::RouteOptimizations supportedRouteOptimizations; + QGeoRouteRequest::SegmentDetails supportedSegmentDetails; + QGeoRouteRequest::ManeuverDetails supportedManeuverDetails; + + QLocale locale; + QLocale::MeasurementSystem measurementSystem; + +private: + Q_DISABLE_COPY(QGeoRoutingManagerEnginePrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoserviceprovider.cpp b/src/location/maps/qgeoserviceprovider.cpp new file mode 100644 index 0000000..2d8151a --- /dev/null +++ b/src/location/maps/qgeoserviceprovider.cpp @@ -0,0 +1,780 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceprovider.h" +#include "qgeoserviceprovider_p.h" +#include "qgeoserviceproviderfactory.h" + +#include "qgeocodingmanager.h" +#include "qgeomappingmanager_p.h" +#include "qgeoroutingmanager.h" +#include "qplacemanager.h" +#include "qnavigationmanager_p.h" +#include "qgeocodingmanagerengine.h" +#include "qgeomappingmanagerengine_p.h" +#include "qgeoroutingmanagerengine.h" +#include "qplacemanagerengine.h" +#include "qplacemanagerengine_p.h" +#include "qnavigationmanagerengine_p.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + ("org.qt-project.qt.geoservice.serviceproviderfactory/5.0", + QLatin1String("/geoservices"))) + +/*! + \class QGeoServiceProvider + \inmodule QtLocation + \ingroup QtLocation-common + \since 5.6 + + \brief The QGeoServiceProvider class aggregates access to services which provide + geographical information. + + The Maps and Navigation API allows people to access various kinds of + geographical information, including functionality to perform geocoding, + routing and the display of maps. The QGeoServiceProvider aggregates the + access to a set of these services that are provided by a single vendor. + + It is possible to mix and match service providers for the various domains, + so that a geocoding manager from one service provider can be used with + a geographic routing manager from another service provider. + + This is not recommended unless the client is able to verify that the + data provided by the different services are compatible, as differences + in the underlying data sets could cause serious incongruences between + the services. + + Subclasses of QGeoServiceProvider guarantee that the different services + that they provide are interoperable. + + At this point there are two GeoServices plugins packaged with Qt. They are + accessible using their provider names: + + \list + \li "mapbox" -> \l {Qt Location Mapbox Plugin}{Mapbox service} + \li "here" -> \l {Qt Location HERE Plugin}{HERE Services} + \li "osm" -> \l {Qt Location Open Street Map Plugin}{OpenStreetMap Services} + \li "esri" -> \l {Qt Location Esri Plugin}{ESRI Services} + \endlist + + Each service provider must follow a naming convention for their service specific + parameter names/keys. They use the provider name as prefix for all their + parameter names. For example, the \l {Qt Location HERE Plugin}{HERE} service provider + requires the \c here.app_id parameter. When a provider is loaded only those parameters are + passed on whose parameter names start with the provider name. This avoids the sharing + sensitive parameters such as confidential \c token or \c app_id parameters with other + plugins. + + Please check the GeoServices plugin specific documentation to + obtain a complete list of the available parameter names/keys and values. +*/ + +/*! + \enum QGeoServiceProvider::Error + + Describes an error related to the loading and setup of a service provider plugin. + + \value NoError + No error has occurred. + \value NotSupportedError + The plugin does not support this functionality. + \value UnknownParameterError + The plugin did not recognize one of the parameters it was given. + \value MissingRequiredParameterError + The plugin did not find one of the parameters it was expecting. + \value ConnectionError + The plugin could not connect to its backend service or database. +*/ + +/*! + \enum QGeoServiceProvider::RoutingFeature + + Describes the routing features supported by the geo service provider. + + \value NoRoutingFeatures No routing features are supported. + \value OnlineRoutingFeature Online routing is supported. + \value OfflineRoutingFeature Offline routing is supported. + \value LocalizedRoutingFeature Supports returning routes with localized addresses and + instructions. + \value RouteUpdatesFeature Updating an existing route based on the current position is + supported. + \value AlternativeRoutesFeature Supports returning alternative routes. + \value ExcludeAreasRoutingFeature Supports specifying a areas which the returned route must + not cross. + \value AnyRoutingFeatures Matches a geo service provider that provides any routing + features. +*/ + +/*! + \enum QGeoServiceProvider::GeocodingFeature + + Describes the geocoding features supported by the geo service provider. + + \value NoGeocodingFeatures No geocoding features are supported. + \value OnlineGeocodingFeature Online geocoding is supported. + \value OfflineGeocodingFeature Offline geocoding is supported. + \value ReverseGeocodingFeature Reverse geocoding is supported. + \value LocalizedGeocodingFeature Supports returning geocoding results with localized + addresses. + \value AnyGeocodingFeatures Matches a geo service provider that provides any geocoding + features. +*/ + +/*! + \enum QGeoServiceProvider::MappingFeature + + Describes the mapping features supported by the geo service provider. + + \value NoMappingFeatures No mapping features are supported. + \value OnlineMappingFeature Online mapping is supported. + \value OfflineMappingFeature Offline mapping is supported. + \value LocalizedMappingFeature Supports returning localized map data. + \value AnyMappingFeatures Matches a geo service provider that provides any mapping + features. +*/ + +/*! + \enum QGeoServiceProvider::PlacesFeature + + Describes the places features supported by the geo service provider. + + \value NoPlacesFeatures No places features are supported. + \value OnlinePlacesFeature Online places is supported. + \value OfflinePlacesFeature Offline places is supported. + \value SavePlaceFeature Saving places is supported. + \value RemovePlaceFeature Removing or deleting places is supported. + \value SaveCategoryFeature Saving categories is supported. + \value RemoveCategoryFeature Removing or deleting categories is supported. + \value PlaceRecommendationsFeature Searching for recommended places similar to another place + is supported. + \value SearchSuggestionsFeature Search suggestions is supported. + \value LocalizedPlacesFeature Supports returning localized place data. + \value NotificationsFeature Notifications of place and category changes is supported. + \value PlaceMatchingFeature Supports matching places from two different geo service + providers. + \value AnyPlacesFeatures Matches a geo service provider that provides any places + features. +*/ + +/*! + \enum QGeoServiceProvider::NavigationFeature + + Describes the navigation features supported by the geo service provider. + + \value NoNavigationFeatures No navigation features are supported. + \value OnlineNavigationFeature Online navigation is supported. + \value OfflineNavigationFeature Offline navigation is supported. + \value AnyNavigationFeatures Matches a geo service provider that provides any navigation + features. +*/ + +/*! + Returns a list of names of the available service providers, for use with + the QGeoServiceProvider constructors. +*/ +QStringList QGeoServiceProvider::availableServiceProviders() +{ + return QGeoServiceProviderPrivate::plugins().keys(); +} + +/*! + Constructs a QGeoServiceProvider whose backend has the name \a + providerName, using the provided \a parameters. + + If multiple plugins have the same \a providerName, the plugin with the + highest reported providerVersion() will be used. + + If \a allowExperimental is true then plugins marked as experimental may be used. By default + experimental plugins are not considered. + + If no plugin matching \a providerName was able to be loaded then error() + and errorString() will provide details about why this is the case. + + \note Before the list of \a parameters is passed on to the to-be-loaded + provider plugin, the list is filtered to avoid the sharing of plugin specific + parameters with unrelated provider plugins. Plugin specific parameter + keys must be prefixed with the provider name (e.g. \c here.app_id). +*/ +QGeoServiceProvider::QGeoServiceProvider(const QString &providerName, + const QVariantMap ¶meters, + bool allowExperimental) + : d_ptr(new QGeoServiceProviderPrivate()) +{ + d_ptr->experimental = allowExperimental; + d_ptr->parameterMap = parameters; + // TODO Qt 6 Remove silent nokia rename + if (providerName == QStringLiteral("nokia")) + d_ptr->providerName = QStringLiteral("here"); + else + d_ptr->providerName = providerName; + d_ptr->loadMeta(); +} + +/*! + Destroys the service provider object. +*/ +QGeoServiceProvider::~QGeoServiceProvider() +{ + delete d_ptr; +} + +/* Template for the routingFeatures(), geocodingFeatures() etc methods. + * Ideally, the enumName would be a template parameter, but strings + * are not a valid const expr. :( */ +template +Flags QGeoServiceProviderPrivate::features(const char *enumName) +{ + const QMetaObject *mo = &QGeoServiceProvider::staticMetaObject; + const QMetaEnum en = mo->enumerator( + mo->indexOfEnumerator(enumName)); + + /* We need the typename keyword here, or Flags::enum_type will be parsed + * as a non-type and lead to an error */ + Flags ret = typename Flags::enum_type(0); + if (this->metaData.contains(QStringLiteral("Features")) + && this->metaData.value(QStringLiteral("Features")).isArray()) { + QJsonArray features = this->metaData.value(QStringLiteral("Features")).toArray(); + foreach (const QJsonValue &v, features) { + int val = en.keyToValue(v.toString().toLatin1().constData()); + if (v.isString() && val != -1) { + ret |= typename Flags::enum_type(val); + } + } + } + + return ret; +} + +/*! + Returns the routing features supported by the geo service provider. +*/ +QGeoServiceProvider::RoutingFeatures QGeoServiceProvider::routingFeatures() const +{ + return d_ptr->features("RoutingFeatures"); +} + +/*! + Returns the geocoding features supported by the geo service provider. +*/ +QGeoServiceProvider::GeocodingFeatures QGeoServiceProvider::geocodingFeatures() const +{ + return d_ptr->features("GeocodingFeatures"); +} + +/*! + Returns the mapping features supported by the geo service provider. +*/ +QGeoServiceProvider::MappingFeatures QGeoServiceProvider::mappingFeatures() const +{ + return d_ptr->features("MappingFeatures"); +} + +/*! + Returns the places features supported by the geo service provider. +*/ +QGeoServiceProvider::PlacesFeatures QGeoServiceProvider::placesFeatures() const +{ + return d_ptr->features("PlacesFeatures"); +} + +/*! + Returns the navigation features supported by the geo service provider. + + \since QtLocation 5.11 +*/ +QGeoServiceProvider::NavigationFeatures QGeoServiceProvider::navigationFeatures() const +{ + return d_ptr->features("NavigationFeatures"); +} + +/* Sadly, these are necessary to figure out which of the factory->createX + * methods we need to call. Ideally it would be nice to find a way to embed + * these into the manager() template. */ +template +Engine *createEngine(QGeoServiceProviderPrivate *) +{ + return 0; +} +template <> QGeoCodingManagerEngine *createEngine(QGeoServiceProviderPrivate *d_ptr) +{ + return d_ptr->factory->createGeocodingManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->geocodeError), &(d_ptr->geocodeErrorString)); +} +template <> QGeoRoutingManagerEngine *createEngine(QGeoServiceProviderPrivate *d_ptr) +{ + return d_ptr->factory->createRoutingManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->routingError), &(d_ptr->routingErrorString)); +} +template <> QGeoMappingManagerEngine *createEngine(QGeoServiceProviderPrivate *d_ptr) +{ + return d_ptr->factory->createMappingManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->mappingError), &(d_ptr->mappingErrorString)); +} +template <> QPlaceManagerEngine *createEngine(QGeoServiceProviderPrivate *d_ptr) +{ + return d_ptr->factory->createPlaceManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->placeError), &(d_ptr->placeErrorString)); +} +template <> QNavigationManagerEngine *createEngine(QGeoServiceProviderPrivate *d_ptr) +{ + if (!d_ptr->factoryV2) + return nullptr; + return d_ptr->factoryV2->createNavigationManagerEngine(d_ptr->cleanedParameterMap, &(d_ptr->placeError), &(d_ptr->placeErrorString)); +} + +/* Template for generating the code for each of the geocodingManager(), + * mappingManager() etc methods */ +template +Manager *QGeoServiceProviderPrivate::manager(QGeoServiceProvider::Error *_error, + QString *_errorString, Manager **_manager) +{ + // make local references just so this method is easier to read + QGeoServiceProvider::Error &error = *_error; + QString &errorString = *_errorString; + Manager *&manager = *_manager; + + if (!this->factory) { + this->filterParameterMap(); + this->loadPlugin(this->parameterMap); + } + + if (!this->factory || error != QGeoServiceProvider::NoError) + return 0; + + if (!manager) { + Engine *engine = createEngine(this); + + if (engine) { + engine->setManagerName( + this->metaData.value(QStringLiteral("Provider")).toString()); + engine->setManagerVersion( + int(this->metaData.value(QStringLiteral("Version")).toDouble())); + manager = new Manager(engine); + } else if (error == QGeoServiceProvider::NoError) { + error = QGeoServiceProvider::NotSupportedError; + errorString = QLatin1String("The service provider does not support the "); + errorString.append(QLatin1String(Manager::staticMetaObject.className())); + errorString.append(QLatin1String(" type.")); + } + + if (error != QGeoServiceProvider::NoError) { + delete manager; + manager = 0; + this->error = error; + this->errorString = errorString; + } + + if (manager && this->localeSet) + manager->setLocale(this->locale); + } + + if (manager) { + this->error = QGeoServiceProvider::NoError; + this->errorString.clear(); + } + + return manager; +} + +/*! + Returns the QGeoCodingManager made available by the service + provider. + + This function will return 0 if the service provider does not provide + any geocoding services. + + This function will attempt to construct a QGeoCodingManager instance + when it is called for the first time. If the attempt is successful the + QGeoCodingManager will be cached, otherwise each call of this function + will attempt to construct a QGeoCodingManager instance until the + construction is successful. + + The QGeoCodingManager is owned by this QGeoServiceProvider and should not + be deleted separately. Users should assume that deleting the + QGeoServiceProvider renders the pointer returned by this method invalid. + + After this function has been called, error() and errorString() will + report any errors which occurred during the construction of the + QGeoCodingManager. +*/ +QGeoCodingManager *QGeoServiceProvider::geocodingManager() const +{ + return d_ptr->manager( + &(d_ptr->geocodeError), &(d_ptr->geocodeErrorString), + &(d_ptr->geocodingManager)); +} + +/*! + Returns the QGeoMappingManager made available by the service provider. + + This function will return 0 if the service provider does not provide + any mapping services. + + This function will attempt to construct a QGeoMappingManager instance + when it is called for the first time. If the attempt is successful the + QGeoMappingManager will be cached, otherwise each call of this function + will attempt to construct a QGeoMappingManager instance until the + construction is successful. + + The QGeoMappingManager is owned by this QGeoServiceProvider and should not + be deleted separately. Users should assume that deleting the + QGeoServiceProvider renders the pointer returned by this method invalid. + + After this function has been called, error() and errorString() will + report any errors which occurred during the construction of the + QGeoMappingManager. + + \internal +*/ +QGeoMappingManager *QGeoServiceProvider::mappingManager() const +{ + return d_ptr->manager( + &(d_ptr->mappingError), &(d_ptr->mappingErrorString), + &(d_ptr->mappingManager)); +} + +/*! + Returns the QGeoRoutingManager made available by the service provider. + + This function will return 0 if the service provider does not provide + any geographic routing services. + + This function will attempt to construct a QGeoRoutingManager instance + when it is called for the first time. If the attempt is successful the + QGeoRoutingManager will be cached, otherwise each call of this function + will attempt to construct a QGeoRoutingManager instance until the + construction is successful. + + The QGeoRoutingManager is owned by this QGeoServiceProvider and should not + be deleted separately. Users should assume that deleting the + QGeoServiceProvider renders the pointer returned by this method invalid. + + After this function has been called, error() and errorString() will + report any errors which occurred during the construction of the + QGeoRoutingManager. +*/ +QGeoRoutingManager *QGeoServiceProvider::routingManager() const +{ + return d_ptr->manager( + &(d_ptr->routingError), &(d_ptr->routingErrorString), + &(d_ptr->routingManager)); +} + +/*! + Returns the QPlaceManager made available by the service provider. + + This function will attempt to construct a QPlaceManager instance + when it is called for the first time. If the attempt is successful the + QPlaceManager will be cached, otherwise each call of this function + will attempt to construct a QPlace instance until the + construction is successful. + + The QGeoPlaceManager is owned by this QGeoServiceProvider and should not + be deleted separately. Users should assume that deleting the + QGeoServiceProvider renders the pointer returned by this method invalid. + + After this function has been called, error() and errorString() will + report any errors which occurred during the construction of the QPlaceManager. +*/ +QPlaceManager *QGeoServiceProvider::placeManager() const +{ + return d_ptr->manager( + &(d_ptr->placeError), &(d_ptr->placeErrorString), + &(d_ptr->placeManager)); +} + +/*! + Returns a new QNavigationManager made available by the service provider. + + After this function has been called, error() and errorString() will + report any errors which occurred during the construction of the QNavigationManagerEngine. +*/ +QNavigationManager *QGeoServiceProvider::navigationManager() const +{ + return d_ptr->manager( + &(d_ptr->navigationError), &(d_ptr->navigationErrorString), + &(d_ptr->navigationManager)); +} + +/*! + Returns an error code describing the error which occurred during the + last operation that was performed by this class. +*/ +QGeoServiceProvider::Error QGeoServiceProvider::error() const +{ + return d_ptr->error; +} + +/*! + Returns a string describing the error which occurred during the + last operation that was performed by this class. +*/ +QString QGeoServiceProvider::errorString() const +{ + return d_ptr->errorString; +} + +/*! + Sets whether experimental plugins are considered when locating the + correct plugin library for this service provider to \a allow. + + \b {Important:} this will destroy any existing managers held by this + service provider instance. You should be sure not to attempt to use any + pointers that you have previously retrieved after calling this method. +*/ +void QGeoServiceProvider::setAllowExperimental(bool allow) +{ + d_ptr->experimental = allow; + d_ptr->unload(); + d_ptr->loadMeta(); +} + +/*! + Sets the parameters used to construct individual manager classes for + this service provider to \a parameters. + + Before the list of \a parameters is passed on to the to-be-loaded + service provider, the list is filtered to avoid the sharing of provider specific + parameters with unrelated service providers. Provider specific parameter + keys must be prefixed with the provider name (e.g. \c here.app_id). + + \b {Important:} this will destroy any existing managers held by this + service provider instance. You should be sure not to attempt to use any + pointers that you have previously retrieved after calling this method. +*/ +void QGeoServiceProvider::setParameters(const QVariantMap ¶meters) +{ + d_ptr->parameterMap = parameters; + d_ptr->unload(); + d_ptr->loadMeta(); +} + +/*! + Sets the locale used by this service provider to \a locale. If the relevant features + (see LocalizedMappingFeature etc), this will change the languages, units + and other locale-specific attributes of the provider's data. +*/ +void QGeoServiceProvider::setLocale(const QLocale &locale) +{ + d_ptr->locale = locale; + d_ptr->localeSet = true; + + if (d_ptr->geocodingManager) + d_ptr->geocodingManager->setLocale(locale); + if (d_ptr->routingManager) + d_ptr->routingManager->setLocale(locale); + if (d_ptr->mappingManager) + d_ptr->mappingManager->setLocale(locale); + if (d_ptr->placeManager) + d_ptr->placeManager->setLocale(locale); + if (d_ptr->navigationManager) + d_ptr->navigationManager->setLocale(locale); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoServiceProviderPrivate::QGeoServiceProviderPrivate() + : factory(0), + experimental(false), + geocodingManager(0), + routingManager(0), + mappingManager(0), + placeManager(0), + geocodeError(QGeoServiceProvider::NoError), + routingError(QGeoServiceProvider::NoError), + mappingError(QGeoServiceProvider::NoError), + placeError(QGeoServiceProvider::NoError), + error(QGeoServiceProvider::NoError), + localeSet(false) +{ + metaData.insert(QStringLiteral("index"), -1); +} + +QGeoServiceProviderPrivate::~QGeoServiceProviderPrivate() +{ + delete geocodingManager; + delete routingManager; + delete mappingManager; + delete placeManager; + delete navigationManager; +} + +void QGeoServiceProviderPrivate::unload() +{ + delete geocodingManager; + geocodingManager = 0; + + delete routingManager; + routingManager = 0; + + delete mappingManager; + mappingManager = 0; + + delete placeManager; + placeManager = 0; + + delete navigationManager; + navigationManager = nullptr; + + factory = factoryV2 = nullptr; + error = QGeoServiceProvider::NoError; + errorString = QLatin1String(""); + metaData = QJsonObject(); + metaData.insert(QStringLiteral("index"), -1); +} + +/* Filter out any parameter that doesn't match any plugin */ +void QGeoServiceProviderPrivate::filterParameterMap() +{ + const auto availablePlugins = QGeoServiceProviderPrivate::plugins(); + + cleanedParameterMap = parameterMap; + for (auto it = availablePlugins.keyBegin(), end = availablePlugins.keyEnd(); it != end; ++it) { + if (*it == providerName) // don't remove parameters for current provider + continue; + + QVariantMap::iterator i = cleanedParameterMap.begin(); + while (i != cleanedParameterMap.end()) { + // remove every parameter meant for other plugins + if (i.key().startsWith(QString(*it + QLatin1Char('.')))) + i = cleanedParameterMap.erase(i); + else + ++i; + } + } +} + +void QGeoServiceProviderPrivate::loadMeta() +{ + factory = factoryV2 = nullptr; + metaData = QJsonObject(); + metaData.insert(QStringLiteral("index"), -1); + error = QGeoServiceProvider::NotSupportedError; + errorString = QString(QLatin1String("The geoservices provider %1 is not supported.")).arg(providerName); + + QList candidates = QGeoServiceProviderPrivate::plugins().values(providerName); + + int versionFound = -1; + int idx = -1; + + // figure out which version of the plugin we want + // (always latest unless experimental) + for (int i = 0; i < candidates.size(); ++i) { + QJsonObject meta = candidates[i]; + if (meta.contains(QStringLiteral("Version")) + && meta.value(QStringLiteral("Version")).isDouble() + && meta.contains(QStringLiteral("Experimental")) + && meta.value(QStringLiteral("Experimental")).isBool()) { + int ver = int(meta.value(QStringLiteral("Version")).toDouble()); + if (ver > versionFound && !(!experimental && meta.value(QStringLiteral("Experimental")).toBool())) { + versionFound = ver; + idx = i; + } + } + } + + if (idx != -1) { + error = QGeoServiceProvider::NoError; + errorString = QStringLiteral(""); + metaData = candidates[idx]; + } +} + +void QGeoServiceProviderPrivate::loadPlugin(const QVariantMap ¶meters) +{ + Q_UNUSED(parameters) + + if (int(metaData.value(QStringLiteral("index")).toDouble()) < 0) { + error = QGeoServiceProvider::NotSupportedError; + errorString = QString(QLatin1String("The geoservices provider is not supported.")); + factory = factoryV2 = nullptr; + return; + } + + error = QGeoServiceProvider::NoError; + errorString = QLatin1String(""); + + int idx = int(metaData.value(QStringLiteral("index")).toDouble()); + + // load the actual plugin + QObject *instance = loader()->instance(idx); + factoryV2 = qobject_cast(instance); + if (!factoryV2) + factory = qobject_cast(instance); + else + factory = factoryV2; +} + +QHash QGeoServiceProviderPrivate::plugins(bool reload) +{ + static QHash plugins; + static bool alreadyDiscovered = false; + + if (reload == true) + alreadyDiscovered = false; + + if (!alreadyDiscovered) { + loadPluginMetadata(plugins); + alreadyDiscovered = true; + } + return plugins; +} + +void QGeoServiceProviderPrivate::loadPluginMetadata(QHash &list) +{ + QFactoryLoader *l = loader(); + QList meta = l->metaData(); + for (int i = 0; i < meta.size(); ++i) { + QJsonObject obj = meta.at(i).value(QStringLiteral("MetaData")).toObject(); + obj.insert(QStringLiteral("index"), i); + list.insertMulti(obj.value(QStringLiteral("Provider")).toString(), obj); + } +} + + +QT_END_NAMESPACE + diff --git a/src/location/maps/qgeoserviceprovider.h b/src/location/maps/qgeoserviceprovider.h new file mode 100644 index 0000000..640e984 --- /dev/null +++ b/src/location/maps/qgeoserviceprovider.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_H +#define QGEOSERVICEPROVIDER_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QLocale; +class QStringList; +class QGeoCodingManager; +class QGeoMappingManager; +class QGeoRoutingManager; +class QPlaceManager; +class QNavigationManager; +class QGeoCodingManagerEngine; +class QGeoMappingManagerEngine; +class QGeoRoutingManagerEngine; +class QPlaceManagerEngine; +class QNavigationManagerEngine; +class QGeoServiceProviderPrivate; + +class Q_LOCATION_EXPORT QGeoServiceProvider : public QObject +{ + Q_OBJECT + Q_ENUMS(Error) +public: + enum Error { + NoError, + NotSupportedError, + UnknownParameterError, + MissingRequiredParameterError, + ConnectionError + }; + + enum RoutingFeature { + NoRoutingFeatures = 0, + OnlineRoutingFeature = (1<<0), + OfflineRoutingFeature = (1<<1), + LocalizedRoutingFeature = (1<<2), + RouteUpdatesFeature = (1<<3), + AlternativeRoutesFeature = (1<<4), + ExcludeAreasRoutingFeature = (1<<5), + AnyRoutingFeatures = ~(0) + }; + + enum GeocodingFeature { + NoGeocodingFeatures = 0, + OnlineGeocodingFeature = (1<<0), + OfflineGeocodingFeature = (1<<1), + ReverseGeocodingFeature = (1<<2), + LocalizedGeocodingFeature = (1<<3), + AnyGeocodingFeatures = ~(0) + }; + + enum MappingFeature { + NoMappingFeatures = 0, + OnlineMappingFeature = (1<<0), + OfflineMappingFeature = (1<<1), + LocalizedMappingFeature = (1<<2), + AnyMappingFeatures = ~(0) + }; + + enum PlacesFeature { + NoPlacesFeatures = 0, + OnlinePlacesFeature = (1<<0), + OfflinePlacesFeature = (1<<1), + SavePlaceFeature = (1<<2), + RemovePlaceFeature = (1<<3), + SaveCategoryFeature = (1<<4), + RemoveCategoryFeature = (1<<5), + PlaceRecommendationsFeature = (1<<6), + SearchSuggestionsFeature = (1<<7), + LocalizedPlacesFeature = (1<<8), + NotificationsFeature = (1<<9), + PlaceMatchingFeature = (1<<10), + AnyPlacesFeatures = ~(0) + }; + + enum NavigationFeature { + NoNavigationFeatures = 0, + OnlineNavigationFeature = (1<<0), + OfflineNavigationFeature = (1<<1), + AnyNavigationFeatures = ~(0) + }; + + Q_DECLARE_FLAGS(RoutingFeatures, RoutingFeature) + Q_FLAGS(RoutingFeatures) + + Q_DECLARE_FLAGS(GeocodingFeatures, GeocodingFeature) + Q_FLAGS(GeocodingFeatures) + + Q_DECLARE_FLAGS(MappingFeatures, MappingFeature) + Q_FLAGS(MappingFeatures) + + Q_DECLARE_FLAGS(PlacesFeatures, PlacesFeature) + Q_FLAGS(PlacesFeatures) + + Q_DECLARE_FLAGS(NavigationFeatures, NavigationFeature) + Q_FLAGS(NavigationFeatures) + + static QStringList availableServiceProviders(); + QGeoServiceProvider(const QString &providerName, + const QVariantMap ¶meters = QVariantMap(), + bool allowExperimental = false); + + ~QGeoServiceProvider(); + + RoutingFeatures routingFeatures() const; + GeocodingFeatures geocodingFeatures() const; + MappingFeatures mappingFeatures() const; + PlacesFeatures placesFeatures() const; + NavigationFeatures navigationFeatures() const; + + QGeoCodingManager *geocodingManager() const; + QGeoMappingManager *mappingManager() const; + QGeoRoutingManager *routingManager() const; + QPlaceManager *placeManager() const; + QNavigationManager *navigationManager() const; + + Error error() const; + QString errorString() const; + + void setParameters(const QVariantMap ¶meters); + void setLocale(const QLocale &locale); + void setAllowExperimental(bool allow); + +private: + QGeoServiceProviderPrivate *d_ptr; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoServiceProvider::RoutingFeatures) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoServiceProvider::GeocodingFeatures) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoServiceProvider::MappingFeatures) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoServiceProvider::PlacesFeatures) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoServiceProvider::NavigationFeatures) + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoserviceprovider_p.h b/src/location/maps/qgeoserviceprovider_p.h new file mode 100644 index 0000000..11b86ba --- /dev/null +++ b/src/location/maps/qgeoserviceprovider_p.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_P_H +#define QGEOSERVICEPROVIDER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeoserviceprovider.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCodingManager; +class QGeoRoutingManager; +class QGeoMappingManager; + +class QGeoServiceProviderFactory; +class QGeoServiceProviderFactoryV2; + +class QGeoServiceProviderPrivate +{ +public: + QGeoServiceProviderPrivate(); + ~QGeoServiceProviderPrivate(); + + void loadMeta(); + void loadPlugin(const QVariantMap ¶meters); + void unload(); + void filterParameterMap(); + + /* helper templates for generating the feature and manager accessors */ + template + Manager *manager(QGeoServiceProvider::Error *error, + QString *errorString, Manager **manager); + template + Flags features(const char *enumName); + + QGeoServiceProviderFactory *factory; + QGeoServiceProviderFactoryV2 *factoryV2 = nullptr; + QJsonObject metaData; + + QVariantMap parameterMap; + QVariantMap cleanedParameterMap; + + bool experimental; + + QGeoCodingManager *geocodingManager; + QGeoRoutingManager *routingManager; + QGeoMappingManager *mappingManager; + QPlaceManager *placeManager; + QNavigationManager *navigationManager = nullptr; + + QGeoServiceProvider::Error geocodeError; + QGeoServiceProvider::Error routingError; + QGeoServiceProvider::Error mappingError; + QGeoServiceProvider::Error placeError; + QGeoServiceProvider::Error navigationError = QGeoServiceProvider::NoError; + + QString geocodeErrorString; + QString routingErrorString; + QString mappingErrorString; + QString placeErrorString; + QString navigationErrorString; + + QGeoServiceProvider::Error error; + QString errorString; + + QString providerName; + + QLocale locale; + bool localeSet; + + static QHash plugins(bool reload = false); + static void loadPluginMetadata(QHash &list); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeoserviceproviderfactory.cpp b/src/location/maps/qgeoserviceproviderfactory.cpp new file mode 100644 index 0000000..44ed353 --- /dev/null +++ b/src/location/maps/qgeoserviceproviderfactory.cpp @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderfactory.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoServiceProviderFactory + \inmodule QtLocation + \ingroup QtLocation-impl + \since 5.6 + \deprecated + + \brief The QGeoServiceProviderFactory class is a factory class used as the + plugin interface for services related to geographical information. + + Implementers must provide a unique combination of providerName() and + providerVersion() per plugin. + + The other functions should be overridden if the plugin supports the + associated set of functionality. + + \sa QGeoServiceProviderFactoryV2 +*/ + +/*! +\fn QGeoServiceProviderFactory::~QGeoServiceProviderFactory() + +Destroys this QGeoServiceProviderFactory instance. +*/ + +/*! + Returns a new QGeoCodingManagerEngine instance, initialized with \a + parameters, which implements the location geocoding functionality. + + If \a error is not 0 it should be set to QGeoServiceProvider::NoError on + success or an appropriate QGeoServiceProvider::Error on failure. + + If \a errorString is not 0 it should be set to a string describing any + error which occurred. + + The default implementation returns 0, which causes a + QGeoServiceProvider::NotSupportedError in QGeoServiceProvider. +*/ +QGeoCodingManagerEngine *QGeoServiceProviderFactory::createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +/*! + Returns a new QGeoMappingManagerEngine instance, initialized with \a + parameters, which implements mapping functionality. + + If \a error is not 0 it should be set to QGeoServiceProvider::NoError on + success or an appropriate QGeoServiceProvider::Error on failure. + + If \a errorString is not 0 it should be set to a string describing any + error which occurred. + + The default implementation returns 0, which causes a + QGeoServiceProvider::NotSupportedError in QGeoServiceProvider. + + \internal +*/ +QGeoMappingManagerEngine *QGeoServiceProviderFactory::createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +/*! + Returns a new QGeoRoutingManagerEngine instance, initialized with \a + parameters, which implements routing functionality. + + If \a error is not 0 it should be set to QGeoServiceProvider::NoError on + success or an appropriate QGeoServiceProvider::Error on failure. + + If \a errorString is not 0 it should be set to a string describing any + error which occurred. + + The default implementation returns 0, which causes a + QGeoServiceProvider::NotSupportedError in QGeoServiceProvider. +*/ +QGeoRoutingManagerEngine *QGeoServiceProviderFactory::createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const + +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +/*! + Returns a new QPlaceManagerEngine instance, initialized with \a + parameters, which implements the place searching functionality. + + If \a error is not 0 it should be set to QGeoServiceProvider::NoError on + success or an appropriate QGeoServiceProvider::Error on failure. + + If \a errorString is not 0 it should be set to a string describing any + error which occurred. + + The default implementation returns 0, which causes a + QGeoServiceProvider::NotSupportedError in QGeoServiceProvider. +*/ +QPlaceManagerEngine *QGeoServiceProviderFactory::createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const + +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +/*! + \class QGeoServiceProviderFactoryV2 + \inmodule QtLocation + \ingroup QtLocation-impl + \since 5.11 + + \brief The QGeoServiceProviderFactoryV2 class is a factory class used as the + plugin interface for services related to geographical information. + + Implementers must provide a unique combination of providerName() and + providerVersion() per plugin. + + The other functions should be overridden if the plugin supports the + associated set of functionality. +*/ + +/*! +\fn QGeoServiceProviderFactoryV2::~QGeoServiceProviderFactoryV2() + +Destroys this QGeoServiceProviderFactoryV2 instance. +*/ + +/*! + Returns a new QNavigationManagerEngine instance, initialized with \a + parameters, which implements navigation functionality. + + If \a error is not nullptr, it should be set to QGeoServiceProvider::NoError on + success or an appropriate QGeoServiceProvider::Error on failure. + + If \a errorString is not nullptr, it should be set to a string describing any + error which occurred. + + The default implementation returns nullptr, which causes a + QGeoServiceProvider::NotSupportedError in QGeoServiceProvider. +*/ +QNavigationManagerEngine *QGeoServiceProviderFactoryV2::createNavigationManagerEngine(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeoserviceproviderfactory.h b/src/location/maps/qgeoserviceproviderfactory.h new file mode 100644 index 0000000..1eb93a1 --- /dev/null +++ b/src/location/maps/qgeoserviceproviderfactory.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDERFACTORY_H +#define QGEOSERVICEPROVIDERFACTORY_H + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_EXPORT QGeoServiceProviderFactory +{ +public: + virtual ~QGeoServiceProviderFactory() {} + + virtual QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + virtual QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + virtual QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + virtual QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +Q_DECLARE_INTERFACE(QGeoServiceProviderFactory, + "org.qt-project.qt.geoservice.serviceproviderfactory/5.0") + +class Q_LOCATION_EXPORT QGeoServiceProviderFactoryV2 : public QGeoServiceProviderFactory +{ +public: + virtual ~QGeoServiceProviderFactoryV2() {} + + virtual QNavigationManagerEngine *createNavigationManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +// Although not actually used for constructing a specialized loader, this is required for +// casting a QObject * into QGeoServiceProviderFactoryV2 * +Q_DECLARE_INTERFACE(QGeoServiceProviderFactoryV2, + "org.qt-project.qt.geoservice.serviceproviderfactoryV2/5.0") + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeotiledmap.cpp b/src/location/maps/qgeotiledmap.cpp new file mode 100644 index 0000000..a1231cf --- /dev/null +++ b/src/location/maps/qgeotiledmap.cpp @@ -0,0 +1,427 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeotiledmap_p.h" +#include "qgeotiledmap_p_p.h" +#include +#include "qgeotiledmappingmanagerengine_p.h" +#include "qabstractgeotilecache_p.h" +#include "qgeotilespec_p.h" +#include "qgeoprojection_p.h" + +#include "qgeocameratiles_p.h" +#include "qgeotilerequestmanager_p.h" +#include "qgeotiledmapscene_p.h" +#include "qgeocameracapabilities_p.h" +#include + +QT_BEGIN_NAMESPACE +#define PREFETCH_FRUSTUM_SCALE 2.0 + +static const double invLog2 = 1.0 / std::log(2.0); + +static double zoomLevelFrom256(double zoomLevelFor256, double tileSize) +{ + return std::log( std::pow(2.0, zoomLevelFor256) * 256.0 / tileSize ) * invLog2; +} + +QGeoTiledMap::QGeoTiledMap(QGeoTiledMappingManagerEngine *engine, QObject *parent) + : QGeoMap(*new QGeoTiledMapPrivate(engine), parent) +{ + Q_D(QGeoTiledMap); + + d->m_tileRequests = new QGeoTileRequestManager(this, engine); + + QObject::connect(engine,&QGeoTiledMappingManagerEngine::tileVersionChanged, + this,&QGeoTiledMap::handleTileVersionChanged); + QObject::connect(this, &QGeoMap::cameraCapabilitiesChanged, + [d](const QGeoCameraCapabilities &oldCameraCapabilities) { + d->onCameraCapabilitiesChanged(oldCameraCapabilities); + }); +} + +QGeoTiledMap::QGeoTiledMap(QGeoTiledMapPrivate &dd, QGeoTiledMappingManagerEngine *engine, QObject *parent) + : QGeoMap(dd, parent) +{ + Q_D(QGeoTiledMap); + + d->m_tileRequests = new QGeoTileRequestManager(this, engine); + + QObject::connect(engine,&QGeoTiledMappingManagerEngine::tileVersionChanged, + this,&QGeoTiledMap::handleTileVersionChanged); + QObject::connect(this, &QGeoMap::cameraCapabilitiesChanged, + [d](const QGeoCameraCapabilities &oldCameraCapabilities) { + d->onCameraCapabilitiesChanged(oldCameraCapabilities); + }); +} + +QGeoTiledMap::~QGeoTiledMap() +{ + Q_D(QGeoTiledMap); + delete d->m_tileRequests; + d->m_tileRequests = 0; + + if (!d->m_engine.isNull()) { + QGeoTiledMappingManagerEngine *engine = qobject_cast(d->m_engine); + Q_ASSERT(engine); + engine->releaseMap(this); + } +} + +QGeoTileRequestManager *QGeoTiledMap::requestManager() +{ + Q_D(QGeoTiledMap); + return d->m_tileRequests; +} + +void QGeoTiledMap::updateTile(const QGeoTileSpec &spec) +{ + Q_D(QGeoTiledMap); + d->updateTile(spec); +} + +void QGeoTiledMap::setPrefetchStyle(QGeoTiledMap::PrefetchStyle style) +{ + Q_D(QGeoTiledMap); + d->m_prefetchStyle = style; +} + +QAbstractGeoTileCache *QGeoTiledMap::tileCache() +{ + Q_D(QGeoTiledMap); + return d->m_cache; +} + +QSGNode *QGeoTiledMap::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window) +{ + Q_D(QGeoTiledMap); + return d->updateSceneGraph(oldNode, window); +} + +void QGeoTiledMap::prefetchData() +{ + Q_D(QGeoTiledMap); + d->prefetchTiles(); +} + +void QGeoTiledMap::clearData() +{ + Q_D(QGeoTiledMap); + d->m_cache->clearAll(); + d->m_mapScene->clearTexturedTiles(); + d->updateScene(); + sgNodeChanged(); +} + +QGeoMap::Capabilities QGeoTiledMap::capabilities() const +{ + return Capabilities(SupportsVisibleRegion + | SupportsSetBearing + | SupportsAnchoringCoordinate); +} + +void QGeoTiledMap::setCopyrightVisible(bool visible) +{ + Q_D(QGeoTiledMap); + if (visible == d->m_copyrightVisible) + return; + + QGeoMap::setCopyrightVisible(visible); + if (visible) + evaluateCopyrights(d->m_mapScene->visibleTiles()); +} + +void QGeoTiledMap::clearScene(int mapId) +{ + Q_D(QGeoTiledMap); + if (activeMapType().mapId() == mapId) + d->clearScene(); +} + +void QGeoTiledMap::handleTileVersionChanged() +{ + Q_D(QGeoTiledMap); + if (!d->m_engine.isNull()) { + QGeoTiledMappingManagerEngine* engine = qobject_cast(d->m_engine); + Q_ASSERT(engine); + d->changeTileVersion(engine->tileVersion()); + } +} + +void QGeoTiledMap::evaluateCopyrights(const QSet &visibleTiles) +{ + Q_UNUSED(visibleTiles); +} + +QGeoTiledMapPrivate::QGeoTiledMapPrivate(QGeoTiledMappingManagerEngine *engine) + : QGeoMapPrivate(engine, new QGeoProjectionWebMercator), + m_cache(engine->tileCache()), + m_visibleTiles(new QGeoCameraTiles()), + m_prefetchTiles(new QGeoCameraTiles()), + m_mapScene(new QGeoTiledMapScene()), + m_tileRequests(0), + m_maxZoomLevel(static_cast(std::ceil(m_cameraCapabilities.maximumZoomLevel()))), + m_minZoomLevel(static_cast(std::ceil(m_cameraCapabilities.minimumZoomLevel()))), + m_prefetchStyle(QGeoTiledMap::PrefetchTwoNeighbourLayers) +{ + int tileSize = m_cameraCapabilities.tileSize(); + QString pluginString(engine->managerName() + QLatin1Char('_') + QString::number(engine->managerVersion())); + m_visibleTiles->setTileSize(tileSize); + m_prefetchTiles->setTileSize(tileSize); + m_visibleTiles->setPluginString(pluginString); + m_prefetchTiles->setPluginString(pluginString); + m_mapScene->setTileSize(tileSize); +} + +QGeoTiledMapPrivate::~QGeoTiledMapPrivate() +{ + // controller_ is a child of map_, don't need to delete it here + + delete m_mapScene; + delete m_visibleTiles; + delete m_prefetchTiles; + + // TODO map items are not deallocated! + // However: how to ensure this is done in rendering thread? +} + +void QGeoTiledMapPrivate::prefetchTiles() +{ + if (m_tileRequests && m_prefetchStyle != QGeoTiledMap::NoPrefetching) { + + QSet tiles; + QGeoCameraData camera = m_visibleTiles->cameraData(); + int currentIntZoom = static_cast(std::floor(camera.zoomLevel())); + + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(PREFETCH_FRUSTUM_SCALE); + tiles = m_prefetchTiles->createTiles(); + + switch (m_prefetchStyle) { + + case QGeoTiledMap::PrefetchNeighbourLayer: { + double zoomFraction = camera.zoomLevel() - currentIntZoom; + int nearestNeighbourLayer = zoomFraction > 0.5 ? currentIntZoom + 1 : currentIntZoom - 1; + if (nearestNeighbourLayer <= m_maxZoomLevel && nearestNeighbourLayer >= m_minZoomLevel) { + camera.setZoomLevel(nearestNeighbourLayer); + // Approx heuristic, keeping total # prefetched tiles roughly independent of the + // fractional zoom level. + double neighbourScale = (1.0 + zoomFraction)/2.0; + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(PREFETCH_FRUSTUM_SCALE * neighbourScale); + tiles += m_prefetchTiles->createTiles(); + } + } + break; + + case QGeoTiledMap::PrefetchTwoNeighbourLayers: { + // This is a simpler strategy, we just prefetch from layer above and below + // for the layer below we only use half the size as this fills the screen + if (currentIntZoom > m_minZoomLevel) { + camera.setZoomLevel(currentIntZoom - 1); + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(0.5); + tiles += m_prefetchTiles->createTiles(); + } + + if (currentIntZoom < m_maxZoomLevel) { + camera.setZoomLevel(currentIntZoom + 1); + m_prefetchTiles->setCameraData(camera); + m_prefetchTiles->setViewExpansion(1.0); + tiles += m_prefetchTiles->createTiles(); + } + } + break; + + default: + break; + } + + m_tileRequests->requestTiles(tiles - m_mapScene->texturedTiles()); + } +} + +QGeoMapType QGeoTiledMapPrivate::activeMapType() +{ + return m_visibleTiles->activeMapType(); +} + +// Called before changeCameraData +void QGeoTiledMapPrivate::onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities) +{ + // Handle varying min/maxZoomLevel + if (oldCameraCapabilities.minimumZoomLevel() != m_cameraCapabilities.minimumZoomLevel()) + m_minZoomLevel = static_cast(std::ceil(m_cameraCapabilities.minimumZoomLevel())); + if (oldCameraCapabilities.maximumZoomLevel() != m_cameraCapabilities.maximumZoomLevel()) + m_maxZoomLevel = static_cast(std::ceil(m_cameraCapabilities.maximumZoomLevel())); + + // Handle varying tile size + if (oldCameraCapabilities.tileSize() != m_cameraCapabilities.tileSize()) { + m_visibleTiles->setTileSize(oldCameraCapabilities.tileSize()); + m_prefetchTiles->setTileSize(oldCameraCapabilities.tileSize()); + m_mapScene->setTileSize(oldCameraCapabilities.tileSize()); + } +} + +void QGeoTiledMapPrivate::changeCameraData(const QGeoCameraData &cameraData) +{ + Q_Q(QGeoTiledMap); + + QGeoCameraData cam = cameraData; + + // The incoming zoom level is intended for a tileSize of 256. + // Adapt it to the current tileSize + double zoomLevel = cameraData.zoomLevel(); + if (m_visibleTiles->tileSize() != 256) + zoomLevel = zoomLevelFrom256(zoomLevel, m_visibleTiles->tileSize()); + cam.setZoomLevel(zoomLevel); + + // For zoomlevel, "snap" 0.01 either side of a whole number. + // This is so that when we turn off bilinear scaling, we're + // snapped to the exact pixel size of the tiles + int izl = static_cast(std::floor(cam.zoomLevel())); + float delta = cam.zoomLevel() - izl; + + if (delta > 0.5) { + izl++; + delta -= 1.0; + } + + // TODO: Don't do this if there's tilt or bearing. + if (qAbs(delta) < 0.01) { + cam.setZoomLevel(izl); + } + + m_visibleTiles->setCameraData(cam); + m_mapScene->setCameraData(cam); + + updateScene(); + q->sgNodeChanged(); +} + +void QGeoTiledMapPrivate::updateScene() +{ + Q_Q(QGeoTiledMap); + // detect if new tiles introduced + const QSet& tiles = m_visibleTiles->createTiles(); + bool newTilesIntroduced = !m_mapScene->visibleTiles().contains(tiles); + m_mapScene->setVisibleTiles(tiles); + + if (newTilesIntroduced && m_copyrightVisible) + q->evaluateCopyrights(tiles); + + // don't request tiles that are already built and textured + QMap > cachedTiles = + m_tileRequests->requestTiles(m_visibleTiles->createTiles() - m_mapScene->texturedTiles()); + + for (auto it = cachedTiles.cbegin(); it != cachedTiles.cend(); ++it) + m_mapScene->addTile(it.key(), it.value()); + + if (!cachedTiles.isEmpty()) + emit q->sgNodeChanged(); +} + +void QGeoTiledMapPrivate::changeActiveMapType(const QGeoMapType mapType) +{ + m_visibleTiles->setTileSize(m_cameraCapabilities.tileSize()); + m_prefetchTiles->setTileSize(m_cameraCapabilities.tileSize()); + m_mapScene->setTileSize(m_cameraCapabilities.tileSize()); + m_visibleTiles->setMapType(mapType); + m_prefetchTiles->setMapType(mapType); + changeCameraData(m_cameraData); // Updates the zoom level to the possibly new tile size + // updateScene called in changeCameraData() +} + +void QGeoTiledMapPrivate::changeTileVersion(int version) +{ + m_visibleTiles->setMapVersion(version); + m_prefetchTiles->setMapVersion(version); + updateScene(); +} + +void QGeoTiledMapPrivate::clearScene() +{ + m_mapScene->clearTexturedTiles(); + m_mapScene->setVisibleTiles(QSet()); + updateScene(); +} + +void QGeoTiledMapPrivate::changeViewportSize(const QSize& size) +{ + Q_Q(QGeoTiledMap); + + m_visibleTiles->setScreenSize(size); + m_prefetchTiles->setScreenSize(size); + m_mapScene->setScreenSize(size); + + + if (!size.isEmpty() && m_cache) { + // absolute minimum size: one tile each side of display, 32-bit colour + int texCacheSize = (size.width() + m_visibleTiles->tileSize() * 2) * + (size.height() + m_visibleTiles->tileSize() * 2) * 4; + + // multiply by 3 so the 'recent' list in the cache is big enough for + // an entire display of tiles + texCacheSize *= 3; + // TODO: move this reasoning into the tilecache + + int newSize = qMax(m_cache->minTextureUsage(), texCacheSize); + m_cache->setMinTextureUsage(newSize); + } + + if (m_copyrightVisible) + q->evaluateCopyrights(m_mapScene->visibleTiles()); + updateScene(); +} + +void QGeoTiledMapPrivate::updateTile(const QGeoTileSpec &spec) +{ + Q_Q(QGeoTiledMap); + // Only promote the texture up to GPU if it is visible + if (m_visibleTiles->createTiles().contains(spec)){ + QSharedPointer tex = m_tileRequests->tileTexture(spec); + if (!tex.isNull() && !tex->image.isNull()) { + m_mapScene->addTile(spec, tex); + emit q->sgNodeChanged(); + } + } +} + +QSGNode *QGeoTiledMapPrivate::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window) +{ + return m_mapScene->updateSceneGraph(oldNode, window); +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeotiledmap_p.h b/src/location/maps/qgeotiledmap_p.h new file mode 100644 index 0000000..a162bdb --- /dev/null +++ b/src/location/maps/qgeotiledmap_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOTILEDMAP_P_H +#define QGEOTILEDMAP_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoTileSpec; +class QGeoTileTexture; +class QAbstractGeoTileCache; +class QGeoTiledMapPrivate; +class QGeoTiledMappingManagerEngine; +class QGeoTileRequestManager; + +class QQuickWindow; +class QSGNode; + +class QPointF; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTiledMap : public QGeoMap +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoTiledMap) +public: + enum PrefetchStyle { NoPrefetching, PrefetchNeighbourLayer, PrefetchTwoNeighbourLayers }; + QGeoTiledMap(QGeoTiledMappingManagerEngine *engine, QObject *parent); + virtual ~QGeoTiledMap(); + + QAbstractGeoTileCache *tileCache(); + QGeoTileRequestManager *requestManager(); + void updateTile(const QGeoTileSpec &spec); + void setPrefetchStyle(PrefetchStyle style); + + void prefetchData() override; + void clearData() override; + Capabilities capabilities() const override; + + void setCopyrightVisible(bool visible) override; + +public Q_SLOTS: + virtual void clearScene(int mapId); + +protected: + QSGNode *updateSceneGraph(QSGNode *, QQuickWindow *window) override; + virtual void evaluateCopyrights(const QSet &visibleTiles); + + QGeoTiledMap(QGeoTiledMapPrivate &dd, QGeoTiledMappingManagerEngine *engine, QObject *parent); + +private Q_SLOTS: + void handleTileVersionChanged(); + +private: + Q_DISABLE_COPY(QGeoTiledMap) + +}; + +QT_END_NAMESPACE + +#endif // QGEOMAP_P_H diff --git a/src/location/maps/qgeotiledmap_p_p.h b/src/location/maps/qgeotiledmap_p_p.h new file mode 100644 index 0000000..d3791d4 --- /dev/null +++ b/src/location/maps/qgeotiledmap_p_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOTILEDMAP_P_P_H +#define QGEOTILEDMAP_P_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCameraTiles; +class QGeoTiledMapScene; +class QAbstractGeoTileCache; +class QGeoTiledMappingManagerEngine; +class QGeoTiledMap; +class QGeoTileRequestManager; +class QGeoTileSpec; +class QSGNode; +class QQuickWindow; +class QGeoCameraCapabilities; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTiledMapPrivate : public QGeoMapPrivate +{ + Q_DECLARE_PUBLIC(QGeoTiledMap) +public: + QGeoTiledMapPrivate(QGeoTiledMappingManagerEngine *engine); + ~QGeoTiledMapPrivate(); + + QSGNode *updateSceneGraph(QSGNode *node, QQuickWindow *window); + + void updateTile(const QGeoTileSpec &spec); + void prefetchTiles(); + QGeoMapType activeMapType(); + void onCameraCapabilitiesChanged(const QGeoCameraCapabilities &oldCameraCapabilities); + +protected: + void changeViewportSize(const QSize& size) override; + void changeCameraData(const QGeoCameraData &cameraData) override; + void changeActiveMapType(const QGeoMapType mapType) override; + void changeTileVersion(int version); + void clearScene(); + + void updateScene(); + +protected: + QAbstractGeoTileCache *m_cache; + QGeoCameraTiles *m_visibleTiles; + QGeoCameraTiles *m_prefetchTiles; + QGeoTiledMapScene *m_mapScene; + QGeoTileRequestManager *m_tileRequests; + int m_maxZoomLevel; + int m_minZoomLevel; + QGeoTiledMap::PrefetchStyle m_prefetchStyle; + Q_DISABLE_COPY(QGeoTiledMapPrivate) +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEDMAP_P_P_H diff --git a/src/location/maps/qgeotiledmappingmanagerengine.cpp b/src/location/maps/qgeotiledmappingmanagerengine.cpp new file mode 100644 index 0000000..c17f7ff --- /dev/null +++ b/src/location/maps/qgeotiledmappingmanagerengine.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmappingmanagerengine_p.h" +#include "qgeotiledmappingmanagerengine_p_p.h" +#include "qgeotilefetcher_p.h" + + +#include "qgeotiledmap_p.h" +#include "qgeotilerequestmanager_p.h" +#include "qgeofiletilecache_p.h" +#include "qgeotilespec_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoTiledMappingManagerEngine::QGeoTiledMappingManagerEngine(QObject *parent) + : QGeoMappingManagerEngine(parent), + m_prefetchStyle(QGeoTiledMap::PrefetchTwoNeighbourLayers), + d_ptr(new QGeoTiledMappingManagerEnginePrivate) +{ +} + +/*! + Destroys this mapping manager. +*/ +QGeoTiledMappingManagerEngine::~QGeoTiledMappingManagerEngine() +{ + delete d_ptr; +} + +/*! + Sets the tile fetcher. Takes ownership of the QObject. +*/ +void QGeoTiledMappingManagerEngine::setTileFetcher(QGeoTileFetcher *fetcher) +{ + Q_D(QGeoTiledMappingManagerEngine); + + if (d->fetcher_) + d->fetcher_->deleteLater(); + fetcher->setParent(this); + d->fetcher_ = fetcher; + + qRegisterMetaType(); + + connect(d->fetcher_, + SIGNAL(tileFinished(QGeoTileSpec,QByteArray,QString)), + this, + SLOT(engineTileFinished(QGeoTileSpec,QByteArray,QString)), + Qt::QueuedConnection); + connect(d->fetcher_, + SIGNAL(tileError(QGeoTileSpec,QString)), + this, + SLOT(engineTileError(QGeoTileSpec,QString)), + Qt::QueuedConnection); + + engineInitialized(); +} + +QGeoTileFetcher *QGeoTiledMappingManagerEngine::tileFetcher() +{ + Q_D(QGeoTiledMappingManagerEngine); + return d->fetcher_; +} + +QGeoMap *QGeoTiledMappingManagerEngine::createMap() +{ + return NULL; +} + +void QGeoTiledMappingManagerEngine::releaseMap(QGeoTiledMap *map) +{ + d_ptr->mapHash_.remove(map); + + QHash > newTileHash = d_ptr->tileHash_; + typedef QHash >::const_iterator h_iter; + h_iter hi = d_ptr->tileHash_.constBegin(); + h_iter hend = d_ptr->tileHash_.constEnd(); + for (; hi != hend; ++hi) { + QSet maps = hi.value(); + if (maps.contains(map)) { + maps.remove(map); + if (maps.isEmpty()) + newTileHash.remove(hi.key()); + else + newTileHash.insert(hi.key(), maps); + } + } + d_ptr->tileHash_ = newTileHash; +} + +void QGeoTiledMappingManagerEngine::updateTileRequests(QGeoTiledMap *map, + const QSet &tilesAdded, + const QSet &tilesRemoved) +{ + Q_D(QGeoTiledMappingManagerEngine); + + typedef QSet::const_iterator tile_iter; + + // add and remove tiles from tileset for this map + + QSet oldTiles = d->mapHash_.value(map); + + tile_iter rem = tilesRemoved.constBegin(); + tile_iter remEnd = tilesRemoved.constEnd(); + for (; rem != remEnd; ++rem) { + oldTiles.remove(*rem); + } + + tile_iter add = tilesAdded.constBegin(); + tile_iter addEnd = tilesAdded.constEnd(); + for (; add != addEnd; ++add) { + oldTiles.insert(*add); + } + + d->mapHash_.insert(map, oldTiles); + + // add and remove map from mapset for the tiles + + QSet reqTiles; + QSet cancelTiles; + + rem = tilesRemoved.constBegin(); + for (; rem != remEnd; ++rem) { + QSet mapSet = d->tileHash_.value(*rem); + mapSet.remove(map); + if (mapSet.isEmpty()) { + cancelTiles.insert(*rem); + d->tileHash_.remove(*rem); + } else { + d->tileHash_.insert(*rem, mapSet); + } + } + + add = tilesAdded.constBegin(); + for (; add != addEnd; ++add) { + QSet mapSet = d->tileHash_.value(*add); + if (mapSet.isEmpty()) { + reqTiles.insert(*add); + } + mapSet.insert(map); + d->tileHash_.insert(*add, mapSet); + } + + cancelTiles -= reqTiles; + + QMetaObject::invokeMethod(d->fetcher_, "updateTileRequests", + Qt::QueuedConnection, + Q_ARG(QSet, reqTiles), + Q_ARG(QSet, cancelTiles)); +} + +void QGeoTiledMappingManagerEngine::engineTileFinished(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format) +{ + Q_D(QGeoTiledMappingManagerEngine); + + QSet maps = d->tileHash_.value(spec); + + typedef QSet::const_iterator map_iter; + + map_iter map = maps.constBegin(); + map_iter mapEnd = maps.constEnd(); + for (; map != mapEnd; ++map) { + QSet tileSet = d->mapHash_.value(*map); + tileSet.remove(spec); + if (tileSet.isEmpty()) + d->mapHash_.remove(*map); + else + d->mapHash_.insert(*map, tileSet); + } + + d->tileHash_.remove(spec); + tileCache()->insert(spec, bytes, format, d->cacheHint_); + + map = maps.constBegin(); + mapEnd = maps.constEnd(); + for (; map != mapEnd; ++map) { + (*map)->requestManager()->tileFetched(spec); + } +} + +void QGeoTiledMappingManagerEngine::engineTileError(const QGeoTileSpec &spec, const QString &errorString) +{ + Q_D(QGeoTiledMappingManagerEngine); + + QSet maps = d->tileHash_.value(spec); + typedef QSet::const_iterator map_iter; + map_iter map = maps.constBegin(); + map_iter mapEnd = maps.constEnd(); + for (; map != mapEnd; ++map) { + QSet tileSet = d->mapHash_.value(*map); + + tileSet.remove(spec); + if (tileSet.isEmpty()) + d->mapHash_.remove(*map); + else + d->mapHash_.insert(*map, tileSet); + } + d->tileHash_.remove(spec); + + for (map = maps.constBegin(); map != mapEnd; ++map) { + (*map)->requestManager()->tileError(spec, errorString); + } + + emit tileError(spec, errorString); +} + +void QGeoTiledMappingManagerEngine::setTileSize(const QSize &tileSize) +{ + Q_D(QGeoTiledMappingManagerEngine); + d->tileSize_ = tileSize; +} + +void QGeoTiledMappingManagerEngine::setTileVersion(int version) +{ + Q_D(QGeoTiledMappingManagerEngine); + if (d->m_tileVersion != version) { + d->m_tileVersion = version; + emit tileVersionChanged(); + } +} + +QSize QGeoTiledMappingManagerEngine::tileSize() const +{ + Q_D(const QGeoTiledMappingManagerEngine); + return d->tileSize_; +} + +int QGeoTiledMappingManagerEngine::tileVersion() const +{ + Q_D(const QGeoTiledMappingManagerEngine); + return d->m_tileVersion; +} + +QAbstractGeoTileCache::CacheAreas QGeoTiledMappingManagerEngine::cacheHint() const +{ + Q_D(const QGeoTiledMappingManagerEngine); + return d->cacheHint_; +} + +void QGeoTiledMappingManagerEngine::setCacheHint(QAbstractGeoTileCache::CacheAreas cacheHint) +{ + Q_D(QGeoTiledMappingManagerEngine); + d->cacheHint_ = cacheHint; +} + +/*! + Sets the tile cache. Takes ownership of the QObject. +*/ +void QGeoTiledMappingManagerEngine::setTileCache(QAbstractGeoTileCache *cache) +{ + Q_D(QGeoTiledMappingManagerEngine); + Q_ASSERT_X(!d->tileCache_, Q_FUNC_INFO, "This should be called only once"); + cache->setParent(this); + d->tileCache_ = cache; + d->tileCache_->init(); +} + +QAbstractGeoTileCache *QGeoTiledMappingManagerEngine::tileCache() +{ + Q_D(QGeoTiledMappingManagerEngine); + if (!d->tileCache_) { + QString cacheDirectory; + if (!managerName().isEmpty()) + cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + managerName(); + d->tileCache_ = new QGeoFileTileCache(cacheDirectory); + d->tileCache_->init(); + } + return d->tileCache_; +} + +QSharedPointer QGeoTiledMappingManagerEngine::getTileTexture(const QGeoTileSpec &spec) +{ + return d_ptr->tileCache_->get(spec); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoTiledMappingManagerEnginePrivate::QGeoTiledMappingManagerEnginePrivate() +: m_tileVersion(-1), + cacheHint_(QAbstractGeoTileCache::AllCaches), + tileCache_(0), + fetcher_(0) +{ +} + +QGeoTiledMappingManagerEnginePrivate::~QGeoTiledMappingManagerEnginePrivate() +{ +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeotiledmappingmanagerengine_p.h b/src/location/maps/qgeotiledmappingmanagerengine_p.h new file mode 100644 index 0000000..54006d9 --- /dev/null +++ b/src/location/maps/qgeotiledmappingmanagerengine_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPPINGMANAGERENGINE_H +#define QGEOTILEDMAPPINGMANAGERENGINE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEnginePrivate; +class QGeoTileFetcher; +class QGeoTileTexture; +class QGeoTileSpec; +class QGeoTiledMap; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTiledMappingManagerEngine : public QGeoMappingManagerEngine +{ + Q_OBJECT + +public: + explicit QGeoTiledMappingManagerEngine(QObject *parent = 0); + virtual ~QGeoTiledMappingManagerEngine(); + + QGeoTileFetcher *tileFetcher(); + + QGeoMap *createMap() override; + void releaseMap(QGeoTiledMap *map); + + QSize tileSize() const; + int tileVersion() const; + + virtual void updateTileRequests(QGeoTiledMap *map, + const QSet &tilesAdded, + const QSet &tilesRemoved); + + QAbstractGeoTileCache *tileCache(); + virtual QSharedPointer getTileTexture(const QGeoTileSpec &spec); + + QAbstractGeoTileCache::CacheAreas cacheHint() const; + +protected Q_SLOTS: + virtual void engineTileFinished(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format); + virtual void engineTileError(const QGeoTileSpec &spec, const QString &errorString); + +Q_SIGNALS: + void tileError(const QGeoTileSpec &spec, const QString &errorString); + void tileVersionChanged(); + +protected: + void setTileFetcher(QGeoTileFetcher *fetcher); + void setTileSize(const QSize &tileSize); + void setTileVersion(int version); + void setCacheHint(QAbstractGeoTileCache::CacheAreas cacheHint); + void setTileCache(QAbstractGeoTileCache *cache); + + QGeoTiledMap::PrefetchStyle m_prefetchStyle; + QGeoTiledMappingManagerEnginePrivate *d_ptr; + + Q_DECLARE_PRIVATE(QGeoTiledMappingManagerEngine) + Q_DISABLE_COPY(QGeoTiledMappingManagerEngine) + + friend class QGeoTileFetcher; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeotiledmappingmanagerengine_p_p.h b/src/location/maps/qgeotiledmappingmanagerengine_p_p.h new file mode 100644 index 0000000..5ef5b37 --- /dev/null +++ b/src/location/maps/qgeotiledmappingmanagerengine_p_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGEOTILEDMAPPINGMANAGER_P_H +#define QGEOTILEDMAPPINGMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include "qgeotiledmappingmanagerengine_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoTiledMap; +class QAbstractGeoTileCache; +class QGeoTileSpec; +class QGeoTileFetcher; + +class QGeoTiledMappingManagerEnginePrivate +{ +public: + QGeoTiledMappingManagerEnginePrivate(); + ~QGeoTiledMappingManagerEnginePrivate(); + + QSize tileSize_; + int m_tileVersion; + QHash > mapHash_; + QHash > tileHash_; + QAbstractGeoTileCache::CacheAreas cacheHint_; + QAbstractGeoTileCache *tileCache_; + QGeoTileFetcher *fetcher_; + +private: + Q_DISABLE_COPY(QGeoTiledMappingManagerEnginePrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeotiledmapreply.cpp b/src/location/maps/qgeotiledmapreply.cpp new file mode 100644 index 0000000..9a9c253 --- /dev/null +++ b/src/location/maps/qgeotiledmapreply.cpp @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmapreply_p.h" +#include "qgeotiledmapreply_p_p.h" + +#include + +QT_BEGIN_NAMESPACE +/*! + \class QGeoTiledMapReply + \inmodule QtLocation + \ingroup QtLocation-impl + \since 5.6 + \internal + + \brief The QGeoTiledMapReply class manages a tile fetch operation started + by an instance of QGeoTiledManagerEngine. + + Instances of QGeoTiledMapReply manage the state and results of these + operations. + + The isFinished(), error() and errorString() methods provide information + on whether the operation has completed and if it completed successfully. + + The finished() and error(QGeoTiledMapReply::Error,QString) + signals can be used to monitor the progress of the operation. + + It is possible that a newly created QGeoTiledMapReply may be in a finished + state, most commonly because an error has occurred. Since such an instance + will never emit the finished() or + error(QGeoTiledMapReply::Error,QString) signals, it is + important to check the result of isFinished() before making the connections + to the signals. + + If the operation completes successfully the results are accessed by + mapImageData() and mapImageFormat(). +*/ + +/*! + \enum QGeoTiledMapReply::Error + + Describes an error which prevented the completion of the operation. + + \value NoError + No error has occurred. + \value CommunicationError + An error occurred while communicating with the service provider. + \value ParseError + The response from the service provider was in an unrecognizable format + supported by the service provider. + \value UnknownError + An error occurred which does not fit into any of the other categories. +*/ + +/*! + Constructs a tiled map reply object based on \a request, with parent \a parent. +*/ +QGeoTiledMapReply::QGeoTiledMapReply(const QGeoTileSpec &spec, QObject *parent) + : QObject(parent), + d_ptr(new QGeoTiledMapReplyPrivate(spec)) +{ +} + +/*! + Constructs a tiled map reply object with a given \a error and \a errorString and the specified \a parent. +*/ +QGeoTiledMapReply::QGeoTiledMapReply(Error error, const QString &errorString, QObject *parent) + : QObject(parent), + d_ptr(new QGeoTiledMapReplyPrivate(error, errorString)) {} + +/*! + Destroys this tiled map reply object. +*/ +QGeoTiledMapReply::~QGeoTiledMapReply() +{ + delete d_ptr; +} + +/*! + Sets whether or not this reply has finished to \a finished. + + If \a finished is true, this will cause the finished() signal to be + emitted. + + If the operation completed successfully, + QGeoTiledMapReply::setMapImageData() should be called before this + function. If an error occurred, QGeoTiledMapReply::setError() should be used + instead. +*/ +void QGeoTiledMapReply::setFinished(bool finished) +{ + d_ptr->isFinished = finished; + if (d_ptr->isFinished) + emit this->finished(); +} + +/*! + Return true if the operation completed successfully or encountered an + error which cause the operation to come to a halt. +*/ +bool QGeoTiledMapReply::isFinished() const +{ + return d_ptr->isFinished; +} + +/*! + Sets the error state of this reply to \a error and the textual + representation of the error to \a errorString. + + This will also cause error() and finished() signals to be emitted, in that + order. +*/ +void QGeoTiledMapReply::setError(QGeoTiledMapReply::Error error, const QString &errorString) +{ + d_ptr->error = error; + d_ptr->errorString = errorString; + emit this->error(error, errorString); + setFinished(true); +} + +/*! + Returns the error state of this reply. + + If the result is QGeoTiledMapReply::NoError then no error has occurred. +*/ +QGeoTiledMapReply::Error QGeoTiledMapReply::error() const +{ + return d_ptr->error; +} + +/*! + Returns the textual representation of the error state of this reply. + + If no error has occurred this will return an empty string. It is possible + that an error occurred which has no associated textual representation, in + which case this will also return an empty string. + + To determine whether an error has occurred, check to see if + QGeoTiledMapReply::error() is equal to QGeoTiledMapReply::NoError. +*/ +QString QGeoTiledMapReply::errorString() const +{ + return d_ptr->errorString; +} + +/*! + Returns whether the reply is coming from a cache. +*/ +bool QGeoTiledMapReply::isCached() const +{ + return d_ptr->isCached; +} + +/*! + Sets whether the reply is coming from a cache to \a cached. +*/ +void QGeoTiledMapReply::setCached(bool cached) +{ + d_ptr->isCached = cached; +} + +/*! + Returns the request which corresponds to this reply. +*/ +QGeoTileSpec QGeoTiledMapReply::tileSpec() const +{ + return d_ptr->spec; +} + +/*! + Returns the tile image data. +*/ +QByteArray QGeoTiledMapReply::mapImageData() const +{ + return d_ptr->mapImageData; +} + +/*! + Sets the tile image data to \a data. +*/ +void QGeoTiledMapReply::setMapImageData(const QByteArray &data) +{ + d_ptr->mapImageData = data; +} + +/*! + Returns the format of the tile image. +*/ +QString QGeoTiledMapReply::mapImageFormat() const +{ + return d_ptr->mapImageFormat; +} + +/*! + Sets the format of the tile image to \a format. +*/ +void QGeoTiledMapReply::setMapImageFormat(const QString &format) +{ + d_ptr->mapImageFormat = format; +} + +/*! + Cancels the operation immediately. + + This will do nothing if the reply is finished. +*/ +void QGeoTiledMapReply::abort() +{ + if (!isFinished()) + setFinished(true); + emit aborted(); +} + +/* + \fn void QGeoTiledMapReply::finished() + + This signal is emitted when this reply has finished processing. + + If error() equals QGeoTiledMapReply::NoError then the processing + finished successfully. + + This signal and QGeoRoutingManager::finished() will be + emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. + + \fn void QGeoTiledMapReply::error(QGeoTiledMapReply::Error error, const QString &errorString) + + This signal is emitted when an error has been detected in the processing of + this reply. The finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error. + + This signal and QGeoRoutingManager::error() will be emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/*! + \fn void QGeoTiledMapReply::finished() + + This signal is emitted when this reply has finished processing. + + If error() equals QGeoTiledMapReply::NoError then the processing + finished successfully. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ +/*! + \fn void QGeoTiledMapReply::error(QGeoTiledMapReply::Error error, const QString &errorString) + + This signal is emitted when an error has been detected in the processing of + this reply. The finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/******************************************************************************* +*******************************************************************************/ + +QGeoTiledMapReplyPrivate::QGeoTiledMapReplyPrivate(const QGeoTileSpec &spec) + : error(QGeoTiledMapReply::NoError), + isFinished(false), + isCached(false), + spec(spec) {} + +QGeoTiledMapReplyPrivate::QGeoTiledMapReplyPrivate(QGeoTiledMapReply::Error error, const QString &errorString) + : error(error), + errorString(errorString), + isFinished(true), + isCached(false) {} + +QGeoTiledMapReplyPrivate::~QGeoTiledMapReplyPrivate() {} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeotiledmapreply_p.h b/src/location/maps/qgeotiledmapreply_p.h new file mode 100644 index 0000000..5a2a246 --- /dev/null +++ b/src/location/maps/qgeotiledmapreply_p.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPREPLY_H +#define QGEOTILEDMAPREPLY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoTileSpec; +class QGeoTiledMapReplyPrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTiledMapReply : public QObject +{ + Q_OBJECT + +public: + enum Error { + NoError, + CommunicationError, + ParseError, + UnknownError + }; + + QGeoTiledMapReply(const QGeoTileSpec &spec, QObject *parent = 0); + QGeoTiledMapReply(Error error, const QString &errorString, QObject *parent = 0); + virtual ~QGeoTiledMapReply(); + + bool isFinished() const; + Error error() const; + QString errorString() const; + + bool isCached() const; + + QGeoTileSpec tileSpec() const; + + QByteArray mapImageData() const; + QString mapImageFormat() const; + + virtual void abort(); + +Q_SIGNALS: + void finished(); + void aborted(); + void error(QGeoTiledMapReply::Error error, const QString &errorString = QString()); + +protected: + void setError(Error error, const QString &errorString); + void setFinished(bool finished); + + void setCached(bool cached); + + void setMapImageData(const QByteArray &data); + void setMapImageFormat(const QString &format); + +private: + QGeoTiledMapReplyPrivate *d_ptr; + Q_DISABLE_COPY(QGeoTiledMapReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeotiledmapreply_p_p.h b/src/location/maps/qgeotiledmapreply_p_p.h new file mode 100644 index 0000000..5dcf5a7 --- /dev/null +++ b/src/location/maps/qgeotiledmapreply_p_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPREPLY_P_H +#define QGEOTILEDMAPREPLY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeotiledmapreply_p.h" +#include "qgeotilespec_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoTiledMapReplyPrivate +{ +public: + QGeoTiledMapReplyPrivate(const QGeoTileSpec &spec); + QGeoTiledMapReplyPrivate(QGeoTiledMapReply::Error error, const QString &errorString); + ~QGeoTiledMapReplyPrivate(); + + QGeoTiledMapReply::Error error; + QString errorString; + bool isFinished; + bool isCached; + + QGeoTileSpec spec; + QByteArray mapImageData; + QString mapImageFormat; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeotiledmapscene.cpp b/src/location/maps/qgeotiledmapscene.cpp new file mode 100644 index 0000000..ab15e37 --- /dev/null +++ b/src/location/maps/qgeotiledmapscene.cpp @@ -0,0 +1,727 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2014 Jolla Ltd, author: +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeotiledmapscene_p.h" +#include "qgeocameradata_p.h" +#include "qabstractgeotilecache_p.h" +#include "qgeotilespec_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static QVector3D toVector3D(const QDoubleVector3D& in) +{ + return QVector3D(in.x(), in.y(), in.z()); +} + +QT_BEGIN_NAMESPACE + +class QGeoTiledMapScenePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGeoTiledMapScene) +public: + QGeoTiledMapScenePrivate(); + ~QGeoTiledMapScenePrivate(); + + QSize m_screenSize; // in pixels + int m_tileSize; // the pixel resolution for each tile + QGeoCameraData m_cameraData; + QSet m_visibleTiles; + + QDoubleVector3D m_cameraUp; + QDoubleVector3D m_cameraEye; + QDoubleVector3D m_cameraCenter; + QMatrix4x4 m_projectionMatrix; + + // scales up the tile geometry and the camera altitude, resulting in no visible effect + // other than to control the accuracy of the render by keeping the values in a sensible range + double m_scaleFactor; + + // rounded down, positive zoom is zooming in, corresponding to reduced altitude + int m_intZoomLevel; + + // mercatorToGrid transform + // the number of tiles in each direction for the whole map (earth) at the current zoom level. + // it is 1< > m_textures; + QVector m_updatedTextures; + + // tilesToGrid transform + int m_minTileX; // the minimum tile index, i.e. 0 to sideLength which is 1<< zoomLevel + int m_minTileY; + int m_maxTileX; + int m_maxTileY; + int m_tileXWrapsBelow; // the wrap point as a tile index + + bool m_linearScaling; + + bool m_dropTextures; + + void addTile(const QGeoTileSpec &spec, QSharedPointer texture); + + void setVisibleTiles(const QSet &visibleTiles); + void removeTiles(const QSet &oldTiles); + bool buildGeometry(const QGeoTileSpec &spec, QSGImageNode *imageNode, bool &overzooming); + void updateTileBounds(const QSet &tiles); + void setupCamera(); + inline bool isTiltedOrRotated() { return (m_cameraData.tilt() > 0.0) || (m_cameraData.bearing() > 0.0); } +}; + +QGeoTiledMapScene::QGeoTiledMapScene(QObject *parent) + : QObject(*new QGeoTiledMapScenePrivate(),parent) +{ +} + +QGeoTiledMapScene::~QGeoTiledMapScene() +{ +} + +void QGeoTiledMapScene::setScreenSize(const QSize &size) +{ + Q_D(QGeoTiledMapScene); + d->m_screenSize = size; +} + +void QGeoTiledMapScene::updateSceneParameters() +{ + Q_D(QGeoTiledMapScene); + d->m_intZoomLevel = static_cast(std::floor(d->m_cameraData.zoomLevel())); + const float delta = d->m_cameraData.zoomLevel() - d->m_intZoomLevel; + d->m_linearScaling = qAbs(delta) > 0.05 || d->isTiltedOrRotated(); + d->m_sideLength = 1 << d->m_intZoomLevel; + d->m_mapEdgeSize = std::pow(2.0, d->m_cameraData.zoomLevel()) * d->m_tileSize; +} + +void QGeoTiledMapScene::setTileSize(int tileSize) +{ + Q_D(QGeoTiledMapScene); + if (d->m_tileSize == tileSize) + return; + + d->m_tileSize = tileSize; + updateSceneParameters(); +} + +void QGeoTiledMapScene::setCameraData(const QGeoCameraData &cameraData) +{ + Q_D(QGeoTiledMapScene); + d->m_cameraData = cameraData; + updateSceneParameters(); +} + +void QGeoTiledMapScene::setVisibleTiles(const QSet &tiles) +{ + Q_D(QGeoTiledMapScene); + d->setVisibleTiles(tiles); +} + +const QSet &QGeoTiledMapScene::visibleTiles() const +{ + Q_D(const QGeoTiledMapScene); + return d->m_visibleTiles; +} + +void QGeoTiledMapScene::addTile(const QGeoTileSpec &spec, QSharedPointer texture) +{ + Q_D(QGeoTiledMapScene); + d->addTile(spec, texture); +} + +QSet QGeoTiledMapScene::texturedTiles() +{ + Q_D(QGeoTiledMapScene); + QSet textured; + for (auto it = d->m_textures.cbegin(); it != d->m_textures.cend(); ++it) + textured += it.value()->spec; + + return textured; +} + +void QGeoTiledMapScene::clearTexturedTiles() +{ + Q_D(QGeoTiledMapScene); + d->m_textures.clear(); + d->m_dropTextures = true; +} + +QGeoTiledMapScenePrivate::QGeoTiledMapScenePrivate() + : QObjectPrivate(), + m_tileSize(0), + m_scaleFactor(10.0), + m_intZoomLevel(0), + m_sideLength(0), + m_minTileX(-1), + m_minTileY(-1), + m_maxTileX(-1), + m_maxTileY(-1), + m_tileXWrapsBelow(0), + m_linearScaling(false), + m_dropTextures(false) +{ +} + +QGeoTiledMapScenePrivate::~QGeoTiledMapScenePrivate() +{ +} + +bool QGeoTiledMapScenePrivate::buildGeometry(const QGeoTileSpec &spec, QSGImageNode *imageNode, bool &overzooming) +{ + overzooming = false; + int x = spec.x(); + + if (x < m_tileXWrapsBelow) + x += m_sideLength; + + if ((x < m_minTileX) + || (m_maxTileX < x) + || (spec.y() < m_minTileY) + || (m_maxTileY < spec.y()) + || (spec.zoom() != m_intZoomLevel)) { + return false; + } + + double edge = m_scaleFactor * m_tileSize; + + double x1 = (x - m_minTileX); + double x2 = x1 + 1.0; + + double y1 = (m_minTileY - spec.y()); + double y2 = y1 - 1.0; + + x1 *= edge; + x2 *= edge; + y1 *= edge; + y2 *= edge; + + imageNode->setRect(QRectF(QPointF(x1, y2), QPointF(x2, y1))); + imageNode->setTextureCoordinatesTransform(QSGImageNode::MirrorVertically); + + // Calculate the texture mapping, in case we are magnifying some lower ZL tile + const auto it = m_textures.find(spec); // This should be always found, but apparently sometimes it isn't, possibly due to memory shortage + if (it != m_textures.end()) { + if (it.value()->spec.zoom() < spec.zoom()) { + // Currently only using lower ZL tiles for the overzoom. + const int tilesPerTexture = 1 << (spec.zoom() - it.value()->spec.zoom()); + const int mappedSize = imageNode->texture()->textureSize().width() / tilesPerTexture; + const int x = (spec.x() % tilesPerTexture) * mappedSize; + const int y = (spec.y() % tilesPerTexture) * mappedSize; + imageNode->setSourceRect(QRectF(x, y, mappedSize, mappedSize)); + overzooming = true; + } else { + imageNode->setSourceRect(QRectF(QPointF(0,0), imageNode->texture()->textureSize())); + } + } else { + qWarning() << "!! buildGeometry: tileSpec not present in m_textures !!"; + imageNode->setSourceRect(QRectF(QPointF(0,0), imageNode->texture()->textureSize())); + } + + return true; +} + +void QGeoTiledMapScenePrivate::addTile(const QGeoTileSpec &spec, QSharedPointer texture) +{ + if (!m_visibleTiles.contains(spec)) // Don't add the geometry if it isn't visible + return; + + if (m_textures.contains(spec)) + m_updatedTextures.append(spec); + m_textures.insert(spec, texture); +} + +void QGeoTiledMapScenePrivate::setVisibleTiles(const QSet &visibleTiles) +{ + // work out the tile bounds for the new scene + updateTileBounds(visibleTiles); + + // set up the gl camera for the new scene + setupCamera(); + + QSet toRemove = m_visibleTiles - visibleTiles; + if (!toRemove.isEmpty()) + removeTiles(toRemove); + + m_visibleTiles = visibleTiles; +} + +void QGeoTiledMapScenePrivate::removeTiles(const QSet &oldTiles) +{ + typedef QSet::const_iterator iter; + iter i = oldTiles.constBegin(); + iter end = oldTiles.constEnd(); + + for (; i != end; ++i) { + QGeoTileSpec tile = *i; + m_textures.remove(tile); + } +} + +void QGeoTiledMapScenePrivate::updateTileBounds(const QSet &tiles) +{ + if (tiles.isEmpty()) { + m_minTileX = -1; + m_minTileY = -1; + m_maxTileX = -1; + m_maxTileY = -1; + return; + } + + typedef QSet::const_iterator iter; + iter i = tiles.constBegin(); + iter end = tiles.constEnd(); + + // determine whether the set of map tiles crosses the dateline. + // A gap in the tiles indicates dateline crossing + bool hasFarLeft = false; + bool hasFarRight = false; + bool hasMidLeft = false; + bool hasMidRight = false; + + for (; i != end; ++i) { + if ((*i).zoom() != m_intZoomLevel) + continue; + int x = (*i).x(); + if (x == 0) + hasFarLeft = true; + else if (x == (m_sideLength - 1)) + hasFarRight = true; + else if (x == ((m_sideLength / 2) - 1)) { + hasMidLeft = true; + } else if (x == (m_sideLength / 2)) { + hasMidRight = true; + } + } + + // if dateline crossing is detected we wrap all x pos of tiles + // that are in the left half of the map. + m_tileXWrapsBelow = 0; + + if (hasFarLeft && hasFarRight) { + if (!hasMidRight) { + m_tileXWrapsBelow = m_sideLength / 2; + } else if (!hasMidLeft) { + m_tileXWrapsBelow = (m_sideLength / 2) - 1; + } + } + + // finally, determine the min and max bounds + i = tiles.constBegin(); + + QGeoTileSpec tile = *i; + + int x = tile.x(); + if (tile.x() < m_tileXWrapsBelow) + x += m_sideLength; + + m_minTileX = x; + m_maxTileX = x; + m_minTileY = tile.y(); + m_maxTileY = tile.y(); + + ++i; + + for (; i != end; ++i) { + tile = *i; + if (tile.zoom() != m_intZoomLevel) + continue; + + int x = tile.x(); + if (tile.x() < m_tileXWrapsBelow) + x += m_sideLength; + + m_minTileX = qMin(m_minTileX, x); + m_maxTileX = qMax(m_maxTileX, x); + m_minTileY = qMin(m_minTileY, tile.y()); + m_maxTileY = qMax(m_maxTileY, tile.y()); + } +} + +void QGeoTiledMapScenePrivate::setupCamera() +{ + // NOTE: The following instruction is correct only because WebMercator is a square projection! + double f = m_screenSize.height(); + + // Using fraction of zoom level, z varies between [ m_tileSize , 2 * m_tileSize [ + double z = std::pow(2.0, m_cameraData.zoomLevel() - m_intZoomLevel) * m_tileSize; + + // calculate altitude that allows the visible map tiles + // to fit in the screen correctly (note that a larger f will cause + // the camera be higher, resulting in gray areas displayed around + // the tiles) + double altitude = f / (2.0 * z); + + // calculate center + double edge = m_scaleFactor * m_tileSize; + + // first calculate the camera center in map space in the range of 0 <-> sideLength (2^z) + QDoubleVector2D camCenterMercator = QWebMercator::coordToMercator(m_cameraData.center()); + QDoubleVector3D center = (m_sideLength * camCenterMercator); + + // wrap the center if necessary (due to dateline crossing) + if (center.x() < m_tileXWrapsBelow) + center.setX(center.x() + 1.0 * m_sideLength); + + // work out where the camera center is w.r.t minimum tile bounds + center.setX(center.x() - 1.0 * m_minTileX); + center.setY(1.0 * m_minTileY - center.y()); + + // apply necessary scaling to the camera center + center *= edge; + + // calculate eye + double apertureSize = 1.0; + if (m_cameraData.fieldOfView() != 90.0) //aperture(90 / 2) = 1 + apertureSize = tan(QLocationUtils::radians(m_cameraData.fieldOfView()) * 0.5); + QDoubleVector3D eye = center; + eye.setZ(altitude * edge / apertureSize); + + // calculate up + + QDoubleVector3D view = eye - center; + QDoubleVector3D side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0)); + QDoubleVector3D up = QDoubleVector3D::normal(side, view); + + // old bearing, tilt and roll code. + // Now using double matrices until distilling the transformation to QMatrix4x4 + QDoubleMatrix4x4 mBearing; + // -1.0 * bearing removed, now map north goes in the bearing direction + mBearing.rotate(-1.0 * m_cameraData.bearing(), view); + up = mBearing * up; + + QDoubleVector3D side2 = QDoubleVector3D::normal(up, view); + if (m_cameraData.tilt() > 0.01) { + QDoubleMatrix4x4 mTilt; + mTilt.rotate(m_cameraData.tilt(), side2); + eye = mTilt * view + center; + } + + view = eye - center; + view.normalize(); + side = QDoubleVector3D::normal(view, QDoubleVector3D(0.0, 1.0, 0.0)); + up = QDoubleVector3D::normal(view, side2); + + // QMatrix4x4 mRoll; + // mRoll.rotate(camera.roll(), view); + // up = mRoll * up; + + // near plane and far plane + + double nearPlane = 1.0; + // Clip plane. Used to be (altitude + 1.0) * edge. This does not affect the perspective. minimum value would be > 0.0 + // Since, for some reasons possibly related to how QSG works, this clipping plane is unable to clip part of tiles, + // Instead of farPlane = (altitude + m_cameraData.clipDistance()) * edge , we use a fixed large clipDistance, and + // leave the clipping only in QGeoCameraTiles::createFrustum + double farPlane = (altitude + 10000.0) * edge; + + m_cameraUp = up; + m_cameraCenter = center; + m_cameraEye = eye; + + double aspectRatio = 1.0 * m_screenSize.width() / m_screenSize.height(); + float halfWidth = 1 * apertureSize; + float halfHeight = 1 * apertureSize; + halfWidth *= aspectRatio; + + m_projectionMatrix.setToIdentity(); + m_projectionMatrix.frustum(-halfWidth, halfWidth, -halfHeight, halfHeight, nearPlane, farPlane); +} + +class QGeoTiledMapTileContainerNode : public QSGTransformNode +{ +public: + void addChild(const QGeoTileSpec &spec, QSGImageNode *node) + { + tiles.insert(spec, node); + appendChildNode(node); + } + QHash tiles; +}; + +class QGeoTiledMapRootNode : public QSGClipNode +{ +public: + QGeoTiledMapRootNode() + : isTextureLinear(false) + , geometry(QSGGeometry::defaultAttributes_Point2D(), 4) + , root(new QSGTransformNode()) + , tiles(new QGeoTiledMapTileContainerNode()) + , wrapLeft(new QGeoTiledMapTileContainerNode()) + , wrapRight(new QGeoTiledMapTileContainerNode()) + { + setIsRectangular(true); + setGeometry(&geometry); + root->appendChildNode(tiles); + root->appendChildNode(wrapLeft); + root->appendChildNode(wrapRight); + appendChildNode(root); + } + + ~QGeoTiledMapRootNode() + { + qDeleteAll(textures); + } + + void setClipRect(const QRect &rect) + { + if (rect != clipRect) { + QSGGeometry::updateRectGeometry(&geometry, rect); + QSGClipNode::setClipRect(rect); + clipRect = rect; + markDirty(DirtyGeometry); + } + } + + void updateTiles(QGeoTiledMapTileContainerNode *root, + QGeoTiledMapScenePrivate *d, + double camAdjust, + QQuickWindow *window, + bool ogl); + + bool isTextureLinear; + + QSGGeometry geometry; + QRect clipRect; + + QSGTransformNode *root; + + QGeoTiledMapTileContainerNode *tiles; // The majority of the tiles + QGeoTiledMapTileContainerNode *wrapLeft; // When zoomed out, the tiles that wrap around on the left. + QGeoTiledMapTileContainerNode *wrapRight; // When zoomed out, the tiles that wrap around on the right + + QHash textures; +}; + +static bool qgeotiledmapscene_isTileInViewport_Straight(const QRectF &tileRect, const QMatrix4x4 &matrix) +{ + const QRectF boundingRect = QRectF(matrix * tileRect.topLeft(), matrix * tileRect.bottomRight()); + return QRectF(-1, -1, 2, 2).intersects(boundingRect); +} + +static bool qgeotiledmapscene_isTileInViewport_rotationTilt(const QRectF &tileRect, const QMatrix4x4 &matrix) +{ + // Transformed corners + const QPointF tlt = matrix * tileRect.topLeft(); + const QPointF trt = matrix * tileRect.topRight(); + const QPointF blt = matrix * tileRect.bottomLeft(); + const QPointF brt = matrix * tileRect.bottomRight(); + + const QRectF boundingRect = QRectF(QPointF(qMin(qMin(qMin(tlt.x(), trt.x()), blt.x()), brt.x()) + ,qMax(qMax(qMax(tlt.y(), trt.y()), blt.y()), brt.y())) + ,QPointF(qMax(qMax(qMax(tlt.x(), trt.x()), blt.x()), brt.x()) + ,qMin(qMin(qMin(tlt.y(), trt.y()), blt.y()), brt.y())) + ); + return QRectF(-1, -1, 2, 2).intersects(boundingRect); +} + +static bool qgeotiledmapscene_isTileInViewport(const QRectF &tileRect, const QMatrix4x4 &matrix, const bool straight) +{ + if (straight) + return qgeotiledmapscene_isTileInViewport_Straight(tileRect, matrix); + return qgeotiledmapscene_isTileInViewport_rotationTilt(tileRect, matrix); +} + +void QGeoTiledMapRootNode::updateTiles(QGeoTiledMapTileContainerNode *root, + QGeoTiledMapScenePrivate *d, + double camAdjust, + QQuickWindow *window, + bool ogl) +{ + // Set up the matrix... + QDoubleVector3D eye = d->m_cameraEye; + eye.setX(eye.x() + camAdjust); + QDoubleVector3D center = d->m_cameraCenter; + center.setX(center.x() + camAdjust); + QMatrix4x4 cameraMatrix; + cameraMatrix.lookAt(toVector3D(eye), toVector3D(center), toVector3D(d->m_cameraUp)); + root->setMatrix(d->m_projectionMatrix * cameraMatrix); + + const QSet tilesInSG = QSet::fromList(root->tiles.keys()); + const QSet toRemove = tilesInSG - d->m_visibleTiles; + const QSet toAdd = d->m_visibleTiles - tilesInSG; + + for (const QGeoTileSpec &s : toRemove) + delete root->tiles.take(s); + bool straight = !d->isTiltedOrRotated(); + bool overzooming; + qreal pixelRatio = window->effectiveDevicePixelRatio(); + for (QHash::iterator it = root->tiles.begin(); + it != root->tiles.end(); ) { + QSGImageNode *node = it.value(); + bool ok = d->buildGeometry(it.key(), node, overzooming) + && qgeotiledmapscene_isTileInViewport(node->rect(), root->matrix(), straight); + + QSGNode::DirtyState dirtyBits = 0; + + if (!ok) { + it = root->tiles.erase(it); + delete node; + } else { + if (isTextureLinear != d->m_linearScaling) { + if (node->texture()->textureSize().width() > d->m_tileSize * pixelRatio) { + node->setFiltering(QSGTexture::Linear); // With mipmapping QSGTexture::Nearest generates artifacts + node->setMipmapFiltering(QSGTexture::Linear); + } else { + node->setFiltering((d->m_linearScaling || overzooming) ? QSGTexture::Linear : QSGTexture::Nearest); + } +#if QT_CONFIG(opengl) + if (ogl) + static_cast(node)->setAnisotropyLevel(QSGTexture::Anisotropy16x); +#else + Q_UNUSED(ogl) +#endif + dirtyBits |= QSGNode::DirtyMaterial; + } + if (dirtyBits != 0) + node->markDirty(dirtyBits); + it++; + } + } + + for (const QGeoTileSpec &s : toAdd) { + QGeoTileTexture *tileTexture = d->m_textures.value(s).data(); + if (!tileTexture || tileTexture->image.isNull()) + continue; + QSGImageNode *tileNode = window->createImageNode(); + // note: setTexture will update coordinates so do it here, before we buildGeometry + tileNode->setTexture(textures.value(s)); + if (d->buildGeometry(s, tileNode, overzooming) + && qgeotiledmapscene_isTileInViewport(tileNode->rect(), root->matrix(), straight)) { + if (tileNode->texture()->textureSize().width() > d->m_tileSize * pixelRatio) { + tileNode->setFiltering(QSGTexture::Linear); // with mipmapping QSGTexture::Nearest generates artifacts + tileNode->setMipmapFiltering(QSGTexture::Linear); + } else { + tileNode->setFiltering((d->m_linearScaling || overzooming) ? QSGTexture::Linear : QSGTexture::Nearest); + } +#if QT_CONFIG(opengl) + if (ogl) + static_cast(tileNode)->setAnisotropyLevel(QSGTexture::Anisotropy16x); +#endif + root->addChild(s, tileNode); + } else { + delete tileNode; + } + } +} + +QSGNode *QGeoTiledMapScene::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window) +{ + Q_D(QGeoTiledMapScene); + float w = d->m_screenSize.width(); + float h = d->m_screenSize.height(); + if (w <= 0 || h <= 0) { + delete oldNode; + return 0; + } + + bool isOpenGL = (window->rendererInterface()->graphicsApi() == QSGRendererInterface::OpenGL); + QGeoTiledMapRootNode *mapRoot = static_cast(oldNode); + if (!mapRoot) + mapRoot = new QGeoTiledMapRootNode(); + + // Setting clip rect to fullscreen, as now the map can never be smaller than the viewport. + mapRoot->setClipRect(QRect(0, 0, w, h)); + + QMatrix4x4 itemSpaceMatrix; + itemSpaceMatrix.scale(w / 2, h / 2); + itemSpaceMatrix.translate(1, 1); + itemSpaceMatrix.scale(1, -1); + mapRoot->root->setMatrix(itemSpaceMatrix); + + if (d->m_dropTextures) { + for (const QGeoTileSpec &s : mapRoot->tiles->tiles.keys()) + delete mapRoot->tiles->tiles.take(s); + for (const QGeoTileSpec &s : mapRoot->wrapLeft->tiles.keys()) + delete mapRoot->wrapLeft->tiles.take(s); + for (const QGeoTileSpec &s : mapRoot->wrapRight->tiles.keys()) + delete mapRoot->wrapRight->tiles.take(s); + for (const QGeoTileSpec &spec : mapRoot->textures.keys()) + mapRoot->textures.take(spec)->deleteLater(); + d->m_dropTextures = false; + } + + // Evicting loZL tiles temporarily used in place of hiZL ones + if (d->m_updatedTextures.size()) { + const QVector &toRemove = d->m_updatedTextures; + for (const QGeoTileSpec &s : toRemove) { + if (mapRoot->tiles->tiles.contains(s)) + delete mapRoot->tiles->tiles.take(s); + + if (mapRoot->wrapLeft->tiles.contains(s)) + delete mapRoot->wrapLeft->tiles.take(s); + + if (mapRoot->wrapRight->tiles.contains(s)) + delete mapRoot->wrapRight->tiles.take(s); + + if (mapRoot->textures.contains(s)) + mapRoot->textures.take(s)->deleteLater(); + } + d->m_updatedTextures.clear(); + } + + const QSet textures = QSet::fromList(mapRoot->textures.keys()); + const QSet toRemove = textures - d->m_visibleTiles; + const QSet toAdd = d->m_visibleTiles - textures; + + for (const QGeoTileSpec &spec : toRemove) + mapRoot->textures.take(spec)->deleteLater(); + for (const QGeoTileSpec &spec : toAdd) { + QGeoTileTexture *tileTexture = d->m_textures.value(spec).data(); + if (!tileTexture || tileTexture->image.isNull()) + continue; + mapRoot->textures.insert(spec, window->createTextureFromImage(tileTexture->image)); + } + + double sideLength = d->m_scaleFactor * d->m_tileSize * d->m_sideLength; + mapRoot->updateTiles(mapRoot->tiles, d, 0, window, isOpenGL); + mapRoot->updateTiles(mapRoot->wrapLeft, d, +sideLength, window, isOpenGL); + mapRoot->updateTiles(mapRoot->wrapRight, d, -sideLength, window, isOpenGL); + + mapRoot->isTextureLinear = d->m_linearScaling; + + return mapRoot; +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeotiledmapscene_p.h b/src/location/maps/qgeotiledmapscene_p.h new file mode 100644 index 0000000..f6a8f71 --- /dev/null +++ b/src/location/maps/qgeotiledmapscene_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOTILEDMAPSCENE_P_H +#define QGEOTILEDMAPSCENE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCameraData; +class QGeoTileSpec; +class QDoubleVector2D; +class QGeoTileTexture; +class QSGNode; +class QQuickWindow; +class QGeoTiledMapScenePrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTiledMapScene : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoTiledMapScene) +public: + explicit QGeoTiledMapScene(QObject *parent = 0); + virtual ~QGeoTiledMapScene(); + + void setScreenSize(const QSize &size); + void setTileSize(int tileSize); + void setCameraData(const QGeoCameraData &cameraData); + + void setVisibleTiles(const QSet &tiles); + const QSet &visibleTiles() const; + + void addTile(const QGeoTileSpec &spec, QSharedPointer texture); + + QSGNode *updateSceneGraph(QSGNode *oldNode, QQuickWindow *window); + + QSet texturedTiles(); + + void clearTexturedTiles(); + +Q_SIGNALS: + void newTilesVisible(const QSet &newTiles); + +private: + void updateSceneParameters(); + + Q_DISABLE_COPY(QGeoTiledMapScene) +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEDMAPSCENE_P_H diff --git a/src/location/maps/qgeotilefetcher.cpp b/src/location/maps/qgeotilefetcher.cpp new file mode 100644 index 0000000..b3f7021 --- /dev/null +++ b/src/location/maps/qgeotilefetcher.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qgeomappingmanagerengine_p.h" +#include "qgeotilefetcher_p.h" +#include "qgeotilefetcher_p_p.h" +#include "qgeotiledmapreply_p.h" +#include "qgeotilespec_p.h" +#include "qgeotiledmap_p.h" + +QT_BEGIN_NAMESPACE + +QGeoTileFetcher::QGeoTileFetcher(QGeoMappingManagerEngine *parent) +: QObject(*new QGeoTileFetcherPrivate(), parent) +{ + Q_D(QGeoTileFetcher); + + d->enabled_ = true; + d->engine_ = parent; +} + +QGeoTileFetcher::QGeoTileFetcher(QGeoTileFetcherPrivate &dd, QGeoMappingManagerEngine *parent) +: QObject(dd,parent) +{ + Q_D(QGeoTileFetcher); + d->enabled_ = true; + d->engine_ = parent; +} + +QGeoTileFetcher::~QGeoTileFetcher() +{ +} + +void QGeoTileFetcher::updateTileRequests(const QSet &tilesAdded, + const QSet &tilesRemoved) +{ + Q_D(QGeoTileFetcher); + + QMutexLocker ml(&d->queueMutex_); + + cancelTileRequests(tilesRemoved); + + d->queue_ += tilesAdded.toList(); + + if (d->enabled_ && initialized() && !d->queue_.isEmpty() && !d->timer_.isActive()) + d->timer_.start(0, this); +} + +void QGeoTileFetcher::cancelTileRequests(const QSet &tiles) +{ + Q_D(QGeoTileFetcher); + + typedef QSet::const_iterator tile_iter; + // No need to lock: called only in updateTileRequests + tile_iter tile = tiles.constBegin(); + tile_iter end = tiles.constEnd(); + for (; tile != end; ++tile) { + QGeoTiledMapReply *reply = d->invmap_.value(*tile, 0); + if (reply) { + d->invmap_.remove(*tile); + reply->abort(); + if (reply->isFinished()) + reply->deleteLater(); + } + d->queue_.removeAll(*tile); + } +} + +void QGeoTileFetcher::requestNextTile() +{ + Q_D(QGeoTileFetcher); + + QMutexLocker ml(&d->queueMutex_); + + if (!d->enabled_) + return; + + if (d->queue_.isEmpty()) + return; + + QGeoTileSpec ts = d->queue_.takeFirst(); + if (d->queue_.isEmpty()) + d->timer_.stop(); + + // Check against min/max zoom to prevent sending requests for not existing objects + const QGeoCameraCapabilities & cameraCaps = d->engine_->cameraCapabilities(ts.mapId()); + // the ZL in QGeoTileSpec is relative to the native tile size of the provider. + // It gets denormalized in QGeoTiledMap. + if (ts.zoom() < cameraCaps.minimumZoomLevel() || ts.zoom() > cameraCaps.maximumZoomLevel() || !fetchingEnabled()) + return; + + QGeoTiledMapReply *reply = getTileImage(ts); + if (!reply) + return; + + if (reply->isFinished()) { + handleReply(reply, ts); + } else { + connect(reply, + SIGNAL(finished()), + this, + SLOT(finished()), + Qt::QueuedConnection); + + d->invmap_.insert(ts, reply); + } +} + +void QGeoTileFetcher::finished() +{ + Q_D(QGeoTileFetcher); + + QMutexLocker ml(&d->queueMutex_); + + QGeoTiledMapReply *reply = qobject_cast(sender()); + if (!reply) + return; + + QGeoTileSpec spec = reply->tileSpec(); + + if (!d->invmap_.contains(spec)) { + reply->deleteLater(); + return; + } + + d->invmap_.remove(spec); + + handleReply(reply, spec); +} + +void QGeoTileFetcher::timerEvent(QTimerEvent *event) +{ + Q_D(QGeoTileFetcher); + if (event->timerId() != d->timer_.timerId()) { + QObject::timerEvent(event); + return; + } + + QMutexLocker ml(&d->queueMutex_); + if (d->queue_.isEmpty() || !initialized()) { + d->timer_.stop(); + return; + } + ml.unlock(); + + requestNextTile(); +} + +bool QGeoTileFetcher::initialized() const +{ + return true; +} + +bool QGeoTileFetcher::fetchingEnabled() const +{ + return true; +} + +void QGeoTileFetcher::handleReply(QGeoTiledMapReply *reply, const QGeoTileSpec &spec) +{ + Q_D(QGeoTileFetcher); + + if (!d->enabled_) { + reply->deleteLater(); + return; + } + + if (reply->error() == QGeoTiledMapReply::NoError) { + emit tileFinished(spec, reply->mapImageData(), reply->mapImageFormat()); + } else { + emit tileError(spec, reply->errorString()); + } + + reply->deleteLater(); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoTileFetcherPrivate::QGeoTileFetcherPrivate() +: QObjectPrivate(), enabled_(false), engine_(0) +{ +} + +QGeoTileFetcherPrivate::~QGeoTileFetcherPrivate() +{ +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeotilefetcher_p.h b/src/location/maps/qgeotilefetcher_p.h new file mode 100644 index 0000000..8888042 --- /dev/null +++ b/src/location/maps/qgeotilefetcher_p.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEFETCHER_H +#define QGEOTILEFETCHER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include "qgeomaptype_p.h" +#include "qgeotiledmappingmanagerengine_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoMapRequestOptions; + +class QGeoTileFetcherPrivate; +class QGeoTiledMappingManagerEngine; +class QGeoTiledMapReply; +class QGeoTileSpec; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTileFetcher : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoTileFetcher) + +public: + QGeoTileFetcher(QGeoMappingManagerEngine *parent); + virtual ~QGeoTileFetcher(); + +public Q_SLOTS: + void updateTileRequests(const QSet &tilesAdded, const QSet &tilesRemoved); + +private Q_SLOTS: + void cancelTileRequests(const QSet &tiles); + void requestNextTile(); + void finished(); + +Q_SIGNALS: + void tileFinished(const QGeoTileSpec &spec, const QByteArray &bytes, const QString &format); + void tileError(const QGeoTileSpec &spec, const QString &errorString); + +protected: + QGeoTileFetcher(QGeoTileFetcherPrivate &dd, QGeoMappingManagerEngine *parent); + + void timerEvent(QTimerEvent *event); + QAbstractGeoTileCache::CacheAreas cacheHint() const; + virtual bool initialized() const; + virtual bool fetchingEnabled() const; + +private: + + virtual QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec) = 0; + virtual void handleReply(QGeoTiledMapReply *reply, const QGeoTileSpec &spec); + + Q_DISABLE_COPY(QGeoTileFetcher) + friend class QGeoTiledMappingManagerEngine; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeotilefetcher_p_p.h b/src/location/maps/qgeotilefetcher_p_p.h new file mode 100644 index 0000000..8e95556 --- /dev/null +++ b/src/location/maps/qgeotilefetcher_p_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEFETCHER_P_H +#define QGEOTILEFETCHER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qgeomaptype_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoTileSpec; +class QGeoTiledMapReply; +class QGeoMappingManagerEngine; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTileFetcherPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGeoTileFetcher) +public: + QGeoTileFetcherPrivate(); + virtual ~QGeoTileFetcherPrivate(); + + bool enabled_; + QBasicTimer timer_; + QMutex queueMutex_; + QList queue_; + QHash invmap_; + QGeoMappingManagerEngine *engine_; + +private: + Q_DISABLE_COPY(QGeoTileFetcherPrivate) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/maps/qgeotilerequestmanager.cpp b/src/location/maps/qgeotilerequestmanager.cpp new file mode 100644 index 0000000..d4d94ad --- /dev/null +++ b/src/location/maps/qgeotilerequestmanager.cpp @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeotilerequestmanager_p.h" +#include "qgeotilespec_p.h" +#include "qgeotiledmap_p.h" +#include "qgeotiledmappingmanagerengine_p.h" +#include "qabstractgeotilecache_p.h" +#include + +QT_BEGIN_NAMESPACE + +class RetryFuture; + +class QGeoTileRequestManagerPrivate +{ +public: + explicit QGeoTileRequestManagerPrivate(QGeoTiledMap *map, QGeoTiledMappingManagerEngine *engine); + ~QGeoTileRequestManagerPrivate(); + + QGeoTiledMap *m_map; + QPointer m_engine; + + QMap > requestTiles(const QSet &tiles); + void tileError(const QGeoTileSpec &tile, const QString &errorString); + + QHash m_retries; + QHash > m_futures; + QSet m_requested; + + void tileFetched(const QGeoTileSpec &spec); +}; + +QGeoTileRequestManager::QGeoTileRequestManager(QGeoTiledMap *map, QGeoTiledMappingManagerEngine *engine) + : d_ptr(new QGeoTileRequestManagerPrivate(map, engine)) +{ + +} + +QGeoTileRequestManager::~QGeoTileRequestManager() +{ + +} + +QMap > QGeoTileRequestManager::requestTiles(const QSet &tiles) +{ + return d_ptr->requestTiles(tiles); +} + +void QGeoTileRequestManager::tileFetched(const QGeoTileSpec &spec) +{ + d_ptr->tileFetched(spec); +} + +QSharedPointer QGeoTileRequestManager::tileTexture(const QGeoTileSpec &spec) +{ + if (d_ptr->m_engine) + return d_ptr->m_engine->getTileTexture(spec); + else + return QSharedPointer(); +} + +void QGeoTileRequestManager::tileError(const QGeoTileSpec &tile, const QString &errorString) +{ + d_ptr->tileError(tile, errorString); +} + +QGeoTileRequestManagerPrivate::QGeoTileRequestManagerPrivate(QGeoTiledMap *map,QGeoTiledMappingManagerEngine *engine) + : m_map(map), + m_engine(engine) +{ +} + +QGeoTileRequestManagerPrivate::~QGeoTileRequestManagerPrivate() +{ +} + +QMap > QGeoTileRequestManagerPrivate::requestTiles(const QSet &tiles) +{ + QSet cancelTiles = m_requested - tiles; + QSet requestTiles = tiles - m_requested; + QSet cached; +// int tileSize = tiles.size(); +// int newTiles = requestTiles.size(); + + typedef QSet::const_iterator iter; + + QMap > cachedTex; + + // remove tiles in cache from request tiles + if (!m_engine.isNull()) { + iter i = requestTiles.constBegin(); + iter end = requestTiles.constEnd(); + for (; i != end; ++i) { + QGeoTileSpec tile = *i; + QSharedPointer tex = m_engine->getTileTexture(tile); + if (tex) { + if (!tex->image.isNull()) + cachedTex.insert(tile, tex); + cached.insert(tile); + } else { + // Try to use textures from lower zoom levels, but still request the proper tile + QGeoTileSpec spec = tile; + const int endRange = qMax(0, tile.zoom() - 4); // Using up to 4 zoom levels up. 4 is arbitrary. + for (int z = tile.zoom() - 1; z >= endRange; z--) { + int denominator = 1 << (tile.zoom() - z); + spec.setZoom(z); + spec.setX(tile.x() / denominator); + spec.setY(tile.y() / denominator); + QSharedPointer t = m_engine->getTileTexture(spec); + if (t && !t->image.isNull()) { + cachedTex.insert(tile, t); + break; + } + } + } + } + } + + requestTiles -= cached; + + m_requested -= cancelTiles; + m_requested += requestTiles; + +// qDebug() << "required # tiles: " << tileSize << ", new tiles: " << newTiles << ", total server requests: " << requested_.size(); + + if (!requestTiles.isEmpty() || !cancelTiles.isEmpty()) { + if (!m_engine.isNull()) { +// qDebug() << "new server requests: " << requestTiles.size() << ", server cancels: " << cancelTiles.size(); + m_engine->updateTileRequests(m_map, requestTiles, cancelTiles); + + // Remove any cancelled tiles from the error retry hash to avoid + // re-using the numbers for a totally different request cycle. + iter i = cancelTiles.constBegin(); + iter end = cancelTiles.constEnd(); + for (; i != end; ++i) { + m_retries.remove(*i); + m_futures.remove(*i); + } + } + } + + return cachedTex; +} + +void QGeoTileRequestManagerPrivate::tileFetched(const QGeoTileSpec &spec) +{ + m_map->updateTile(spec); + m_requested.remove(spec); + m_retries.remove(spec); + m_futures.remove(spec); +} + +// Represents a tile that needs to be retried after a certain period of time +class RetryFuture : public QObject +{ + Q_OBJECT +public: + RetryFuture(const QGeoTileSpec &tile, QGeoTiledMap *map, QGeoTiledMappingManagerEngine* engine, QObject *parent = 0); + +public Q_SLOTS: + void retry(); + +private: + QGeoTileSpec m_tile; + QGeoTiledMap *m_map; + QPointer m_engine; +}; + +RetryFuture::RetryFuture(const QGeoTileSpec &tile, QGeoTiledMap *map, QGeoTiledMappingManagerEngine* engine, QObject *parent) + : QObject(parent), m_tile(tile), m_map(map), m_engine(engine) +{} + +void RetryFuture::retry() +{ + QSet requestTiles; + QSet cancelTiles; + requestTiles.insert(m_tile); + if (!m_engine.isNull()) + m_engine->updateTileRequests(m_map, requestTiles, cancelTiles); +} + +void QGeoTileRequestManagerPrivate::tileError(const QGeoTileSpec &tile, const QString &errorString) +{ + if (m_requested.contains(tile)) { + int count = m_retries.value(tile, 0); + m_retries.insert(tile, count + 1); + + if (count >= 5) { + qWarning("QGeoTileRequestManager: Failed to fetch tile (%d,%d,%d) 5 times, giving up. " + "Last error message was: '%s'", + tile.x(), tile.y(), tile.zoom(), qPrintable(errorString)); + m_requested.remove(tile); + m_retries.remove(tile); + m_futures.remove(tile); + + } else { + // Exponential time backoff when retrying + int delay = (1 << count) * 500; + + QSharedPointer future(new RetryFuture(tile,m_map,m_engine)); + m_futures.insert(tile, future); + + QTimer::singleShot(delay, future.data(), SLOT(retry())); + // Passing .data() to singleShot is ok -- Qt will clean up the + // connection if the target qobject is deleted + } + } +} + +#include "qgeotilerequestmanager.moc" + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeotilerequestmanager_p.h b/src/location/maps/qgeotilerequestmanager_p.h new file mode 100644 index 0000000..000e3f3 --- /dev/null +++ b/src/location/maps/qgeotilerequestmanager_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOTILEREQUESTMANAGER_P_H +#define QGEOTILEREQUESTMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoTiledMap; +class QGeoTiledMappingManagerEngine; +class QGeoTileSpec; +class QGeoTileTexture; + +class QGeoTileRequestManagerPrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTileRequestManager +{ +public: + explicit QGeoTileRequestManager(QGeoTiledMap *map, QGeoTiledMappingManagerEngine *engine); + ~QGeoTileRequestManager(); + + QMap > requestTiles(const QSet &tiles); + + void tileError(const QGeoTileSpec &tile, const QString &errorString); + void tileFetched(const QGeoTileSpec &spec); + QSharedPointer tileTexture(const QGeoTileSpec &spec); + +private: + QScopedPointer d_ptr; + Q_DISABLE_COPY(QGeoTileRequestManager) +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEREQUESTMANAGER_P_H diff --git a/src/location/maps/qgeotilespec.cpp b/src/location/maps/qgeotilespec.cpp new file mode 100644 index 0000000..2e04b53 --- /dev/null +++ b/src/location/maps/qgeotilespec.cpp @@ -0,0 +1,241 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotilespec_p.h" +#include "qgeotilespec_p_p.h" + +#include + +QT_BEGIN_NAMESPACE + +QGeoTileSpec::QGeoTileSpec() + : d(QSharedDataPointer(new QGeoTileSpecPrivate())) {} + +QGeoTileSpec::QGeoTileSpec(const QString &plugin, int mapId, int zoom, int x, int y, int version) + : d(QSharedDataPointer(new QGeoTileSpecPrivate(plugin, mapId, zoom, x, y, version))) {} + +QGeoTileSpec::QGeoTileSpec(const QGeoTileSpec &other) + : d(other.d) {} + +QGeoTileSpec::~QGeoTileSpec() { +} + +QGeoTileSpec &QGeoTileSpec::operator = (const QGeoTileSpec &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +QString QGeoTileSpec::plugin() const +{ + return d->plugin_; +} + +void QGeoTileSpec::setZoom(int zoom) +{ + d->zoom_ = zoom; +} + +int QGeoTileSpec::zoom() const +{ + return d->zoom_; +} + +void QGeoTileSpec::setX(int x) +{ + d->x_ = x; +} + +int QGeoTileSpec::x() const +{ + return d->x_; +} + +void QGeoTileSpec::setY(int y) +{ + d->y_ = y; +} + +int QGeoTileSpec::y() const +{ + return d->y_; +} + +void QGeoTileSpec::setMapId(int mapId) +{ + d->mapId_ = mapId; +} + +int QGeoTileSpec::mapId() const +{ + return d->mapId_; +} + +void QGeoTileSpec::setVersion(int version) +{ + d->version_ = version; +} + +int QGeoTileSpec::version() const +{ + return d->version_; +} + +bool QGeoTileSpec::operator == (const QGeoTileSpec &rhs) const +{ + return (*(d.constData()) == *(rhs.d.constData())); +} + +bool QGeoTileSpec::operator < (const QGeoTileSpec &rhs) const +{ + return (*(d.constData()) < *(rhs.d.constData())); +} + +unsigned int qHash(const QGeoTileSpec &spec) +{ + unsigned int result = (qHash(spec.plugin()) * 13) % 31; + result += ((spec.mapId() * 17) % 31) << 5; + result += ((spec.zoom() * 19) % 31) << 10; + result += ((spec.x() * 23) % 31) << 15; + result += ((spec.y() * 29) % 31) << 20; + result += (spec.version() % 3) << 25; + return result; +} + +QDebug operator<< (QDebug dbg, const QGeoTileSpec &spec) +{ + dbg << spec.plugin() << spec.mapId() << spec.zoom() << spec.x() << spec.y() << spec.version(); + return dbg; +} + +QGeoTileSpecPrivate::QGeoTileSpecPrivate() + : mapId_(0), + zoom_(-1), + x_(-1), + y_(-1), + version_(-1) {} + +QGeoTileSpecPrivate::QGeoTileSpecPrivate(const QGeoTileSpecPrivate &other) + : QSharedData(other), + plugin_(other.plugin_), + mapId_(other.mapId_), + zoom_(other.zoom_), + x_(other.x_), + y_(other.y_), + version_(other.version_) {} + +QGeoTileSpecPrivate::QGeoTileSpecPrivate(const QString &plugin, int mapId, int zoom, int x, int y, int version) + : plugin_(plugin), + mapId_(mapId), + zoom_(zoom), + x_(x), + y_(y), + version_(version) {} + +QGeoTileSpecPrivate::~QGeoTileSpecPrivate() {} + +QGeoTileSpecPrivate &QGeoTileSpecPrivate::operator = (const QGeoTileSpecPrivate &other) +{ + if (this == &other) + return *this; + + plugin_ = other.plugin_; + mapId_ = other.mapId_; + zoom_ = other.zoom_; + x_ = other.x_; + y_ = other.y_; + version_ = other.version_; + + return *this; +} + +bool QGeoTileSpecPrivate::operator == (const QGeoTileSpecPrivate &rhs) const +{ + if (plugin_ != rhs.plugin_) + return false; + + if (mapId_ != rhs.mapId_) + return false; + + if (zoom_ != rhs.zoom_) + return false; + + if (x_ != rhs.x_) + return false; + + if (y_ != rhs.y_) + return false; + + if (version_ != rhs.version_) + return false; + + return true; +} + +bool QGeoTileSpecPrivate::operator < (const QGeoTileSpecPrivate &rhs) const +{ + if (plugin_ < rhs.plugin_) + return true; + if (plugin_ > rhs.plugin_) + return false; + + if (mapId_ < rhs.mapId_) + return true; + if (mapId_ > rhs.mapId_) + return false; + + if (zoom_ < rhs.zoom_) + return true; + if (zoom_ > rhs.zoom_) + return false; + + if (x_ < rhs.x_) + return true; + if (x_ > rhs.x_) + return false; + + if (y_ < rhs.y_) + return true; + if (y_ > rhs.y_) + return false; + + return (version_ < rhs.version_); +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qgeotilespec_p.h b/src/location/maps/qgeotilespec_p.h new file mode 100644 index 0000000..e0120e9 --- /dev/null +++ b/src/location/maps/qgeotilespec_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILESPEC_H +#define QGEOTILESPEC_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoTileSpecPrivate; + +class Q_LOCATION_PRIVATE_EXPORT QGeoTileSpec +{ +public: + QGeoTileSpec(); + QGeoTileSpec(const QGeoTileSpec &other); + QGeoTileSpec(const QString &plugin, int mapId, int zoom, int x, int y, int version = -1); + ~QGeoTileSpec(); + + QGeoTileSpec &operator = (const QGeoTileSpec &other); + + QString plugin() const; + + void setZoom(int zoom); + int zoom() const; + + void setX(int x); + int x() const; + + void setY(int y); + int y() const; + + void setMapId(int mapId); + int mapId() const; + + void setVersion(int version); + int version() const; + + bool operator == (const QGeoTileSpec &rhs) const; + bool operator < (const QGeoTileSpec &rhs) const; + +private: + QSharedDataPointer d; +}; + +Q_LOCATION_PRIVATE_EXPORT unsigned int qHash(const QGeoTileSpec &spec); + +Q_LOCATION_PRIVATE_EXPORT QDebug operator<<(QDebug, const QGeoTileSpec &); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoTileSpec) + +#endif // QGEOTILESPEC_H diff --git a/src/location/maps/qgeotilespec_p_p.h b/src/location/maps/qgeotilespec_p_p.h new file mode 100644 index 0000000..1e7442f --- /dev/null +++ b/src/location/maps/qgeotilespec_p_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOTILESPEC_P_H +#define QGEOTILESPEC_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoTileSpecPrivate : public QSharedData +{ +public: + QGeoTileSpecPrivate(); + QGeoTileSpecPrivate(const QGeoTileSpecPrivate &other); + QGeoTileSpecPrivate(const QString &plugin, int mapId, int zoom, int x, int y, int version); + ~QGeoTileSpecPrivate(); + + QGeoTileSpecPrivate &operator = (const QGeoTileSpecPrivate &other); + + bool operator == (const QGeoTileSpecPrivate &rhs) const; + bool operator < (const QGeoTileSpecPrivate &rhs) const; + + QString plugin_; + int mapId_; + int zoom_; + int x_; + int y_; + int version_; +}; + +QT_END_NAMESPACE + +#endif // QGEOTILESPEC_P_H diff --git a/src/location/maps/qnavigationmanager.cpp b/src/location/maps/qnavigationmanager.cpp new file mode 100644 index 0000000..5372e3a --- /dev/null +++ b/src/location/maps/qnavigationmanager.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnavigationmanager_p.h" +#include "qnavigationmanagerengine_p.h" + +QT_BEGIN_NAMESPACE + +class QNavigationManagerPrivate +{ +public: + QNavigationManagerPrivate(); + ~QNavigationManagerPrivate(); + + QNavigationManagerEngine *engine = nullptr; + QDeclarativeNavigatorPrivate *navigator = nullptr; + QList parameters; + +private: + Q_DISABLE_COPY(QNavigationManagerPrivate) +}; + +QNavigationManagerPrivate::QNavigationManagerPrivate() +{ + +} + +QNavigationManagerPrivate::~QNavigationManagerPrivate() +{ + delete engine; + engine = nullptr; +} + +QNavigationManager::~QNavigationManager() +{ + delete d_ptr; +} + +QString QNavigationManager::managerName() const +{ + return d_ptr->engine->managerName(); +} + +int QNavigationManager::managerVersion() const +{ + return d_ptr->engine->managerVersion(); +} + +QNavigationManagerEngine *QNavigationManager::engine() +{ + return d_ptr->engine; +} + +bool QNavigationManager::isInitialized() const +{ + return d_ptr->engine->isInitialized(); +} + +void QNavigationManager::setNavigator(QDeclarativeNavigatorPrivate *navigator) +{ + d_ptr->navigator = navigator; +} + +QDeclarativeNavigatorPrivate *QNavigationManager::declarativeNavigator() const +{ + return d_ptr->navigator; +} + +void QNavigationManager::setLocale(const QLocale &locale) +{ + d_ptr->engine->setLocale(locale); +} + +QLocale QNavigationManager::locale() const +{ + return d_ptr->engine->locale(); +} + +void QNavigationManager::setParameters(const QList ¶meters) +{ + d_ptr->parameters = parameters; +} + +QList QNavigationManager::parameters() const +{ + return d_ptr->parameters; +} + +bool QNavigationManager::ready() const +{ + return d_ptr->engine->ready(*d_ptr->navigator, d_ptr->parameters); +} + +bool QNavigationManager::start() +{ + return d_ptr->engine->start(*d_ptr->navigator, d_ptr->parameters); +} + +bool QNavigationManager::stop() +{ + return d_ptr->engine->stop(*d_ptr->navigator); +} + +bool QNavigationManager::active() const +{ + return d_ptr->engine->active(*d_ptr->navigator); +} + +QNavigationManager::QNavigationManager(QNavigationManagerEngine *engine, QObject *parent) : QObject(parent), + d_ptr(new QNavigationManagerPrivate) +{ + d_ptr->engine = engine; + if (!d_ptr->engine) { + qFatal("The navigation manager engine that was set for this mapping manager was NULL."); + } + + connect(d_ptr->engine, + SIGNAL(initialized()), + this, + SIGNAL(initialized()), + Qt::QueuedConnection); +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qnavigationmanager_p.h b/src/location/maps/qnavigationmanager_p.h new file mode 100644 index 0000000..1d8c172 --- /dev/null +++ b/src/location/maps/qnavigationmanager_p.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNAVIGATIONMANAGER_P_H +#define QNAVIGATIONMANAGER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QNavigationManagerEngine; +class QNavigationManagerPrivate; +class QDeclarativeNavigatorPrivate; +class QDeclarativeGeoWaypoint; +class QGeoRoute; +class QGeoRouteSegment; + +class Q_LOCATION_PRIVATE_EXPORT QNavigationManager : public QObject +{ + Q_OBJECT + +public: + ~QNavigationManager(); + + QString managerName() const; + int managerVersion() const; + QNavigationManagerEngine *engine(); + bool isInitialized() const; + + void setNavigator(QDeclarativeNavigatorPrivate *navigator); + QDeclarativeNavigatorPrivate *declarativeNavigator() const; + + void setLocale(const QLocale &locale); + QLocale locale() const; + + void setParameters(const QList ¶meters); + QList parameters() const; + + bool ready() const; + bool start(); + bool stop(); + bool active() const; + +Q_SIGNALS: + void initialized(); + + // These must be emitted by the engine + void activeChanged(bool active); + void waypointReached(const QDeclarativeGeoWaypoint *pos); + void destinationReached(); + void currentRouteChanged(const QGeoRoute &route); + void currentSegmentChanged(int segment); + +protected: + QNavigationManager(QNavigationManagerEngine *engine, QObject *parent = 0); + +private: + QNavigationManagerPrivate *d_ptr; + Q_DISABLE_COPY(QNavigationManager) + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; +}; + + +QT_END_NAMESPACE + +#endif // QNAVIGATIONMANAGER_P_H diff --git a/src/location/maps/qnavigationmanagerengine.cpp b/src/location/maps/qnavigationmanagerengine.cpp new file mode 100644 index 0000000..b9191e7 --- /dev/null +++ b/src/location/maps/qnavigationmanagerengine.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroute.h" +#include "qnavigationmanagerengine_p.h" + +QT_BEGIN_NAMESPACE + +class QNavigationManagerEnginePrivate +{ +public: + QString managerName; + int managerVersion; + QLocale locale; + QLocale::MeasurementSystem measurementSystem; + bool initialized = false; +}; + +QNavigationManagerEngine::QNavigationManagerEngine(const QVariantMap ¶meters, QObject *parent) + : QObject(parent) + , d_ptr(new QNavigationManagerEnginePrivate) +{ + Q_UNUSED(parameters) +} + +QNavigationManagerEngine::~QNavigationManagerEngine() +{ +} + +void QNavigationManagerEngine::setManagerName(const QString &name) +{ + d_ptr->managerName = name; +} + +QString QNavigationManagerEngine::managerName() const +{ + return d_ptr->managerName; +} + +void QNavigationManagerEngine::setManagerVersion(int version) +{ + d_ptr->managerVersion = version; +} + +int QNavigationManagerEngine::managerVersion() const +{ + return d_ptr->managerVersion; +} + +void QNavigationManagerEngine::setLocale(const QLocale &locale) +{ + d_ptr->locale = locale; +} + +QLocale QNavigationManagerEngine::locale() const +{ + return d_ptr->locale; +} + +void QNavigationManagerEngine::setMeasurementSystem(QLocale::MeasurementSystem system) +{ + d_ptr->measurementSystem = system; +} + +QLocale::MeasurementSystem QNavigationManagerEngine::measurementSystem() const +{ + return d_ptr->measurementSystem; +} + +bool QNavigationManagerEngine::isInitialized() const +{ + return d_ptr->initialized; +} + +// Subclasses are supposed to emit activeChanged from here. +bool QNavigationManagerEngine::start(QDeclarativeNavigatorPrivate & /*navigator*/, const QList & /*navigationParams*/) +{ + + return false; +} + +bool QNavigationManagerEngine::stop(QDeclarativeNavigatorPrivate & /*navigator*/) // navigator needed to find the right navi session to stop. +{ + return false; +} + +void QNavigationManagerEngine::engineInitialized() +{ + d_ptr->initialized = true; + emit initialized(); +} + +QT_END_NAMESPACE diff --git a/src/location/maps/qnavigationmanagerengine_p.h b/src/location/maps/qnavigationmanagerengine_p.h new file mode 100644 index 0000000..803050c --- /dev/null +++ b/src/location/maps/qnavigationmanagerengine_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNAVIGATIONMANAGERENGINE_H +#define QNAVIGATIONMANAGERENGINE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMap; +class QGeoMapParameter; +class QMapRouteObject; +class QNavigationManagerEnginePrivate; +class QDeclarativeNavigatorPrivate; + +class Q_LOCATION_PRIVATE_EXPORT QNavigationManagerEngine : public QObject +{ + Q_OBJECT +public: + explicit QNavigationManagerEngine(const QVariantMap ¶meters, QObject *parent = nullptr); + virtual ~QNavigationManagerEngine(); + + void setManagerName(const QString &name); + QString managerName() const; + void setManagerVersion(int version); + int managerVersion() const; + + virtual void setLocale(const QLocale &locale); + virtual QLocale locale() const; + virtual void setMeasurementSystem(QLocale::MeasurementSystem system); + virtual QLocale::MeasurementSystem measurementSystem() const; + virtual bool isInitialized() const; + virtual bool ready(const QDeclarativeNavigatorPrivate &navigator, const QList &navigationParams) = 0; + virtual bool active(const QDeclarativeNavigatorPrivate &navigator) = 0; + +signals: + void initialized(); + +public slots: + virtual bool start(QDeclarativeNavigatorPrivate &navigator, const QList &navigationParams); + virtual bool stop(QDeclarativeNavigatorPrivate &navigator); + +protected: + /*! + Marks the engine as initialized. Subclasses of QGeoMappingManagerEngine are to + call this method after performing implementation-specific initialization within + the constructor. + */ + virtual void engineInitialized(); + + QScopedPointer d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QNAVIGATIONMANAGERENGINE_H diff --git a/src/location/places/placemacro.h b/src/location/places/placemacro.h new file mode 100644 index 0000000..e0603fc --- /dev/null +++ b/src/location/places/placemacro.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLACE_MACRO_H +#define PLACE_MACRO_H + +#include + +QT_BEGIN_NAMESPACE + +#define Q_DECLARE_D_FUNC(Class) \ + inline Class##Private *d_func(); \ + inline const Class##Private *d_func() const;\ + friend class Class##Private; + +#define Q_DECLARE_COPY_CTOR(Class, BaseClass) \ + Class(const BaseClass &other); + +#define Q_IMPLEMENT_D_FUNC(Class) \ + Class##Private *Class::d_func() { return reinterpret_cast(d_ptr.data()); } \ + const Class##Private *Class::d_func() const { return reinterpret_cast(d_ptr.constData()); } + +#define Q_IMPLEMENT_COPY_CTOR(Class, BaseClass) \ + Class::Class(const BaseClass &other) : BaseClass() { Class##Private::copyIfPossible(d_ptr, other); } + +#define Q_DEFINE_PRIVATE_HELPER(Class, BaseClass, ClassType) \ + BaseClass##Private *clone() const { return new Class##Private(*this); } \ + static void copyIfPossible(QSharedDataPointer &d_ptr, const BaseClass &other) \ + { \ + if (other.type() == ClassType) \ + d_ptr = extract_d(other); \ + else \ + d_ptr = new Class##Private; \ + } + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/places.pri b/src/location/places/places.pri new file mode 100644 index 0000000..1a3796f --- /dev/null +++ b/src/location/places/places.pri @@ -0,0 +1,93 @@ +INCLUDEPATH += places + +PUBLIC_HEADERS += \ + places/placemacro.h \ +#data classes + places/qplace.h \ + places/qplaceattribute.h \ + places/qplacecontactdetail.h \ + places/qplacecategory.h \ + places/qplacecontent.h \ + places/qplacecontentreply.h \ + places/qplaceeditorial.h \ + places/qplaceimage.h \ + places/qplaceicon.h \ + places/qplaceratings.h \ + places/qplacereview.h \ + places/qplacesupplier.h \ + places/qplaceuser.h \ +#result + places/qplacesearchresult.h \ + places/qplaceresult.h \ + places/qplaceproposedsearchresult.h \ +#request classes + places/qplacecontentrequest.h \ + places/qplacematchrequest.h \ + places/qplacesearchrequest.h \ +#reply classes + places/qplacereply.h \ + places/qplacedetailsreply.h \ + places/qplaceidreply.h \ + places/qplacematchreply.h \ + places/qplacesearchreply.h \ + places/qplacesearchsuggestionreply.h \ + places/unsupportedreplies_p.h \ +#manager and engine + places/qplacemanager.h \ + places/qplacemanagerengine.h + +PRIVATE_HEADERS += \ + places/qplace_p.h \ + places/qplaceattribute_p.h \ + places/qplacecategory_p.h \ + places/qplacecontent_p.h \ + places/qplacecontactdetail_p.h \ + places/qplaceeditorial_p.h \ + places/qplaceicon_p.h \ + places/qplaceimage_p.h \ + places/qplaceratings_p.h \ + places/qplaceresult_p.h \ + places/qplaceproposedsearchresult_p.h \ + places/qplacereview_p.h \ + places/qplacesupplier_p.h \ + places/qplacesearchresult_p.h \ + places/qplacereply_p.h \ + places/qplacemanagerengine_p.h \ + places/qplacecontentrequest_p.h \ + places/qplaceuser_p.h + +SOURCES += \ +#data classes + places/qplace.cpp \ + places/qplaceattribute.cpp \ + places/qplacecategory.cpp \ + places/qplacecontactdetail.cpp \ + places/qplacecontent.cpp \ + places/qplacecontentreply.cpp \ + places/qplaceeditorial.cpp \ + places/qplaceuser.cpp \ +#result + places/qplaceicon.cpp \ + places/qplaceimage.cpp \ + places/qplaceratings.cpp \ + places/qplacereview.cpp \ + places/qplaceidreply.cpp \ + places/qplacesupplier.cpp \ +#result + places/qplacesearchresult.cpp \ + places/qplaceresult.cpp \ + places/qplaceproposedsearchresult.cpp \ +#request classes + places/qplacecontentrequest.cpp \ + places/qplacematchrequest.cpp \ + places/qplacesearchrequest.cpp \ +#reply classes + places/qplacereply.cpp \ + places/qplacedetailsreply.cpp \ + places/qplacematchreply.cpp \ + places/qplacesearchreply.cpp \ + places/qplacesearchsuggestionreply.cpp \ +#manager and engine + places/qplacemanager.cpp \ + places/qplacemanagerengine.cpp + diff --git a/src/location/places/qplace.cpp b/src/location/places/qplace.cpp new file mode 100644 index 0000000..ab115b5 --- /dev/null +++ b/src/location/places/qplace.cpp @@ -0,0 +1,814 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplace.h" +#include "qplace_p.h" + +#ifdef QPLACE_DEBUG +#include +#endif + +#include + +QT_BEGIN_NAMESPACE + +template<> +QPlacePrivate *QSharedDataPointer::clone() +{ + return d->clone(); +} + +/*! + \class QPlace + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlace class represents a set of data about a place. + + \input place-definition.qdocinc + + \section2 Contact Information + The contact information of a place is based around a common set of + \l {Contact Types}{contact types}. To retrieve all the phone numbers + of a place, one would do: + + \snippet places/requesthandler.h Phone numbers + + The contact types are string values by design to allow for providers + to introduce new contact types. + + For convenience there are a set of functions which return the value + of the first contact detail of each type. + \list + \li QPlace::primaryPhone() + \li QPlace::primaryEmail() + \li QPlace::primaryWebsite() + \li QPlace::primaryFax() + \endlist + + \section2 Extended Attributes + Places may have additional attributes which are not covered in the formal API. + Similar to contacts attributes are based around a common set of + \l {Attribute Types}{attribute types}. To retrieve an extended attribute one + would do: + \snippet places/requesthandler.h Opening hours + + The attribute types are string values by design to allow providers + to introduce new attribute types. + + \section2 Content + The QPlace object is only meant to be a convenient container to hold + rich content such as images, reviews and so on. Retrieval of content + should happen via QPlaceManager::getPlaceContent(). + + The content is stored as a QPlaceContent::Collection which contains + both the index of the content, as well as the content itself. This enables + developers to check whether a particular item has already been retrieved + and if not, then request that content. + + \section3 Attribution + Places have a field for a rich text attribution string. Some providers + may require that the attribution be shown when a place is displayed + to a user. + + \section2 Categories + Different categories may be assigned to a place to indicate that the place + is associated with those categories. When saving a place, the only meaningful + data is the category id, the rest of the category data is effectively ignored. + The category must already exist before saving the place (it is not possible + to create a new category, assign it to the place, save the place and expect + the category to be created). + + \section2 Saving Caveats + \input place-caveats.qdocinc +*/ + +/*! + Constructs an empty place object. +*/ +QPlace::QPlace() + : d_ptr(new QPlacePrivateDefault()) +{ +} + +/*! + Constructs an place object using \a dd as private implementation. +*/ +QPlace::QPlace(const QSharedDataPointer &dd): d_ptr(dd) +{ +} + +/*! + Returns the d-pointer. +*/ +QSharedDataPointer &QPlace::d() +{ + return d_ptr; +} + +/*! + Constructs a copy of \a other. +*/ +QPlace::QPlace(const QPlace &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys this place. +*/ +QPlace::~QPlace() +{ +} + +/*! + Assigns \a other to this place and returns a reference + to this place. +*/ +QPlace &QPlace::operator= (const QPlace & other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +inline QPlacePrivate *QPlace::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QPlacePrivate *QPlace::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +/*! + Returns true if \a other is equal to this place, + otherwise returns false. +*/ +bool QPlace::operator== (const QPlace &other) const +{ + return ( (d_ptr.constData() == other.d_ptr.constData()) + || (*d_ptr) == (*other.d_ptr)); +} + +/*! + Returns true if \a other is not equal to this place, + otherwise returns false. +*/ +bool QPlace::operator!= (const QPlace &other) const +{ + return !(operator==(other)); +} + +/*! + Returns categories that this place belongs to. +*/ +QList QPlace::categories() const +{ + return d_ptr->categories(); +} + +/*! + Sets a single \a category that this place belongs to. +*/ +void QPlace::setCategory(const QPlaceCategory &category) +{ + d_ptr->setCategories(QList()); + d_ptr->setCategories(QList() << category); +} + +/*! + Sets the \a categories that this place belongs to. +*/ +void QPlace::setCategories(const QList &categories) +{ + d_ptr->setCategories(categories); +} + +/*! + Returns the location of the place. +*/ +QGeoLocation QPlace::location() const +{ + return d_ptr->location(); +} + +/*! + Sets the \a location of the place. +*/ +void QPlace::setLocation(const QGeoLocation &location) +{ + d_ptr->setLocation(location); +} + +/*! + Returns an aggregated rating of the place. +*/ +QPlaceRatings QPlace::ratings() const +{ + return d_ptr->ratings(); +} + +/*! + Sets the aggregated \a rating of the place. +*/ +void QPlace::setRatings(const QPlaceRatings &rating) +{ + d_ptr->setRatings(rating); +} + +/*! + Returns the supplier of this place. +*/ +QPlaceSupplier QPlace::supplier() const +{ + return d_ptr->supplier(); +} + +/*! + Sets the supplier of this place to \a supplier. +*/ +void QPlace::setSupplier(const QPlaceSupplier &supplier) +{ + d_ptr->setSupplier(supplier); +} + +/*! + Returns a collection of content associated with a place. + This collection is a map with the key being the index of the content object + and value being the content object itself. + + The \a type specifies which kind of content is to be retrieved. +*/ +QPlaceContent::Collection QPlace::content(QPlaceContent::Type type) const +{ + return d_ptr->m_contentCollections.value(type); +} + +/*! + Sets a collection of \a content for the given \a type. +*/ +void QPlace::setContent(QPlaceContent::Type type, const QPlaceContent::Collection &content) +{ + d_ptr->m_contentCollections.insert(type, content); +} + +/*! + Adds a collection of \a content of the given \a type to the place. Any index in \a content + that already exists is overwritten. +*/ +void QPlace::insertContent(QPlaceContent::Type type, const QPlaceContent::Collection &content) +{ + QMapIterator iter(content); + while (iter.hasNext()) { + iter.next(); + d_ptr->m_contentCollections[type].insert(iter.key(), iter.value()); + } +} + +/*! + Returns the total count of content objects of the given \a type. + This total count indicates how many the manager/provider should have available. + (As opposed to how many objects this place instance is currently assigned). + + A negative count indicates that the total number of items is unknown. + By default the total content count is set to 0. +*/ +int QPlace::totalContentCount(QPlaceContent::Type type) const +{ + return d_ptr->m_contentCounts.value(type, 0); +} + +/*! + Sets the \a totalCount of content objects of the given \a type. +*/ +void QPlace::setTotalContentCount(QPlaceContent::Type type, int totalCount) +{ + d_ptr->m_contentCounts.insert(type, totalCount); +} + +/*! + Returns the name of the place. +*/ +QString QPlace::name() const +{ + return d_ptr->name(); +} + +/*! + Sets the \a name of the place. +*/ +void QPlace::setName(const QString &name) +{ + d_ptr->setName(name); +} + +/*! + Returns the identifier of the place. The place identifier is only meaningful to the QPlaceManager that + generated it and is not transferable between managers. The place identifier is not guaranteed + to be universally unique, but unique for the manager that generated it. +*/ +QString QPlace::placeId() const +{ + return d_ptr->placeId(); +} + +/*! + Sets the \a identifier of the place. +*/ +void QPlace::setPlaceId(const QString &identifier) +{ + d_ptr->setPlaceId(identifier); +} + +/*! + Returns a rich text attribution string of the place. Note, some providers may have a + requirement where the attribution must be shown whenever a place is displayed to an end user. +*/ +QString QPlace::attribution() const +{ + return d_ptr->attribution(); +} + +/*! + Sets the \a attribution string of the place. +*/ +void QPlace::setAttribution(const QString &attribution) +{ + d_ptr->setAttribution(attribution); +} + +/*! + Returns the icon of the place. +*/ +QPlaceIcon QPlace::icon() const +{ + return d_ptr->icon(); +} + +/*! + Sets the \a icon of the place. +*/ +void QPlace::setIcon(const QPlaceIcon &icon) +{ + d_ptr->setIcon(icon); +} + +/*! + Returns the primary phone number for this place. This accesses the first contact detail + of the \l {QPlaceContactDetail::Phone}{phone number type}. If no phone details exist, then an empty string is returned. +*/ +QString QPlace::primaryPhone() const +{ + QList phoneNumbers = d_ptr->contacts().value(QPlaceContactDetail::Phone); + if (!phoneNumbers.isEmpty()) + return phoneNumbers.at(0).value(); + else + return QString(); +} + +/*! + Returns the primary fax number for this place. This convenience function accesses the first contact + detail of the \l {QPlaceContactDetail::Fax}{fax type}. If no fax details exist, then an empty string is returned. +*/ +QString QPlace::primaryFax() const +{ + QList faxNumbers = d_ptr->contacts().value(QPlaceContactDetail::Fax); + if (!faxNumbers.isEmpty()) + return faxNumbers.at(0).value(); + else + return QString(); +} + +/*! + Returns the primary email address for this place. This convenience function accesses the first + contact detail of the \l {QPlaceContactDetail::Email}{email type}. If no email addresses exist, then + an empty string is returned. +*/ +QString QPlace::primaryEmail() const +{ + QList emailAddresses = d_ptr->contacts().value(QPlaceContactDetail::Email); + if (!emailAddresses.isEmpty()) + return emailAddresses.at(0).value(); + else + return QString(); +} + +/*! + Returns the primary website of the place. This convenience function accesses the first + contact detail of the \l {QPlaceContactDetail::Website}{website type}. If no websites exist, + then an empty string is returned. +*/ +QUrl QPlace::primaryWebsite() const +{ + QList websites = d_ptr->contacts().value(QPlaceContactDetail::Website); + if (!websites.isEmpty()) + return QUrl(websites.at(0).value()); + else + return QString(); +} + +/*! + Returns true if the details of this place have been fetched, + otherwise returns false. +*/ +bool QPlace::detailsFetched() const +{ + return d_ptr->detailsFetched(); +} + +/*! + Sets whether the details of this place have been \a fetched or not. +*/ +void QPlace::setDetailsFetched(bool fetched) +{ + d_ptr->setDetailsFetched(fetched); +} + +/*! + Returns the types of extended attributes that this place has. +*/ +QStringList QPlace::extendedAttributeTypes() const +{ + return d_ptr->extendedAttributes().keys(); +} + +/*! + Returns the exteded attribute corresponding to the specified \a attributeType. + If the place does not have that particular attribute type, a default constructed + QPlaceExtendedAttribute is returned. +*/ +QPlaceAttribute QPlace::extendedAttribute(const QString &attributeType) const +{ + return d_ptr->extendedAttribute(attributeType); +} + +/*! + Assigns an \a attribute of the given \a attributeType to a place. If the given \a attributeType + already exists in the place, then it is overwritten. + + If \a attribute is a default constructed QPlaceAttribute, then the \a attributeType + is removed from the place which means it will not be listed by QPlace::extendedAttributeTypes(). +*/ +void QPlace::setExtendedAttribute(const QString &attributeType, + const QPlaceAttribute &attribute) +{ + if (attribute == QPlaceAttribute()) + d_ptr->extendedAttributes().remove(attributeType); + else + d_ptr->extendedAttributes().insert(attributeType, attribute); +} + +/*! + Remove the attribute of \a attributeType from the place. + + The attribute will no longer be listed by QPlace::extendedAttributeTypes() +*/ +void QPlace::removeExtendedAttribute(const QString &attributeType) +{ + setExtendedAttribute(attributeType, QPlaceAttribute()); +} + +/*! + Returns the type of contact details this place has. + + See QPlaceContactDetail for a list of common \l {QPlaceContactDetail::Email}{contact types}. +*/ +QStringList QPlace::contactTypes() const +{ + return d_ptr->contacts().keys(); +} + +/*! + Returns a list of contact details of the specified \a contactType. + + See QPlaceContactDetail for a list of common \l {QPlaceContactDetail::Email}{contact types}. +*/ +QList QPlace::contactDetails(const QString &contactType) const +{ + return d_ptr->contacts().value(contactType); +} + +/*! + Sets the contact \a details of a specified \a contactType. + + If \a details is empty, then the \a contactType is removed from the place such + that it is no longer returned by QPlace::contactTypes(). + + See QPlaceContactDetail for a list of common \l {QPlaceContactDetail::Email}{contact types}. +*/ +void QPlace::setContactDetails(const QString &contactType, QList details) +{ + if (details.isEmpty()) + d_ptr->contacts().remove(contactType); + else + d_ptr->contacts().insert(contactType, details); +} + +/*! + Appends a contact \a detail of a specified \a contactType. + + See QPlaceContactDetail for a list of common \l {QPlaceContactDetail::Email}{contact types}. +*/ +void QPlace::appendContactDetail(const QString &contactType, const QPlaceContactDetail &detail) +{ + QList details = d_ptr->contacts().value(contactType); + details.append(detail); + d_ptr->contacts().insert(contactType, details); +} + +/*! + Removes all the contact details of a given \a contactType. + + The \a contactType is no longer returned when QPlace::contactTypes() is called. +*/ +void QPlace::removeContactDetails(const QString &contactType) +{ + d_ptr->contacts().remove(contactType); +} + +/*! + Sets the visibility of the place to \a visibility. +*/ +void QPlace::setVisibility(QLocation::Visibility visibility) +{ + d_ptr->setVisibility(visibility); +} + +/*! + Returns the visibility of the place. + + The default visibility of a new place is set to QtLocatin::Unspecified visibility. + If a place is saved with unspecified visibility the backend chooses an appropriate + default visibility to use when saving. +*/ +QLocation::Visibility QPlace::visibility() const +{ + return d_ptr->visibility(); +} + +/*! + Returns a boolean indicating whether the all the fields of the place are empty or not. +*/ +bool QPlace::isEmpty() const +{ + return d_ptr->isEmpty(); +} + +/******************************************************************************* +*******************************************************************************/ + +QPlacePrivate::QPlacePrivate() +: QSharedData() +{ +} + +QPlacePrivate::QPlacePrivate(const QPlacePrivate &other) + : QSharedData(other), + m_contentCollections(other.m_contentCollections), + m_contentCounts(other.m_contentCounts) +{ +} + +QPlacePrivate::~QPlacePrivate() {} + +bool QPlacePrivate::operator== (const QPlacePrivate &other) const +{ + return (categories() == other.categories() + && location() == other.location() + && ratings() == other.ratings() + && supplier() == other.supplier() + && m_contentCollections == other.m_contentCollections + && m_contentCounts == other.m_contentCounts + && name() == other.name() + && placeId() == other.placeId() + && attribution() == other.attribution() + && contacts() == other.contacts() + && extendedAttributes() == other.extendedAttributes() + && visibility() == other.visibility() + && icon() == other.icon() + ); +} + + +bool QPlacePrivate::isEmpty() const +{ + return (categories().isEmpty() + && location().isEmpty() + && ratings().isEmpty() + && supplier().isEmpty() + && m_contentCollections.isEmpty() + && m_contentCounts.isEmpty() + && name().isEmpty() + && placeId().isEmpty() + && attribution().isEmpty() + && contacts().isEmpty() + && extendedAttributes().isEmpty() + && QLocation::UnspecifiedVisibility == visibility() + && icon().isEmpty() + ); +} + +QPlaceAttribute QPlacePrivate::extendedAttribute(const QString &attributeType) const +{ + return extendedAttributes().value(attributeType); +} + + + +// +// Default implementation +// + +QPlacePrivateDefault::QPlacePrivateDefault() + : QPlacePrivate(), m_visibility(QLocation::UnspecifiedVisibility), m_detailsFetched(false) +{ +} + +QPlacePrivateDefault::QPlacePrivateDefault(const QPlacePrivateDefault &other) + : QPlacePrivate(other), + m_categories(other.m_categories), + m_location(other.m_location), + m_ratings(other.m_ratings), + m_supplier(other.m_supplier), + m_name(other.m_name), + m_placeId(other.m_placeId), + m_attribution(other.m_attribution), + m_extendedAttributes(other.m_extendedAttributes), + m_contacts(other.m_contacts), + m_visibility(other.m_visibility), + m_icon(other.m_icon), + m_detailsFetched(other.m_detailsFetched) +{ +} + +QPlacePrivateDefault::~QPlacePrivateDefault() +{ +} + +QPlacePrivate *QPlacePrivateDefault::clone() +{ + return new QPlacePrivateDefault(*this); +} + +QList QPlacePrivateDefault::categories() const +{ + return m_categories; +} + +void QPlacePrivateDefault::setCategories(const QList &categories) +{ + m_categories = categories; +} + +QGeoLocation QPlacePrivateDefault::location() const +{ + return m_location; +} + +void QPlacePrivateDefault::setLocation(const QGeoLocation &location) +{ + m_location = location; +} + +QPlaceRatings QPlacePrivateDefault::ratings() const +{ + return m_ratings; +} + +void QPlacePrivateDefault::setRatings(const QPlaceRatings &ratings) +{ + m_ratings = ratings; +} + +QPlaceSupplier QPlacePrivateDefault::supplier() const +{ + return m_supplier; +} + +void QPlacePrivateDefault::setSupplier(const QPlaceSupplier &supplier) +{ + m_supplier = supplier; +} + +QString QPlacePrivateDefault::name() const +{ + return m_name; +} + +void QPlacePrivateDefault::setName(const QString &name) +{ + m_name = name; +} + +QString QPlacePrivateDefault::placeId() const +{ + return m_placeId; +} + +void QPlacePrivateDefault::setPlaceId(const QString &placeIdentifier) +{ + m_placeId = placeIdentifier; +} + +QString QPlacePrivateDefault::attribution() const +{ + return m_attribution; +} + +void QPlacePrivateDefault::setAttribution(const QString &attribution) +{ + m_attribution = attribution; +} + +QLocation::Visibility QPlacePrivateDefault::visibility() const +{ + return m_visibility; +} + +void QPlacePrivateDefault::setVisibility(QLocation::Visibility visibility) +{ + m_visibility = visibility; +} + +QPlaceIcon QPlacePrivateDefault::icon() const +{ + return m_icon; +} + +void QPlacePrivateDefault::setIcon(const QPlaceIcon &icon) +{ + m_icon = icon; +} + +bool QPlacePrivateDefault::detailsFetched() const +{ + return m_detailsFetched; +} + +void QPlacePrivateDefault::setDetailsFetched(bool fetched) +{ + m_detailsFetched = fetched; +} + +QMap QPlacePrivateDefault::extendedAttributes() const +{ + return m_extendedAttributes; +} + +QMap &QPlacePrivateDefault::extendedAttributes() +{ + return m_extendedAttributes; +} + +QMap > QPlacePrivateDefault::contacts() const +{ + return m_contacts; +} + +QMap > &QPlacePrivateDefault::contacts() +{ + return m_contacts; +} + + + +QT_END_NAMESPACE diff --git a/src/location/places/qplace.h b/src/location/places/qplace.h new file mode 100644 index 0000000..5eac1c8 --- /dev/null +++ b/src/location/places/qplace.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACE_H +#define QPLACE_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QString; +class QPlaceIcon; +class QPlacePrivate; + +class Q_LOCATION_EXPORT QPlace +{ +public: + QPlace(); + QPlace(const QPlace &other); + ~QPlace(); + + QPlace &operator=(const QPlace &other); + + bool operator==(const QPlace &other) const; + bool operator!=(const QPlace &other) const; + + QList categories() const; + void setCategory(const QPlaceCategory &category); + void setCategories(const QList &categories); + QGeoLocation location() const; + void setLocation(const QGeoLocation &location); + QPlaceRatings ratings() const; + void setRatings(const QPlaceRatings &ratings); + QPlaceSupplier supplier() const; + void setSupplier(const QPlaceSupplier &supplier); + + QString attribution() const; + void setAttribution(const QString &attribution); + + QPlaceIcon icon() const; + void setIcon(const QPlaceIcon &icon); + + QPlaceContent::Collection content(QPlaceContent::Type type) const; + void setContent(QPlaceContent::Type type, const QPlaceContent::Collection &content); + void insertContent(QPlaceContent::Type type, const QPlaceContent::Collection &content); + + int totalContentCount(QPlaceContent::Type type) const; + void setTotalContentCount(QPlaceContent::Type type, int total); + + QString name() const; + void setName(const QString &name); + QString placeId() const; + void setPlaceId(const QString &identifier); + + QString primaryPhone() const; + QString primaryFax() const; + QString primaryEmail() const; + QUrl primaryWebsite() const; + + bool detailsFetched() const; + void setDetailsFetched(bool fetched); + + QStringList extendedAttributeTypes() const; + QPlaceAttribute extendedAttribute(const QString &attributeType) const; + void setExtendedAttribute(const QString &attributeType, const QPlaceAttribute &attribute); + void removeExtendedAttribute(const QString &attributeType); + + QStringList contactTypes() const; + QList contactDetails(const QString &contactType) const; + void setContactDetails(const QString &contactType, QList details); + void appendContactDetail(const QString &contactType, const QPlaceContactDetail &detail); + void removeContactDetails(const QString &contactType); + + QLocation::Visibility visibility() const; + void setVisibility(QLocation::Visibility visibility); + + bool isEmpty() const; + +protected: + QPlace(const QSharedDataPointer &dd); + QSharedDataPointer &d(); + +private: + QSharedDataPointer d_ptr; + + inline QPlacePrivate *d_func(); + inline const QPlacePrivate *d_func() const; + friend class QDeclarativePlace; +}; + +Q_DECLARE_TYPEINFO(QPlace, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlace) + +#endif diff --git a/src/location/places/qplace_p.h b/src/location/places/qplace_p.h new file mode 100644 index 0000000..5b6f167 --- /dev/null +++ b/src/location/places/qplace_p.h @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACE_P_H +#define QPLACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_LOCATION_PRIVATE_EXPORT QPlacePrivate : public QSharedData +{ +public: + QPlacePrivate(); + QPlacePrivate(const QPlacePrivate &other); + virtual ~QPlacePrivate(); + virtual QPlacePrivate *clone() = 0; + + bool operator==(const QPlacePrivate &other) const; + + virtual bool isEmpty() const; + virtual QList categories() const = 0; + virtual void setCategories(const QList &categories) = 0; + virtual QGeoLocation location() const = 0; + virtual void setLocation(const QGeoLocation &location) = 0; + virtual QPlaceRatings ratings() const = 0; + virtual void setRatings(const QPlaceRatings &ratings) = 0; + virtual QPlaceSupplier supplier() const = 0; + virtual void setSupplier(const QPlaceSupplier &supplier) = 0; + virtual QString name() const = 0; + virtual void setName(const QString &name) = 0; + virtual QString placeId() const = 0; + virtual void setPlaceId(const QString &placeIdentifier) = 0; + virtual QString attribution() const = 0; + virtual void setAttribution(const QString &attribution) = 0; + virtual QLocation::Visibility visibility() const = 0; + virtual void setVisibility(QLocation::Visibility visibility) = 0; + virtual QPlaceIcon icon() const = 0; + virtual void setIcon(const QPlaceIcon &icon) = 0; + virtual bool detailsFetched() const = 0; + virtual void setDetailsFetched(bool fetched) = 0; + + virtual QMap extendedAttributes() const = 0; + virtual QMap &extendedAttributes() = 0; + virtual QMap > contacts() const = 0; + virtual QMap > &contacts() = 0; + virtual QPlaceAttribute extendedAttribute(const QString &attributeType) const; + + + // The place content, that has to be manually retrieved from the place manager and manually added to the place. + // Currently, place content types can be: + // ImageType, + // ReviewType, + // EditorialType, + // CustomType = 0x0100 + QMap m_contentCollections; + QMap m_contentCounts; +}; + + +class Q_LOCATION_PRIVATE_EXPORT QPlacePrivateDefault : public QPlacePrivate +{ +public: + QPlacePrivateDefault(); + QPlacePrivateDefault(const QPlacePrivateDefault &other); + virtual ~QPlacePrivateDefault(); + virtual QPlacePrivate *clone() override; + + virtual QList categories() const override; + virtual void setCategories(const QList &categories) override; + virtual QGeoLocation location() const override; + virtual void setLocation(const QGeoLocation &location) override; + virtual QPlaceRatings ratings() const override; + virtual void setRatings(const QPlaceRatings &ratings) override; + virtual QPlaceSupplier supplier() const override; + virtual void setSupplier(const QPlaceSupplier &supplier) override; + virtual QString name() const override; + virtual void setName(const QString &name) override; + virtual QString placeId() const override; + virtual void setPlaceId(const QString &placeIdentifier) override; + virtual QString attribution() const override; + virtual void setAttribution(const QString &attribution) override; + virtual QLocation::Visibility visibility() const override; + virtual void setVisibility(QLocation::Visibility visibility) override; + virtual QPlaceIcon icon() const override; + virtual void setIcon(const QPlaceIcon &icon) override; + virtual bool detailsFetched() const override; + virtual void setDetailsFetched(bool fetched) override; + + virtual QMap extendedAttributes() const override; + virtual QMap &extendedAttributes() override; + virtual QMap > contacts() const override; + virtual QMap > &contacts() override; + + + // data members + + QList m_categories; + QGeoLocation m_location; + QPlaceRatings m_ratings; + QPlaceSupplier m_supplier; + QString m_name; + QString m_placeId; + QString m_attribution; + + QMap m_extendedAttributes; + QMap > m_contacts; + + QLocation::Visibility m_visibility; + QPlaceIcon m_icon; + bool m_detailsFetched; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/location/places/qplaceattribute.cpp b/src/location/places/qplaceattribute.cpp new file mode 100644 index 0000000..e7812a7 --- /dev/null +++ b/src/location/places/qplaceattribute.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceattribute_p.h" +#include "qplaceattribute.h" + +QT_USE_NAMESPACE + +template<> QPlaceAttributePrivate *QSharedDataPointer::clone() +{ + return d->clone(); +} + +QPlaceAttributePrivate::QPlaceAttributePrivate(const QPlaceAttributePrivate &other) + : QSharedData(other), + label(other.label), + text(other.text) +{ +} + +bool QPlaceAttributePrivate::operator== (const QPlaceAttributePrivate &other) const +{ + return label == other.label + && text == other.text; +} + +bool QPlaceAttributePrivate::isEmpty() const +{ + return label.isEmpty() + && text.isEmpty(); +} + + +/*! + \class QPlaceAttribute + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceAttribute class represents generic attribute information about a place. + + A QPlaceAttribute instance stores an additional piece of information about a place that is not + otherwise exposed through the QPlace class. A QPlaceAttribute encapsulates a + localized label which describes the attribute and rich text string representing the attribute's value. + Generally, both are intended to be displayed to the end-user as is. + + Some plugins may not support attributes at all, others may only support a + certain set, others still may support a dynamically changing set of attributes + over time or even allow attributes to be arbitrarily defined by the client + application. The attributes could also vary on a place by place basis, + for example one place may have opening hours while another does not. + Consult the \l {Plugin References and Parameters}{plugin + references} for details. + + \section2 Attribute Types + The QPlaceAttribute class defines some constant strings which characterize standard \e {attribute types}. + \list + \li QPlaceAttribute::OpeningHours + \li QPlaceAttribute::Payment + \li QPlaceAttribute::Provider + \endlist + + There is a class of attribute types of the format x_id_ for example x_id_here. + This class of attributes is a set of alternative identifiers of the place, from the specified provider's + perspective. + + The above types are used to access and modify attributes in QPlace via: + \list + \li QPlace::extendedAttribute() + \li QPlace::setExtendedAttribute() + \li QPlace::removeExtendedAttribute() + \li QPlace::removeExtendedAttribute() + \endlist + + The \e {attribute type} is a string type so that providers are able to introduce + new attributes as necessary. Custom attribute types should always be prefixed + by a qualifier in order to avoid conflicts. + + \section3 User Readable and Non-User Readable Attributes + Some attributes may not be intended to be readable by end users, the label field + of such attributes are empty to indicate this fact. +*/ + +/*! + \variable QPlaceAttribute::OpeningHours + Specifies the opening hours. +*/ +const QString QPlaceAttribute::OpeningHours(QLatin1String("openingHours")); + +/*! + \variable QPlaceAttribute::Payment + The constant to specify an attribute that defines the methods of payment. +*/ +const QString QPlaceAttribute::Payment(QLatin1String("payment")); + +/*! + \variable QPlaceAttribute::Provider + The constant to specify an attribute that defines which + provider the place came from. +*/ +const QString QPlaceAttribute::Provider(QLatin1String("x_provider")); + +/*! + Constructs an attribute. +*/ +QPlaceAttribute::QPlaceAttribute() + : d_ptr(new QPlaceAttributePrivate) +{ +} + +/*! + Destroys the attribute. +*/ +QPlaceAttribute::~QPlaceAttribute() +{ +} + +/*! + Creates a copy of \a other. +*/ +QPlaceAttribute::QPlaceAttribute(const QPlaceAttribute &other) + :d_ptr(other.d_ptr) +{ +} + +/*! + Assigns \a other to this attribute and returns a reference to this + attribute. +*/ +QPlaceAttribute &QPlaceAttribute::operator=(const QPlaceAttribute &other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if \a other is equal to this attribute, otherwise + returns false. +*/ +bool QPlaceAttribute::operator== (const QPlaceAttribute &other) const +{ + if (d_ptr == other.d_ptr) + return true; + return ( *(d_ptr.constData()) == *(other.d_ptr.constData())); +} + +/*! + Returns true if \a other is not equal to this attribute, + otherwise returns false. +*/ +bool QPlaceAttribute::operator!= (const QPlaceAttribute &other) const +{ + return (!this->operator ==(other)); +} + +/*! + Returns a localized label describing the attribute. +*/ +QString QPlaceAttribute::label() const +{ + return d_ptr->label; +} + +/*! + Sets the \a label of the attribute. +*/ +void QPlaceAttribute::setLabel(const QString &label) +{ + d_ptr->label = label; +} + +/*! + Returns a piece of rich text representing the attribute value. +*/ +QString QPlaceAttribute::text() const +{ + return d_ptr->text; +} + +/*! + Sets the \a text of the attribute. +*/ +void QPlaceAttribute::setText(const QString &text) +{ + d_ptr->text = text; +} + +/*! + Returns a boolean indicating whether the all the fields of the place attribute are empty or not. +*/ +bool QPlaceAttribute::isEmpty() const +{ + return d_ptr->isEmpty(); +} diff --git a/src/location/places/qplaceattribute.h b/src/location/places/qplaceattribute.h new file mode 100644 index 0000000..91a171d --- /dev/null +++ b/src/location/places/qplaceattribute.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEATTRIBUTE_H +#define QPLACEATTRIBUTE_H + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceAttributePrivate; +class Q_LOCATION_EXPORT QPlaceAttribute +{ +public: + static const QString OpeningHours; + static const QString Payment; + static const QString Provider; + + QPlaceAttribute(); + QPlaceAttribute(const QPlaceAttribute &other); + virtual ~QPlaceAttribute(); + + QPlaceAttribute &operator=(const QPlaceAttribute &other); + + bool operator==(const QPlaceAttribute &other) const; + bool operator!=(const QPlaceAttribute &other) const; + + QString label() const; + void setLabel(const QString &label); + + QString text() const; + void setText(const QString &text); + + bool isEmpty() const; + +protected: + QSharedDataPointer d_ptr; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceAttribute) + +#endif diff --git a/src/location/places/qplaceattribute_p.h b/src/location/places/qplaceattribute_p.h new file mode 100644 index 0000000..1f7442e --- /dev/null +++ b/src/location/places/qplaceattribute_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEATTRIBUTE_P_H +#define QPLACEATTRIBUTE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceAttributePrivate : public QSharedData +{ +public: + QPlaceAttributePrivate(){} + QPlaceAttributePrivate(const QPlaceAttributePrivate &other); + virtual ~QPlaceAttributePrivate(){} + + + virtual bool operator== (const QPlaceAttributePrivate &other) const; + virtual QPlaceAttributePrivate *clone() const { return new QPlaceAttributePrivate(*this); } + + bool isEmpty() const; + + QString label; + QString text; +}; + +template<> QPlaceAttributePrivate *QSharedDataPointer::clone(); + +QT_END_NAMESPACE + +#endif + diff --git a/src/location/places/qplacecategory.cpp b/src/location/places/qplacecategory.cpp new file mode 100644 index 0000000..64a7516 --- /dev/null +++ b/src/location/places/qplacecategory.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecategory.h" +#include "qplacecategory_p.h" + +QT_BEGIN_NAMESPACE + +QPlaceCategoryPrivate::QPlaceCategoryPrivate() +: visibility(QLocation::UnspecifiedVisibility) +{ +} + +QPlaceCategoryPrivate::QPlaceCategoryPrivate(const QPlaceCategoryPrivate &other) +: QSharedData(other), categoryId(other.categoryId), name(other.name), visibility(other.visibility), + icon(other.icon) +{ +} + +QPlaceCategoryPrivate::~QPlaceCategoryPrivate() +{ +} + +QPlaceCategoryPrivate &QPlaceCategoryPrivate::operator=(const QPlaceCategoryPrivate &other) +{ + if (this == &other) + return *this; + + categoryId = other.categoryId; + name = other.name; + icon = other.icon; + return *this; +} + +bool QPlaceCategoryPrivate::isEmpty() const +{ + return categoryId.isEmpty() + && name.isEmpty() + && icon.isEmpty() + && QLocation::UnspecifiedVisibility == visibility; +} + +/*! + \class QPlaceCategory + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceCategory class represents a category that a \l QPlace can be associated with. + + Categories are used to search for places based on the categories they are associated with. The + list/tree of available categories can be obtained from \l QPlaceManager. The + \l QPlaceSearchRequest::setCategories() function can be used to limit the search results to + places with the specified categories. + + If the \l QGeoServiceProvider supports it, categories can be created and removed. This + functionality is available in the \l QPlaceManager class. +*/ + +/*! + \fn bool QPlaceCategory::operator!=(const QPlaceCategory &other) const + + Returns true if \a other is not equal to this category; otherwise returns false. +*/ + +/*! + Constructs a category. +*/ +QPlaceCategory::QPlaceCategory() + : d(new QPlaceCategoryPrivate) +{ +} + +/*! + Constructs a category which is a copy of \a other. +*/ +QPlaceCategory::QPlaceCategory(const QPlaceCategory &other) + :d(other.d) +{ +} + +/*! + Destroys the category. +*/ +QPlaceCategory::~QPlaceCategory() +{ +} + +/*! + Assigns \a other to this category and returns a reference to this category. +*/ +QPlaceCategory &QPlaceCategory::operator =(const QPlaceCategory &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +/*! + Returns true if \a other is equal to this category; otherwise returns false. +*/ +bool QPlaceCategory::operator==(const QPlaceCategory &other) const +{ + return d->categoryId == other.d->categoryId && + d->name == other.d->name && + (d->visibility == QLocation::UnspecifiedVisibility || + other.d->visibility == QLocation::UnspecifiedVisibility || + d->visibility == other.d->visibility) && + d->icon == other.d->icon; +} + +/*! + Returns the identifier of the category. The category identifier is a string which uniquely identifies this category + within a particular \l QPlaceManager. The identifier is only meaningful to the QPlaceManager + that generated it and is not transferable between managers. +*/ +QString QPlaceCategory::categoryId() const +{ + return d->categoryId; +} + +/*! + Sets the \a identifier of the category. +*/ +void QPlaceCategory::setCategoryId(const QString &identifier) +{ + d->categoryId = identifier; +} + +/*! + Returns the name of category. +*/ +QString QPlaceCategory::name() const +{ + return d->name; +} + +/*! + Sets the \a name of the category. +*/ +void QPlaceCategory::setName(const QString &name) +{ + d->name = name; +} + +/*! + Sets the \a visibility of the category. +*/ +void QPlaceCategory::setVisibility(QLocation::Visibility visibility) +{ + d->visibility = visibility; +} + +/*! + Returns the visibility of the category. +*/ +QLocation::Visibility QPlaceCategory::visibility() const +{ + return d->visibility; +} + +/*! + Returns the icon associated with the category. +*/ +QPlaceIcon QPlaceCategory::icon() const +{ + return d->icon; +} + +/*! + Sets the \a icon of the category. +*/ +void QPlaceCategory::setIcon(const QPlaceIcon &icon) +{ + d->icon = icon; +} + +/*! + Returns a boolean indicating whether the all the fields of the place category are empty or not. +*/ +bool QPlaceCategory::isEmpty() const +{ + return d->isEmpty(); +} + +QT_END_NAMESPACE diff --git a/src/location/places/qplacecategory.h b/src/location/places/qplacecategory.h new file mode 100644 index 0000000..56a9854 --- /dev/null +++ b/src/location/places/qplacecategory.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECATEGORY_H +#define QPLACECATEGORY_H + +#include + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceIcon; + +class QPlaceCategoryPrivate; +class Q_LOCATION_EXPORT QPlaceCategory +{ +public: + QPlaceCategory(); + QPlaceCategory(const QPlaceCategory &other); + + virtual ~QPlaceCategory(); + + QPlaceCategory &operator=(const QPlaceCategory &other); + + bool operator==(const QPlaceCategory &other) const; + bool operator!=(const QPlaceCategory &other) const { + return !(other == *this); + } + + QString categoryId() const; + void setCategoryId(const QString &identifier); + + QString name() const; + void setName(const QString &name); + + QLocation::Visibility visibility() const; + void setVisibility(QLocation::Visibility visibility); + + QPlaceIcon icon() const; + void setIcon(const QPlaceIcon &icon); + + bool isEmpty() const; + +private: + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(QPlaceCategory, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceCategory) + +#endif // QPLACECATEGORY_H diff --git a/src/location/places/qplacecategory_p.h b/src/location/places/qplacecategory_p.h new file mode 100644 index 0000000..07fec3c --- /dev/null +++ b/src/location/places/qplacecategory_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECATEGORY_P_H +#define QPLACECATEGORY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include "qplaceicon.h" + +QT_BEGIN_NAMESPACE + +class QPlaceCategoryPrivate : public QSharedData +{ +public: + QPlaceCategoryPrivate(); + QPlaceCategoryPrivate(const QPlaceCategoryPrivate &other); + + ~QPlaceCategoryPrivate(); + QPlaceCategoryPrivate &operator= (const QPlaceCategoryPrivate &other); + bool operator==(const QPlaceCategoryPrivate &other) const; + bool isEmpty() const; + + QString categoryId; + QString name; + QLocation::Visibility visibility; + QPlaceIcon icon; +}; + +QT_END_NAMESPACE + +#endif // QPLACECATEGORY_P_H diff --git a/src/location/places/qplacecontactdetail.cpp b/src/location/places/qplacecontactdetail.cpp new file mode 100644 index 0000000..d19fafb --- /dev/null +++ b/src/location/places/qplacecontactdetail.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecontactdetail_p.h" +#include "qplacecontactdetail.h" + +QT_USE_NAMESPACE + +QPlaceContactDetailPrivate::QPlaceContactDetailPrivate(const QPlaceContactDetailPrivate &other) + : QSharedData(other), + label(other.label), + value(other.value) +{ +} + +bool QPlaceContactDetailPrivate::operator== (const QPlaceContactDetailPrivate &other) const +{ + return label == other.label + && value == other.value; +} + +/*! +\class QPlaceContactDetail +\brief The QPlaceContactDetail class represents a contact detail such as a phone number or website url. +\inmodule QtLocation + +\ingroup QtLocation-places +\ingroup QtLocation-places-data + +The detail consists of a label and value. The label is a localized string that can be presented +to the end user that describes that detail value which is the actual phone number, email address and so on. + +\section2 Contact Types + +The QPlaceContactDetail class defines some constant strings which characterize standard \e {contact types}. +\list + \li QPlaceContactDetail::Phone + \li QPlaceContactDetail::Email + \li QPlaceContactDetail::Website + \li QPlaceContactDetail::Fax +\endlist + +These types are used to access and modify contact details in QPlace via: +\list + \li QPlace::contactDetails() + \li QPlace::setContactDetails() + \li QPlace::appendContactDetail() + \li QPlace::contactTypes() +\endlist + +The \e {contact type} is intended to be a string type so that providers are able to introduce new contact +types if necessary. +*/ + +/*! + \variable QPlaceContactDetail::Phone + The constant to specify phone contact details +*/ +const QString QPlaceContactDetail::Phone(QLatin1String("phone")); + +/*! + \variable QPlaceContactDetail::Email + The constant to specify email contact details. +*/ +const QString QPlaceContactDetail::Email(QLatin1String("email")); + +/*! + \variable QPlaceContactDetail::Website + The constant used to specify website contact details. +*/ +const QString QPlaceContactDetail::Website(QLatin1String("website")); + +/*! + \variable QPlaceContactDetail::Fax + The constant used to specify fax contact details. +*/ +const QString QPlaceContactDetail::Fax(QLatin1String("fax")); + +/*! + Constructs a contact detail. +*/ +QPlaceContactDetail::QPlaceContactDetail() + : d_ptr(new QPlaceContactDetailPrivate) +{ +} + +/*! + Destroys the contact detail. +*/ +QPlaceContactDetail::~QPlaceContactDetail() +{ +} + +/*! + Creates a copy of \a other. +*/ +QPlaceContactDetail::QPlaceContactDetail(const QPlaceContactDetail &other) + :d_ptr(other.d_ptr) +{ +} + +/*! + Assigns \a other to this contact detail and returns a reference to this + contact detail. +*/ +QPlaceContactDetail &QPlaceContactDetail::operator=(const QPlaceContactDetail &other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if \a other is equal to this contact detail, otherwise + returns false. +*/ +bool QPlaceContactDetail::operator== (const QPlaceContactDetail &other) const +{ + if (d_ptr == other.d_ptr) + return true; + return ( *(d_ptr.constData()) == *(other.d_ptr.constData())); +} + +/*! + Returns true if \a other is not equal to this contact detail, + otherwise returns false. +*/ +bool QPlaceContactDetail::operator!= (const QPlaceContactDetail &other) const +{ + return (!this->operator ==(other)); +} + +/*! + Returns a label describing the contact detail. + + The label can potentially be localized. The language is dependent on the entity that sets it, + typically this is the manager from which the places are sourced. + The QPlaceManager::locales() field defines what language is used. +*/ +QString QPlaceContactDetail::label() const +{ + return d_ptr->label; +} + +/*! + Sets the \a label of the contact detail. +*/ +void QPlaceContactDetail::setLabel(const QString &label) +{ + d_ptr->label = label; +} + +/*! + Returns the value of the contact detail. +*/ +QString QPlaceContactDetail::value() const +{ + return d_ptr->value; +} + +/*! + Sets the \a value of this contact detail. +*/ +void QPlaceContactDetail::setValue(const QString &value) +{ + d_ptr->value = value; +} + +/*! + Clears the contact detail. +*/ +void QPlaceContactDetail::clear() +{ + d_ptr->label.clear(); + d_ptr->value.clear(); +} diff --git a/src/location/places/qplacecontactdetail.h b/src/location/places/qplacecontactdetail.h new file mode 100644 index 0000000..5113d83 --- /dev/null +++ b/src/location/places/qplacecontactdetail.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECONTACTDETAIL_H +#define QPLACECONTACTDETAIL_H + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceContactDetailPrivate; +class Q_LOCATION_EXPORT QPlaceContactDetail +{ +public: + static const QString Phone; + static const QString Email; + static const QString Website; + static const QString Fax; + + QPlaceContactDetail(); + QPlaceContactDetail(const QPlaceContactDetail &other); + virtual ~QPlaceContactDetail(); + + QPlaceContactDetail &operator=(const QPlaceContactDetail &other); + + bool operator==(const QPlaceContactDetail &other) const; + bool operator!=(const QPlaceContactDetail &other) const; + + QString label() const; + void setLabel(const QString &label); + + QString value() const; + void setValue(const QString &value); + + void clear(); + +private: + QSharedDataPointer d_ptr; + +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceContactDetail) + +#endif diff --git a/src/location/places/qplacecontactdetail_p.h b/src/location/places/qplacecontactdetail_p.h new file mode 100644 index 0000000..5d4002e --- /dev/null +++ b/src/location/places/qplacecontactdetail_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECONTACTDETAIL_P_H +#define QPLACECONTACTDETAIL_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceContactDetailPrivate : public QSharedData +{ +public: + QPlaceContactDetailPrivate(){} + QPlaceContactDetailPrivate(const QPlaceContactDetailPrivate &other); + virtual ~QPlaceContactDetailPrivate(){} + + bool operator== (const QPlaceContactDetailPrivate &other) const; + + QString label; + QString value; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacecontent.cpp b/src/location/places/qplacecontent.cpp new file mode 100644 index 0000000..97aaa4e --- /dev/null +++ b/src/location/places/qplacecontent.cpp @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecontent.h" +#include "qplacecontent_p.h" + +#include + +QT_USE_NAMESPACE + +template<> QPlaceContentPrivate *QSharedDataPointer::clone() +{ + return d->clone(); +} + +inline QPlaceContentPrivate *QPlaceContent::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QPlaceContentPrivate *QPlaceContent::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +bool QPlaceContentPrivate::compare(const QPlaceContentPrivate *other) const +{ + return supplier == other->supplier + && user == other->user + && attribution == other->attribution; +} + +/*! + \class QPlaceContent + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceContent class serves as the base class for rich content types. + + Rich content such as \l {QPlaceImage}{images}, \l {QPlaceReview}{reviews} + and \l {QPlaceEditorial}{editorials} inherit + from the QPlaceContent class which contains common properties such as + an attribution string and content contributor, which may take the form of a + \l {QPlaceUser}{user} and/or \l {QPlaceSupplier}{supplier}. It is possible that + a user from a supplier is contributing content, hence both fields could + be filled in simultaneously. + + \b {Note:} Some providers may \e {require} that the attribution string be displayed + to the user whenever a piece of content is viewed. + + Conversion between QPlaceContent and it's subclasses can be easily performed without + casting. Due to the way it has been implemented, object slicing is not an issue, + the following code is valid: + \snippet places/requesthandler.h Content conversion + + The rich content of a place is typically made available as paginated items. The ability + to convert between QPlaceContent and it's subclasses means that code which handles + the mechanics of paging can be easily shared for each of the sub types. + + At present the QPlaceContent class is not extensible by 3rd parties. + + Note: The Places API considers content objects to be 'retrieve-only' objects. + Submission of content to a provider is not a supported use case. + \sa QPlaceImage, QPlaceReview, QPlaceEditorial +*/ + +/*! + \typedef QPlaceContent::Collection + Synonym for QMap. The key of the map is an \c int representing the + index of the content. The value is the content object itself. + + The \c {Collection} is intended to be a container where content items, that have been retrieved + as pages, can be stored. This enables a developer to skip pages, for example indexes 0-9 may be + stored in the collection, if the user skips to indexes 80-99, these can be stored in the + collection as well. +*/ + +/*! + \enum QPlaceContent::Type + Defines the type of content. + \value NoType + The content object is default constructed, any other content type may be assigned + to this content object + \value ImageType + The content object is an image + \value ReviewType + The content object is a review + \value EditorialType + The content object is an editorial + \value CustomType + The content object is of a custom type +*/ + +/*! + Constructs an default content object which has no type. +*/ +QPlaceContent::QPlaceContent() + :d_ptr(0) +{ +} + +/*! + Constructs a new copy of \a other. +*/ +QPlaceContent::QPlaceContent(const QPlaceContent &other) + :d_ptr(other.d_ptr) +{ +} + +/*! + Assigns the \a other content object to this and returns a reference + to this content object. +*/ +QPlaceContent &QPlaceContent::operator=(const QPlaceContent &other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Destroys the content object. +*/ +QPlaceContent::~QPlaceContent() +{ +} + +/*! + Returns the content type. +*/ +QPlaceContent::Type QPlaceContent::type() const +{ + if (!d_ptr) + return NoType; + return d_ptr->type(); +} + +/*! + Returns true if this content object is equivalent to \a other, + otherwise returns false. +*/ +bool QPlaceContent::operator==(const QPlaceContent &other) const +{ + // An invalid content object is only equal to another invalid content object + if (!d_ptr) + return !other.d_ptr; + + if (type() != other.type()) + return false; + + return d_ptr->compare(other.d_ptr); +} + +/*! + Returns true if this content object is not equivalent to \a other, + otherwise returns false. +*/ +bool QPlaceContent::operator!=(const QPlaceContent &other) const +{ + return !(*this == other); +} + +/*! + Returns the supplier who contributed this content. +*/ +QPlaceSupplier QPlaceContent::supplier() const +{ + Q_D(const QPlaceContent); + + return d->supplier; +} + +/*! + Sets the \a supplier of the content. +*/ +void QPlaceContent::setSupplier(const QPlaceSupplier &supplier) +{ + Q_D(QPlaceContent); + + d->supplier = supplier; +} + +/*! + Returns the user who contributed this content. +*/ +QPlaceUser QPlaceContent::user() const +{ + Q_D(const QPlaceContent); + return d->user; +} + +/*! + Sets the \a user who contributed this content. +*/ +void QPlaceContent::setUser(const QPlaceUser &user) +{ + Q_D(QPlaceContent); + d->user = user; +} + +/*! + Returns a rich text attribution string. + + \b {Note}: Some providers may require that the attribution + of a particular content item always be displayed + when the content item is shown. +*/ +QString QPlaceContent::attribution() const +{ + Q_D(const QPlaceContent); + return d->attribution; +} + +/*! + Sets a rich text \a attribution string for this content item. +*/ +void QPlaceContent::setAttribution(const QString &attribution) +{ + Q_D(QPlaceContent); + d->attribution = attribution; +} + +/*! + \internal + Constructs a new content object from the given pointer \a d. +*/ +QPlaceContent::QPlaceContent(QPlaceContentPrivate *d) + :d_ptr(d) +{ +} diff --git a/src/location/places/qplacecontent.h b/src/location/places/qplacecontent.h new file mode 100644 index 0000000..d8e4d4e --- /dev/null +++ b/src/location/places/qplacecontent.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPLACECONTENT_H +#define QPLACECONTENT_H + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#define Q_DECLARE_CONTENT_D_FUNC(Class) \ + inline Class##Private *d_func(); \ + inline const Class##Private *d_func() const;\ + friend class Class##Private; + +#define Q_DECLARE_CONTENT_COPY_CTOR(Class) \ + Class(const QPlaceContent &other); + +class QPlaceUser; +class QPlaceSupplier; +class QPlaceContentPrivate; +class Q_LOCATION_EXPORT QPlaceContent +{ +public: + typedef QMap Collection; + + enum Type { + NoType = 0, + ImageType, + ReviewType, + EditorialType, + CustomType = 0x0100 + }; + + QPlaceContent(); + QPlaceContent(const QPlaceContent &other); + virtual ~QPlaceContent(); + + QPlaceContent &operator=(const QPlaceContent &other); + + bool operator==(const QPlaceContent &other) const; + bool operator!=(const QPlaceContent &other) const; + + QPlaceContent::Type type() const; + + QPlaceSupplier supplier() const; + void setSupplier(const QPlaceSupplier &supplier); + + QPlaceUser user() const; + void setUser(const QPlaceUser &user); + + QString attribution() const; + void setAttribution(const QString &attribution); + +protected: + explicit QPlaceContent(QPlaceContentPrivate *d); + QSharedDataPointer d_ptr; + +private: + inline QPlaceContentPrivate *d_func(); + inline const QPlaceContentPrivate *d_func() const; + + friend class QPlaceContentPrivate; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceContent) +Q_DECLARE_METATYPE(QPlaceContent::Type) + +#endif + diff --git a/src/location/places/qplacecontent_p.h b/src/location/places/qplacecontent_p.h new file mode 100644 index 0000000..23b176b --- /dev/null +++ b/src/location/places/qplacecontent_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECONTENT_P_H +#define QPLACECONTENT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplacecontent.h" +#include "qplacesupplier.h" +#include "qplaceuser.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + + +#define Q_IMPLEMENT_CONTENT_D_FUNC(Class) \ + Class##Private *Class::d_func() { return reinterpret_cast(d_ptr.data()); } \ + const Class##Private *Class::d_func() const { return reinterpret_cast(d_ptr.constData()); } \ + +#define Q_IMPLEMENT_CONTENT_COPY_CTOR(Class) \ + Class::Class(const QPlaceContent &other) : QPlaceContent() { Class##Private::copyIfPossible(d_ptr, other); } + +#define Q_DEFINE_CONTENT_PRIVATE_HELPER(Class, ContentType) \ + virtual QPlaceContentPrivate *clone() const { return new Class##Private(*this); } \ + virtual QPlaceContent::Type type() const {return ContentType;} \ + static void copyIfPossible(QSharedDataPointer &d_ptr, const QPlaceContent &other) \ + { \ + if (other.type() == ContentType) \ + d_ptr = extract_d(other); \ + else \ + d_ptr = new Class##Private; \ + } + +class QPlaceContentPrivate : public QSharedData +{ +public: + QPlaceContentPrivate(){} + virtual ~QPlaceContentPrivate(){} + + virtual bool compare(const QPlaceContentPrivate *other) const; + virtual QPlaceContentPrivate *clone() const = 0; + virtual QPlaceContent::Type type() const = 0; + + /* Helper functions for C++ protection rules */ + static const QSharedDataPointer &extract_d(const QPlaceContent &other) {return other.d_ptr;} + + QPlaceSupplier supplier; + QPlaceUser user; + QString attribution; +}; + +template<> QPlaceContentPrivate *QSharedDataPointer::clone(); + +QT_END_NAMESPACE + +#endif + diff --git a/src/location/places/qplacecontentreply.cpp b/src/location/places/qplacecontentreply.cpp new file mode 100644 index 0000000..f603019 --- /dev/null +++ b/src/location/places/qplacecontentreply.cpp @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecontentreply.h" +#include "qplacereply_p.h" + +QT_BEGIN_NAMESPACE +class QPlaceContentReplyPrivate : public QPlaceReplyPrivate +{ +public: + QPlaceContentReplyPrivate() + : totalCount(0) + { } + + QPlaceContent::Collection contentCollection; + int totalCount; + QPlaceContentRequest contentRequest; + QPlaceContentRequest previousPageRequest; + QPlaceContentRequest nextPageRequest; +}; + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +/*! + \class QPlaceContentReply + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-replies + \since 5.6 + + \brief The QPlaceContentReply class manages a content retrieval operation started by an + instance of QPlaceManager. + + See \l {Fetching Rich Content} for an example on how to use a content reply. + \sa QPlaceContentRequest, QPlaceManager +*/ + +/*! + Constructs a content reply with a given \a parent. +*/ +QPlaceContentReply::QPlaceContentReply(QObject *parent) + : QPlaceReply(new QPlaceContentReplyPrivate, parent) +{ +} + +/*! + Destroys the reply. +*/ +QPlaceContentReply::~QPlaceContentReply() +{ +} + + /*! + Returns the collection of content retrieved. +*/ +QPlaceContent::Collection QPlaceContentReply::content() const +{ + Q_D(const QPlaceContentReply); + return d->contentCollection; +} + +/*! + Returns the type of reply. +*/ +QPlaceReply::Type QPlaceContentReply::type() const +{ + return QPlaceReply::ContentReply; +} + +/*! + Sets the \a content of the reply. +*/ +void QPlaceContentReply::setContent(const QPlaceContent::Collection &content) +{ + Q_D(QPlaceContentReply); + d->contentCollection = content; +} + +/*! + Returns the total number of content objects for a place. If the total number of + content objects cannot be counted, a value of -1 is returned. This count only + refers to the total count for a single content type, that is, the content type that + was specified when content was requested with the QPlaceManager. +*/ +int QPlaceContentReply::totalCount() const +{ + Q_D(const QPlaceContentReply); + return d->totalCount; +} + +/*! + Sets the \a total number of content objects for a place. +*/ +void QPlaceContentReply::setTotalCount(int total) +{ + Q_D(QPlaceContentReply); + d->totalCount = total; +} + +/*! + Returns the content request that was used to generate this reply. +*/ +QPlaceContentRequest QPlaceContentReply::request() const +{ + Q_D(const QPlaceContentReply); + return d->contentRequest; +} + +/*! + Returns a place content request that can be used to request the previous batch of place content + results. +*/ +QPlaceContentRequest QPlaceContentReply::previousPageRequest() const +{ + Q_D(const QPlaceContentReply); + return d->previousPageRequest; +} + +/*! + Returns a place content request that can be used to request the next batch of place content + results. +*/ +QPlaceContentRequest QPlaceContentReply::nextPageRequest() const +{ + Q_D(const QPlaceContentReply); + return d->nextPageRequest; +} + +/*! + Sets the content \a request used to generate this this reply. +*/ +void QPlaceContentReply::setRequest(const QPlaceContentRequest &request) +{ + Q_D(QPlaceContentReply); + d->contentRequest = request; +} + +/*! + Sets the place content request that can be used to request the previous batch of place content + results to \a previous. +*/ +void QPlaceContentReply::setPreviousPageRequest(const QPlaceContentRequest &previous) +{ + Q_D(QPlaceContentReply); + d->previousPageRequest = previous; +} + +/*! + Sets the place content request that can be used to request the next batch of place content + results to \a next. +*/ +void QPlaceContentReply::setNextPageRequest(const QPlaceContentRequest &next) +{ + Q_D(QPlaceContentReply); + d->nextPageRequest = next; +} diff --git a/src/location/places/qplacecontentreply.h b/src/location/places/qplacecontentreply.h new file mode 100644 index 0000000..929d338 --- /dev/null +++ b/src/location/places/qplacecontentreply.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECONTENTREPLY_H +#define QPLACECONTENTREPLY_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceContentReplyPrivate; +class Q_LOCATION_EXPORT QPlaceContentReply : public QPlaceReply +{ + Q_OBJECT + +public: + explicit QPlaceContentReply(QObject *parent = nullptr); + virtual ~QPlaceContentReply(); + + QPlaceReply::Type type() const; + + QPlaceContent::Collection content() const; + + int totalCount() const; + + QPlaceContentRequest request() const; + + QPlaceContentRequest previousPageRequest() const; + QPlaceContentRequest nextPageRequest() const; + +protected: + void setContent(const QPlaceContent::Collection &content); + void setTotalCount(int total); + void setRequest(const QPlaceContentRequest &request); + void setPreviousPageRequest(const QPlaceContentRequest &previous); + void setNextPageRequest(const QPlaceContentRequest &next); + +private: + Q_DISABLE_COPY(QPlaceContentReply) + Q_DECLARE_PRIVATE(QPlaceContentReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacecontentrequest.cpp b/src/location/places/qplacecontentrequest.cpp new file mode 100644 index 0000000..5a211ae --- /dev/null +++ b/src/location/places/qplacecontentrequest.cpp @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecontentrequest_p.h" +#include "qplacecontentrequest.h" +#include "qgeocoordinate.h" + +QT_BEGIN_NAMESPACE + +QPlaceContentRequestPrivate::QPlaceContentRequestPrivate() +: QSharedData(), contentType(QPlaceContent::NoType), limit(-1) +{ +} + +QPlaceContentRequestPrivate::QPlaceContentRequestPrivate(const QPlaceContentRequestPrivate &other) +: QSharedData(other), contentType(other.contentType), placeId(other.placeId), + contentContext(other.contentContext), limit(other.limit) +{ +} + +QPlaceContentRequestPrivate::~QPlaceContentRequestPrivate() +{ +} + +bool QPlaceContentRequestPrivate::operator==(const QPlaceContentRequestPrivate &other) const +{ + return contentType == other.contentType + && limit == other.limit; +} + +void QPlaceContentRequestPrivate::clear() +{ + contentType = QPlaceContent::NoType; + limit = -1; +} + +/*! + \class QPlaceContentRequest + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-requests + \since 5.6 + + \brief The QPlaceContentRequest class represents the parameters of a content request. + + The QPlaceContentRequest class is used in conjunction with a QPlaceManager to + retrieve rich content like images and reviews in a paginated fashion. + The following code would request a set of 5 images from the 10th index: + + \snippet places/requesthandler.h Content request + \dots + \dots + \snippet places/requesthandler.h Content handler + + \sa QPlaceContentReply +*/ + +/*! + Constructs a new request object. +*/ +QPlaceContentRequest::QPlaceContentRequest() + : d_ptr(new QPlaceContentRequestPrivate()) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QPlaceContentRequest::QPlaceContentRequest(const QPlaceContentRequest &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys the request object +*/ +QPlaceContentRequest::~QPlaceContentRequest() +{ +} + +/*! + Assigns \a other to this content request and returns a reference + to this content request. +*/ +QPlaceContentRequest &QPlaceContentRequest::operator= (const QPlaceContentRequest & other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if \a other is equal to this content request, + otherwise returns false. +*/ +bool QPlaceContentRequest::operator== (const QPlaceContentRequest &other) const +{ + Q_D(const QPlaceContentRequest); + return *d == *other.d_func(); +} + +/*! + Returns true if \a other is not equal to this content request, + otherwise returns false. +*/ +bool QPlaceContentRequest::operator!= (const QPlaceContentRequest &other) const +{ + Q_D(const QPlaceContentRequest); + return !(*d == *other.d_func()); +} + +/*! + Returns the type of content to be requested, for example reviews or images +*/ +QPlaceContent::Type QPlaceContentRequest::contentType() const +{ + Q_D(const QPlaceContentRequest); + return d->contentType; +} + +/*! + Sets the \a type of content to be requested. +*/ +void QPlaceContentRequest::setContentType(QPlaceContent::Type type) +{ + Q_D(QPlaceContentRequest); + d->contentType = type; +} + +/*! + Returns the identifier of the place content is to be fetched for. +*/ +QString QPlaceContentRequest::placeId() const +{ + Q_D(const QPlaceContentRequest); + return d->placeId; +} + +/*! + Sets the identifier of the place to fetch content for to \a identifier. +*/ +void QPlaceContentRequest::setPlaceId(const QString &identifier) +{ + Q_D(QPlaceContentRequest); + d->placeId = identifier; +} + +/*! + Returns backend specific additional content context associated with this place content request. +*/ +QVariant QPlaceContentRequest::contentContext() const +{ + Q_D(const QPlaceContentRequest); + return d->contentContext; +} + +/*! + Sets the content context to \a context. + + \note This method is intended to be used by geo service plugins when returning place content + results. + + The content context is used by backends to store additional content context related to the + content request. Other relevant fields should also be filled in. For example, if the content + request is for image content the content type should also be set with \l setContentType(). The + content context allows additional context to be kept which is not directly accessible via the + Qt Location API. + + The content context can be of any type storable in a QVariant. The value of the content context + is not intended to be used directly by applications. +*/ +void QPlaceContentRequest::setContentContext(const QVariant &context) +{ + Q_D(QPlaceContentRequest); + d->contentContext = context; +} + +/*! + Returns the maximum number of content items to retrieve. + + A negative value for limit means that it is undefined. It is left up to the backend + provider to choose an appropriate number of items to return. + + The default limit is -1. +*/ +int QPlaceContentRequest::limit() const +{ + Q_D(const QPlaceContentRequest); + return d->limit; +} + +/*! + Set the maximum number of content items to retrieve to + \a limit. +*/ +void QPlaceContentRequest::setLimit(int limit) +{ + Q_D(QPlaceContentRequest); + d->limit = limit; +} + +/*! + Clears the content request. +*/ +void QPlaceContentRequest::clear() +{ + Q_D(QPlaceContentRequest); + d->clear(); +} + +inline QPlaceContentRequestPrivate *QPlaceContentRequest::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QPlaceContentRequestPrivate *QPlaceContentRequest::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +QT_END_NAMESPACE diff --git a/src/location/places/qplacecontentrequest.h b/src/location/places/qplacecontentrequest.h new file mode 100644 index 0000000..51b88f3 --- /dev/null +++ b/src/location/places/qplacecontentrequest.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECONTENTREQUEST_H +#define QPLACECONTENTREQUEST_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceContentRequestPrivate; + +class Q_LOCATION_EXPORT QPlaceContentRequest +{ +public: + QPlaceContentRequest(); + QPlaceContentRequest(const QPlaceContentRequest &other); + ~QPlaceContentRequest(); + + QPlaceContentRequest &operator=(const QPlaceContentRequest &other); + + bool operator==(const QPlaceContentRequest &other) const; + bool operator!=(const QPlaceContentRequest &other) const; + + QPlaceContent::Type contentType() const; + void setContentType(QPlaceContent::Type type); + + QString placeId() const; + void setPlaceId(const QString &identifier); + + QVariant contentContext() const; + void setContentContext(const QVariant &context); + + int limit() const; + void setLimit(int limit); + + void clear(); + +private: + QSharedDataPointer d_ptr; + inline QPlaceContentRequestPrivate *d_func(); + inline const QPlaceContentRequestPrivate *d_func() const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacecontentrequest_p.h b/src/location/places/qplacecontentrequest_p.h new file mode 100644 index 0000000..d9ba370 --- /dev/null +++ b/src/location/places/qplacecontentrequest_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECONTENTREQUEST_P_H +#define QPLACECONTENTREQUEST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceContentRequestPrivate : public QSharedData +{ +public: + QPlaceContentRequestPrivate(); + QPlaceContentRequestPrivate(const QPlaceContentRequestPrivate &other); + ~QPlaceContentRequestPrivate(); + + QPlaceContentRequestPrivate &operator=(const QPlaceContentRequestPrivate &other); + bool operator==(const QPlaceContentRequestPrivate &other) const; + + void clear(); + + QPlaceContent::Type contentType; + QString placeId; + QVariant contentContext; + int limit; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacedetailsreply.cpp b/src/location/places/qplacedetailsreply.cpp new file mode 100644 index 0000000..84ea9f8 --- /dev/null +++ b/src/location/places/qplacedetailsreply.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacedetailsreply.h" +#include "qplacereply_p.h" + +QT_BEGIN_NAMESPACE +class QPlaceDetailsReplyPrivate : public QPlaceReplyPrivate +{ +public: + QPlaceDetailsReplyPrivate() {} + ~QPlaceDetailsReplyPrivate() {} + QPlace result; +}; + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +/*! + \class QPlaceDetailsReply + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-replies + \since 5.6 + + \brief The QPlaceDetailsReply class manages a place details fetch operation started by an + instance of QPlaceManager. + + See \l {QML Places API#Fetching Place Details}{Fetching Place Details} for an example on how to use a details reply. + \sa QPlaceManager +*/ + +/*! + Constructs a details reply with a given \a parent. +*/ +QPlaceDetailsReply::QPlaceDetailsReply(QObject *parent) + : QPlaceReply(new QPlaceDetailsReplyPrivate, parent) +{ +} + +/*! + Destroys the details reply. +*/ +QPlaceDetailsReply::~QPlaceDetailsReply() +{ +} + +/*! + Returns the type of reply. +*/ +QPlaceReply::Type QPlaceDetailsReply::type() const +{ + return QPlaceReply::DetailsReply; +} + + /*! + Returns the place that was fetched. +*/ +QPlace QPlaceDetailsReply::place() const +{ + Q_D(const QPlaceDetailsReply); + return d->result; +} + +/*! + Sets the fetched \a place of the reply. +*/ +void QPlaceDetailsReply::setPlace(const QPlace &place) +{ + Q_D(QPlaceDetailsReply); + d->result = place; +} diff --git a/src/location/places/qplacedetailsreply.h b/src/location/places/qplacedetailsreply.h new file mode 100644 index 0000000..6c3acca --- /dev/null +++ b/src/location/places/qplacedetailsreply.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEDETAILSREPLY_H +#define QPLACEDETAILSREPLY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceDetailsReplyPrivate; +class Q_LOCATION_EXPORT QPlaceDetailsReply : public QPlaceReply +{ + Q_OBJECT +public: + explicit QPlaceDetailsReply(QObject *parent = nullptr); + virtual ~QPlaceDetailsReply(); + + QPlaceReply::Type type() const; + + QPlace place() const; + +protected: + void setPlace(const QPlace &place); + +private: + Q_DISABLE_COPY(QPlaceDetailsReply) + Q_DECLARE_PRIVATE(QPlaceDetailsReply) +}; + +QT_END_NAMESPACE + +#endif // QPLACEDETAILSREPLY_H diff --git a/src/location/places/qplaceeditorial.cpp b/src/location/places/qplaceeditorial.cpp new file mode 100644 index 0000000..bd1cb64 --- /dev/null +++ b/src/location/places/qplaceeditorial.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceeditorial.h" +#include "qplaceeditorial_p.h" + +QT_USE_NAMESPACE + +QPlaceEditorialPrivate::QPlaceEditorialPrivate() +: QPlaceContentPrivate() +{ +} + +QPlaceEditorialPrivate::QPlaceEditorialPrivate(const QPlaceEditorialPrivate &other) +: QPlaceContentPrivate(other), text(other.text), contentTitle(other.contentTitle), + language(other.language) +{ +} + +QPlaceEditorialPrivate::~QPlaceEditorialPrivate() +{ +} + +bool QPlaceEditorialPrivate::compare(const QPlaceContentPrivate *other) const +{ + const QPlaceEditorialPrivate *od = static_cast(other); + return QPlaceContentPrivate::compare(other) + && text == od->text + && contentTitle == od->contentTitle + && language == od->language; +} + +/*! + \class QPlaceEditorial + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceEditorial class represents a publisher's article describing a place. + + Each QPlaceEditorial has a title, text and language; in addition to those properties + inherited from QPlaceContent. + + Note: The Places API only supports editorials as 'retrieve-only' objects. Submitting editorials + to a provider is not a supported use case. + + \sa QPlaceContent +*/ + +/*! + Constructs a new editorial object. +*/ +QPlaceEditorial::QPlaceEditorial() +: QPlaceContent(new QPlaceEditorialPrivate) +{ +} + +/*! + Destructor. +*/ +QPlaceEditorial::~QPlaceEditorial() +{ +} + +/*! + \fn QPlaceEditorial::QPlaceEditorial(const QPlaceContent &other) + Constructs a copy of \a other if possible, otherwise constructs a default editorial object. +*/ +Q_IMPLEMENT_CONTENT_COPY_CTOR(QPlaceEditorial) + +Q_IMPLEMENT_CONTENT_D_FUNC(QPlaceEditorial) + +/*! + Returns a textual description of the place. + + Depending upon the provider, the + editorial text could be either rich(HTML based) text or plain text. +*/ +QString QPlaceEditorial::text() const +{ + Q_D(const QPlaceEditorial); + return d->text; +} + +/*! + Sets the \a text of the editorial. +*/ +void QPlaceEditorial::setText(const QString &text) +{ + Q_D(QPlaceEditorial); + d->text = text; +} + +/*! + Returns the title of the editorial. +*/ +QString QPlaceEditorial::title() const +{ + Q_D(const QPlaceEditorial); + return d->contentTitle; +} + +/*! + Sets the \a title of the editorial. +*/ +void QPlaceEditorial::setTitle(const QString &title) +{ + Q_D(QPlaceEditorial); + d->contentTitle = title; +} + +/*! + Returns the language of the editorial. Typically this would be a language code + in the 2 letter ISO 639-1 format. +*/ +QString QPlaceEditorial::language() const +{ + Q_D(const QPlaceEditorial); + return d->language; +} + +/*! + Sets the \a language of the editorial. Typically this would be a language code + in the 2 letter ISO 639-1 format. +*/ +void QPlaceEditorial::setLanguage(const QString &language) +{ + Q_D(QPlaceEditorial); + d->language = language; +} diff --git a/src/location/places/qplaceeditorial.h b/src/location/places/qplaceeditorial.h new file mode 100644 index 0000000..ecfe8db --- /dev/null +++ b/src/location/places/qplaceeditorial.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEEDITORIAL_H +#define QPLACEEDITORIAL_H + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceEditorialPrivate; + +class Q_LOCATION_EXPORT QPlaceEditorial : public QPlaceContent +{ +public: + QPlaceEditorial(); +#ifdef Q_QDOC + QPlaceEditorial(const QPlaceContent &other); +#else + Q_DECLARE_CONTENT_COPY_CTOR(QPlaceEditorial) +#endif + + virtual ~QPlaceEditorial(); + + QString text() const; + void setText(const QString &text); + QString title() const; + void setTitle(const QString &data); + QString language() const; + void setLanguage(const QString &data); + +private: + Q_DECLARE_CONTENT_D_FUNC(QPlaceEditorial) +}; + +QT_END_NAMESPACE + +#endif // QPLACEEDITORIAL_H diff --git a/src/location/places/qplaceeditorial_p.h b/src/location/places/qplaceeditorial_p.h new file mode 100644 index 0000000..f502db7 --- /dev/null +++ b/src/location/places/qplaceeditorial_p.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEDESCRIPTION_P_H +#define QPLACEDESCRIPTION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include "qplacecontent_p.h" + +QT_BEGIN_NAMESPACE + +class QPlaceEditorialPrivate : public QPlaceContentPrivate +{ +public: + QPlaceEditorialPrivate(); + QPlaceEditorialPrivate(const QPlaceEditorialPrivate &other); + + ~QPlaceEditorialPrivate(); + + bool compare(const QPlaceContentPrivate *other) const; + + Q_DEFINE_CONTENT_PRIVATE_HELPER(QPlaceEditorial, QPlaceContent::EditorialType) + + QString text; + QString contentTitle; + QString language; +}; + +QT_END_NAMESPACE + +#endif // QPLACEDESCRIPTION_P_H diff --git a/src/location/places/qplaceicon.cpp b/src/location/places/qplaceicon.cpp new file mode 100644 index 0000000..c3a2b85 --- /dev/null +++ b/src/location/places/qplaceicon.cpp @@ -0,0 +1,233 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceicon.h" +#include "qplaceicon_p.h" +#include "qplacemanager.h" +#include "qplacemanagerengine.h" + +QT_USE_NAMESPACE + +QPlaceIconPrivate::QPlaceIconPrivate() + : QSharedData(), manager(0) +{ +} + +QPlaceIconPrivate::QPlaceIconPrivate(const QPlaceIconPrivate &other) + : QSharedData(other), + manager(other.manager), + parameters(other.parameters) +{ +} + +QPlaceIconPrivate::~QPlaceIconPrivate() +{ +} + +QPlaceIconPrivate &QPlaceIconPrivate::operator=(const QPlaceIconPrivate &other) +{ + if (this == &other) + return *this; + + manager = other.manager; + parameters = other.parameters; + + return *this; +} + +bool QPlaceIconPrivate::operator == (const QPlaceIconPrivate &other) const +{ + return manager == other.manager + && parameters == other.parameters; +} + +/*! + \class QPlaceIcon + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceIcon class represents an icon. + + The typical usage of an icon is to use the url() function to specify + a preferred icon size. + + \snippet places/requesthandler.h icon + + The icons are typically backend dependent, if a manager backend does not support a given size, the URL of the icon that most + closely matches those parameters is returned. + + The icon class also has a key-value set of parameters. The precise key one + needs to use depends on the \l {Qt Location#Plugin References and Parameters}{plugin} + being used. These parameters influence which icon URL is returned by + the manager and may also be used to specify icon URL locations when + saving icons. + + If there is only ever one image for an icon, then QPlaceIcon::SingleUrl can be used as a parameter + key with a QUrl as the associated value. If this key is set, then the url() function will always return the specified URL + and not defer to any manager. +*/ + +/*! + \variable QPlaceIcon::SingleUrl + \brief Parameter key for an icon that only has a single image URL. + + The parameter value to be used with this key is a QUrl. An icon with this parameter set will + always return the specified URL regardless of the requested size when url() is called. +*/ +const QString QPlaceIcon::SingleUrl(QLatin1String("singleUrl")); + +/*! + Constructs an icon. +*/ +QPlaceIcon::QPlaceIcon() + : d(new QPlaceIconPrivate) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QPlaceIcon::QPlaceIcon(const QPlaceIcon &other) + : d(other.d) +{ +} + +/*! + Destroys the icon. +*/ +QPlaceIcon::~QPlaceIcon() +{ +} + +/*! + Assigns \a other to this icon and returns a reference to this icon. +*/ +QPlaceIcon &QPlaceIcon::operator=(const QPlaceIcon &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +/*! + Returns true if this icon is equal to \a other, otherwise returns false. +*/ +bool QPlaceIcon::operator==(const QPlaceIcon &other) const +{ + return *d == *(other.d); +} + +/*! + \fn QPlaceIcon::operator!=(const QPlaceIcon &other) const + + Returns true if \a other is not equal to this icon, otherwise returns false. +*/ + +/*! + Returns an icon URL according to the given \a size. + + If no manager has been assigned to the icon, and the parameters do not contain the QPlaceIcon::SingleUrl key, a default constructed QUrl + is returned. +*/ +QUrl QPlaceIcon::url(const QSize &size) const +{ + if (d->parameters.contains(QPlaceIcon::SingleUrl)) { + QVariant value = d->parameters.value(QPlaceIcon::SingleUrl); + if (value.type() == QVariant::Url) + return value.toUrl(); + else if (value.type() == QVariant::String) + return QUrl::fromUserInput(value.toString()); + + return QUrl(); + } + + if (!d->manager) + return QUrl(); + + return d->manager->d->constructIconUrl(*this, size); +} + +/*! + Returns a set of parameters for the icon that are manager/plugin specific. + These parameters are used by the manager to return the appropriate + URL when url() is called and to specify locations to save to + when saving icons. + + Consult the \l {Qt Location#Plugin References and Parameters}{plugin documentation} + for what parameters are supported and how they should be used. +*/ +QVariantMap QPlaceIcon::parameters() const +{ + return d->parameters; +} + +/*! + Sets the parameters of the icon to \a parameters. +*/ +void QPlaceIcon::setParameters(const QVariantMap ¶meters) +{ + d->parameters = parameters; +} + +/*! + Returns the manager that this icon is associated with. +*/ +QPlaceManager *QPlaceIcon::manager() const +{ + return d->manager; +} + +/*! + Sets the \a manager that this icon is associated with. The icon does not take + ownership of the pointer. +*/ +void QPlaceIcon::setManager(QPlaceManager *manager) +{ + d->manager = manager; +} + +/*! + Returns a boolean indicating whether the all the fields of the icon are empty or not. +*/ +bool QPlaceIcon::isEmpty() const +{ + return (d->manager == 0 + && d->parameters.isEmpty()); +} diff --git a/src/location/places/qplaceicon.h b/src/location/places/qplaceicon.h new file mode 100644 index 0000000..c603572 --- /dev/null +++ b/src/location/places/qplaceicon.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEICON_H +#define QPLACEICON_H + +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManager; + +class QPlaceIconPrivate; +class Q_LOCATION_EXPORT QPlaceIcon +{ +public: + static const QString SingleUrl; + + QPlaceIcon(); + QPlaceIcon(const QPlaceIcon &other); + + ~QPlaceIcon(); + + QPlaceIcon &operator=(const QPlaceIcon &other); + bool operator == (const QPlaceIcon &other) const; + bool operator != (const QPlaceIcon &other) const { + return !(*this == other); + } + + QUrl url(const QSize &size = QSize()) const; + + QPlaceManager *manager() const; + void setManager(QPlaceManager *manager); + + QVariantMap parameters() const; + void setParameters(const QVariantMap ¶meters); + + bool isEmpty() const; + +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceIcon) + +#endif diff --git a/src/location/places/qplaceicon_p.h b/src/location/places/qplaceicon_p.h new file mode 100644 index 0000000..8f94102 --- /dev/null +++ b/src/location/places/qplaceicon_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEICON_P_H +#define QPLACEICON_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManager; +class QPlaceIconPrivate: public QSharedData +{ +public: + QPlaceIconPrivate(); + QPlaceIconPrivate(const QPlaceIconPrivate &other); + ~QPlaceIconPrivate(); + + QPlaceIconPrivate &operator=(const QPlaceIconPrivate &other); + bool operator == (const QPlaceIconPrivate &other) const; + + QPlaceManager *manager; + QVariantMap parameters; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplaceidreply.cpp b/src/location/places/qplaceidreply.cpp new file mode 100644 index 0000000..7fb8bbe --- /dev/null +++ b/src/location/places/qplaceidreply.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceidreply.h" +#include "qplacereply_p.h" + +QT_BEGIN_NAMESPACE +class QPlaceIdReplyPrivate : public QPlaceReplyPrivate +{ +public: + QPlaceIdReplyPrivate(QPlaceIdReply::OperationType operationType) + : operationType(operationType) {} + ~QPlaceIdReplyPrivate() {} + QString id; + QPlaceIdReply::OperationType operationType; +}; + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +/*! + \class QPlaceIdReply + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-replies + \since 5.6 + + \brief The QPlaceIdReply class manages operations which return an identifier such as + saving and removal operations of places and categories. + + The QPlaceIdReply can be considered a multipurpose reply in that it can + be used to save places, save categories, remove places and remove categories. + In each case it returns an identifier of the place or category that was added, modified or removed. + + See \l {Saving a place cpp}{Saving a place} for an example of how to use an identifier reply. + \sa QPlaceManager +*/ + +/*! + \enum QPlaceIdReply::OperationType + Defines the type of operation that was used to generate this reply. + \value SavePlace The reply was created for a save place operation + \value RemovePlace The reply was created for a remove place operation. + \value SaveCategory The reply was created for a save category operation + \value RemoveCategory The reply was created for a remove category operation. +*/ + +/*! + Constructs a reply which contains the identifier of the object operated upon. The reply is for the given \a operationType and with \a parent. +*/ +QPlaceIdReply::QPlaceIdReply(QPlaceIdReply::OperationType operationType, QObject *parent) + : QPlaceReply(new QPlaceIdReplyPrivate(operationType), parent) {} + +/*! + Destroys the reply. +*/ +QPlaceIdReply::~QPlaceIdReply() +{ +} + +/*! + Returns the type of reply. +*/ +QPlaceReply::Type QPlaceIdReply::type() const +{ + return QPlaceReply::IdReply; +} + +/*! + Returns the operation type of the reply. This means whether this + identifier reply was for a save place operation, + remove category operation and so on. +*/ +QPlaceIdReply::OperationType QPlaceIdReply::operationType() const +{ + Q_D(const QPlaceIdReply); + return d->operationType; +} + +/*! + Returns the relevant identifier for the operation. For example for a save place operation, + the identifier is that of the saved place. For a category removal operation, + it is the identifier of the category that was removed. +*/ +QString QPlaceIdReply::id() const +{ + Q_D(const QPlaceIdReply); + return d->id; +} + +/*! + Sets the \a identifier of the reply. +*/ +void QPlaceIdReply::setId(const QString &identifier) +{ + Q_D(QPlaceIdReply); + d->id = identifier; +} diff --git a/src/location/places/qplaceidreply.h b/src/location/places/qplaceidreply.h new file mode 100644 index 0000000..6ecf12c --- /dev/null +++ b/src/location/places/qplaceidreply.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEIDREPLY_H +#define QPLACEIDREPLY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceIdReplyPrivate; +class Q_LOCATION_EXPORT QPlaceIdReply : public QPlaceReply +{ + Q_OBJECT +public: + enum OperationType + { + SavePlace, + SaveCategory, + RemovePlace, + RemoveCategory + }; + + explicit QPlaceIdReply(OperationType operationType, QObject *parent = nullptr); + virtual ~QPlaceIdReply(); + + QPlaceReply::Type type() const; + OperationType operationType() const; + + QString id() const; + +protected: + void setId(const QString &identifier); +private: + Q_DISABLE_COPY(QPlaceIdReply) + Q_DECLARE_PRIVATE(QPlaceIdReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplaceimage.cpp b/src/location/places/qplaceimage.cpp new file mode 100644 index 0000000..f3dda15 --- /dev/null +++ b/src/location/places/qplaceimage.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceimage.h" +#include "qplaceimage_p.h" + +QT_USE_NAMESPACE + +QPlaceImagePrivate::QPlaceImagePrivate() : QPlaceContentPrivate() +{ +} + +QPlaceImagePrivate::QPlaceImagePrivate(const QPlaceImagePrivate &other) + : QPlaceContentPrivate(other) +{ + url = other.url; + id = other.id; + mimeType = other.mimeType; +} + +QPlaceImagePrivate::~QPlaceImagePrivate() +{ +} + +bool QPlaceImagePrivate::compare(const QPlaceContentPrivate *other) const +{ + const QPlaceImagePrivate *od = static_cast(other); + return QPlaceContentPrivate::compare(other) + && url == od->url && id == od->id && mimeType == od->mimeType; +} + +/*! + \class QPlaceImage + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceImage class represents a reference to an image. + + Each QPlaceImage represents a set of metadata about an image such as it's + url, identifier and MIME type. These are properties in addition to those provided + by QPlaceContent. + + Note: The Places API only supports images as 'retrieve-only' objects. Submitting + images to a provider is not a supported use case. + \sa QPlaceContent +*/ + +/*! + Constructs an new QPlaceImage. +*/ +QPlaceImage::QPlaceImage() + : QPlaceContent(new QPlaceImagePrivate) +{ +} + +/*! + Destructor. +*/ +QPlaceImage::~QPlaceImage() +{ +} + +/*! + \fn QPlaceImage::QPlaceImage(const QPlaceContent &other) + Constructs a copy of \a other if possible, otherwise constructs a default image. +*/ + +Q_IMPLEMENT_CONTENT_COPY_CTOR(QPlaceImage) + +Q_IMPLEMENT_CONTENT_D_FUNC(QPlaceImage) + +/*! + Returns the image's url. +*/ +QUrl QPlaceImage::url() const +{ + Q_D(const QPlaceImage); + return d->url; +} + +/*! + Sets the image's \a url. +*/ +void QPlaceImage::setUrl(const QUrl &url) +{ + Q_D(QPlaceImage); + d->url = url; +} + +/*! + Returns the image's identifier. +*/ +QString QPlaceImage::imageId() const +{ + Q_D(const QPlaceImage); + return d->id; +} + +/*! + Sets image's \a identifier. +*/ +void QPlaceImage::setImageId(const QString &identifier) +{ + Q_D(QPlaceImage); + d->id = identifier; +} + +/*! + Returns the image's MIME type. +*/ +QString QPlaceImage::mimeType() const +{ + Q_D(const QPlaceImage); + return d->mimeType; +} + +/*! + Sets image's MIME \a type. +*/ +void QPlaceImage::setMimeType(const QString &type) +{ + Q_D(QPlaceImage); + d->mimeType = type; +} diff --git a/src/location/places/qplaceimage.h b/src/location/places/qplaceimage.h new file mode 100644 index 0000000..732d3de --- /dev/null +++ b/src/location/places/qplaceimage.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEIMAGE_H +#define QPLACEIMAGE_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceImagePrivate; +class QUrl; + +class Q_LOCATION_EXPORT QPlaceImage : public QPlaceContent +{ +public: + QPlaceImage(); +#ifdef Q_QDOC + QPlaceImage(const QPlaceContent &other); +#else + Q_DECLARE_CONTENT_COPY_CTOR(QPlaceImage) +#endif + + virtual ~QPlaceImage(); + + QUrl url() const; + void setUrl(const QUrl &url); + + QString imageId() const; + void setImageId(const QString &identifier); + + QString mimeType() const; + void setMimeType(const QString &data); + +private: + Q_DECLARE_CONTENT_D_FUNC(QPlaceImage) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplaceimage_p.h b/src/location/places/qplaceimage_p.h new file mode 100644 index 0000000..4030b08 --- /dev/null +++ b/src/location/places/qplaceimage_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEIMAGE_P_H +#define QPLACEIMAGE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include "qplaceimage.h" +#include "qplacecontent_p.h" + +QT_BEGIN_NAMESPACE + +class QPlaceImagePrivate : public QPlaceContentPrivate +{ +public: + QPlaceImagePrivate(); + QPlaceImagePrivate(const QPlaceImagePrivate &other); + + ~QPlaceImagePrivate(); + + bool compare(const QPlaceContentPrivate *other) const; + + Q_DEFINE_CONTENT_PRIVATE_HELPER(QPlaceImage, QPlaceContent::ImageType) + + QUrl url; + QString id; + QString mimeType; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacemanager.cpp b/src/location/places/qplacemanager.cpp new file mode 100644 index 0000000..e78489f --- /dev/null +++ b/src/location/places/qplacemanager.cpp @@ -0,0 +1,489 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacemanager.h" +#include "qplacemanagerengine.h" +#include "qplacemanagerengine_p.h" + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QPlaceManager + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-manager + \since 5.6 + + \brief The QPlaceManager class provides the interface which allows clients to access + places stored in a particular backend. + + The following table gives an overview of the functionality provided by the QPlaceManager + \table + \header + \li Functionality + \li Description + \row + \li Searching for places + \li Using set of parameters such as a search term and search area, relevant places + can be returned to the user. + \row + \li Categories + \li Places can be classified as belonging to different categories. The + manager supports access to these categories. + \row + \li Search term suggestions + \li Given a partially complete search term, a list of potential + search terms can be given. + \row + \li Recommendations + \li Given an existing place, a set of similar recommended places can + be suggested to the user. + \row + \li Rich Content + \li Rich content such as images, reviews etc can be retrieved in a paged + fashion. + \row + \li Place or Category management + \li Places and categories may be saved and removed. It is possible + for notifications to be given when this happens. + \row + \li Localization + \li Different locales may be specified to return place + data in different languages. + \endtable + + \section1 Obtaining a QPlaceManager Instance + Creation of a QPlaceManager is facilitated by the QGeoServiceProvider. + See \l {Initializing a manager} for an example on how to create a manager. + + + \section1 Asynchronous Interface + The QPlaceManager class provides an abstraction of the datastore which contains place information. + The functions provided by the QPlaceManager and primarily asynchronous and follow + a request-reply model. Typically a request is given to the manager, consisting + of a various set of parameters and a reply object is created. The reply object + has a signal to notify when the request is done, and once completed, the reply + contains the results of the request, along with any errors that occurred, if any. + + An asynchronous request is generally handled as follows: + \snippet places/requesthandler.h Simple search + \dots + \dots + \snippet places/requesthandler.h Simple search handler + + See \l {Common Operations} for a list of examples demonstrating how the QPlaceManger + is used. + + \section1 Category Initialization + Sometime during startup of an application, the initializeCategories() function should + be called to setup the categories. Initializing the categories enables the usage of the + following functions: + + \list + \li QPlaceManager::childCategories() + \li QPlaceManager::category() + \li QPlaceManager::parentCategoryId() + \li QPlaceManager::childCategoryIds(); + \endlist + + If the categories need to be refreshed or reloaded, the initializeCategories() function + may be called again. + +*/ + +/*! + Constructs a new manager with the specified \a parent and with the + implementation provided by \a engine. + + This constructor is used internally by QGeoServiceProviderFactory. Regular + users should acquire instances of QGeoRoutingManager with + QGeoServiceProvider::routingManager(); +*/ +QPlaceManager::QPlaceManager(QPlaceManagerEngine *engine, QObject *parent) + : QObject(parent), d(engine) +{ + if (d) { + d->setParent(this); + d->d_ptr->manager = this; + + qRegisterMetaType(); + + connect(d, SIGNAL(finished(QPlaceReply*)), this, SIGNAL(finished(QPlaceReply*))); + connect(d, SIGNAL(error(QPlaceReply*,QPlaceReply::Error)), + this, SIGNAL(error(QPlaceReply*,QPlaceReply::Error))); + + connect(d, SIGNAL(placeAdded(QString)), + this, SIGNAL(placeAdded(QString)), Qt::QueuedConnection); + connect(d, SIGNAL(placeUpdated(QString)), + this, SIGNAL(placeUpdated(QString)), Qt::QueuedConnection); + connect(d, SIGNAL(placeRemoved(QString)), + this, SIGNAL(placeRemoved(QString)), Qt::QueuedConnection); + + connect(d, SIGNAL(categoryAdded(QPlaceCategory,QString)), + this, SIGNAL(categoryAdded(QPlaceCategory,QString))); + connect(d, SIGNAL(categoryUpdated(QPlaceCategory,QString)), + this, SIGNAL(categoryUpdated(QPlaceCategory,QString))); + connect(d, SIGNAL(categoryRemoved(QString,QString)), + this, SIGNAL(categoryRemoved(QString,QString))); + connect(d, SIGNAL(dataChanged()), + this, SIGNAL(dataChanged()), Qt::QueuedConnection); + } else { + qFatal("The place manager engine that was set for this place manager was NULL."); + } +} + +/*! + Destroys the manager. This destructor is used internally by QGeoServiceProvider + and should never need to be called in application code. +*/ +QPlaceManager::~QPlaceManager() +{ + delete d; +} + +/*! + Returns the name of the manager +*/ +QString QPlaceManager::managerName() const +{ + return d->managerName(); +} + +/*! + Returns the manager version. +*/ +int QPlaceManager::managerVersion() const +{ + return d->managerVersion(); +} + +/*! + Retrieves a details of place corresponding to the given \a placeId. + + See \l {QML Places API#Fetching Place Details}{Fetching Place Details} for an example of usage. +*/ +QPlaceDetailsReply *QPlaceManager::getPlaceDetails(const QString &placeId) const +{ + return d->getPlaceDetails(placeId); +} + +/*! + Retrieves content for a place according to the parameters specified in \a request. + + See \l {Fetching Rich Content} for an example of usage. +*/ +QPlaceContentReply *QPlaceManager::getPlaceContent(const QPlaceContentRequest &request) const +{ + return d->getPlaceContent(request); +} + +/*! + Searches for places according to the parameters specified in \a request. + + See \l {Discovery/Search} for an example of usage. +*/ +QPlaceSearchReply *QPlaceManager::search(const QPlaceSearchRequest &request) const +{ + return d->search(request); +} + +/*! + Requests a set of search term suggestions according to the parameters specified in \a request. + The \a request can hold the incomplete search term, along with other data such + as a search area to narrow down relevant results. + + See \l {Search Suggestions} for an example of usage. +*/ +QPlaceSearchSuggestionReply *QPlaceManager::searchSuggestions(const QPlaceSearchRequest &request) const +{ + return d->searchSuggestions(request); +} + +/*! + Saves a specified \a place. + + See \l {Saving a place cpp} for an example of usage. +*/ +QPlaceIdReply *QPlaceManager::savePlace(const QPlace &place) +{ + return d->savePlace(place); +} + +/*! + Removes the place corresponding to \a placeId from the manager. + + See \l {Removing a place cpp} for an example of usage. +*/ +QPlaceIdReply *QPlaceManager::removePlace(const QString &placeId) +{ + return d->removePlace(placeId); +} + +/*! + Saves a \a category that is a child of the category specified by \a parentId. + An empty \a parentId means \a category is saved as a top level category. + + See \l {Saving a category} for an example of usage. +*/ +QPlaceIdReply *QPlaceManager::saveCategory(const QPlaceCategory &category, const QString &parentId) +{ + return d->saveCategory(category, parentId); +} + +/*! + Removes the category corresponding to \a categoryId from the manager. + + See \l {Removing a category} for an example of usage. +*/ +QPlaceIdReply *QPlaceManager::removeCategory(const QString &categoryId) +{ + return d->removeCategory(categoryId); +} + +/*! + Initializes the categories of the manager. + + See \l {Using Categories} for an example of usage. +*/ +QPlaceReply *QPlaceManager::initializeCategories() +{ + return d->initializeCategories(); +} + +/*! + Returns the parent category identifier of the category corresponding to \a categoryId. +*/ +QString QPlaceManager::parentCategoryId(const QString &categoryId) const +{ + return d->parentCategoryId(categoryId); +} + +/*! + Returns the child category identifiers of the category corresponding to \a parentId. + If \a parentId is empty then all top level category identifiers are returned. +*/ +QStringList QPlaceManager::childCategoryIds(const QString &parentId) const +{ + return d->childCategoryIds(parentId); +} + +/*! + Returns the category corresponding to the given \a categoryId. +*/ +QPlaceCategory QPlaceManager::category(const QString &categoryId) const +{ + return d->category(categoryId); +} + +/*! + Returns a list of categories that are children of the category corresponding to \a parentId. + If \a parentId is empty, all the top level categories are returned. +*/ +QList QPlaceManager::childCategories(const QString &parentId) const +{ + return d->childCategories(parentId); +} + +/*! + Returns a list of preferred locales. The locales are used as a hint to the manager for what language + place and category details should be returned in. + + If the first specified locale cannot be accommodated, the manager falls back to the next and so forth. + Some manager backends may not support a set of locales which are rigidly defined. An arbitrary + example is that some places in France could have French and English localizations, while + certain areas in America may only have the English localization available. In this example, + the set of supported locales is context dependent on the search location. + + If the manager cannot accommodate any of the preferred locales, the manager falls + back to using a supported language that is backend specific. + + Support for locales may vary from provider to provider. For those that do support it, + by default, the global default locale is set as the manager's only locale. + + For managers that do not support locales, the locale list is always empty. +*/ +QList QPlaceManager::locales() const +{ + return d->locales(); +} + +/*! + Convenience function which sets the manager's list of preferred locales + to a single \a locale. +*/ +void QPlaceManager::setLocale(const QLocale &locale) +{ + QList locales; + locales << locale; + d->setLocales(locales); +} + +/*! + Set the list of preferred \a locales. +*/ +void QPlaceManager::setLocales(const QList &locales) +{ + d->setLocales(locales); +} + +/*! + Returns a pruned or modified version of the \a original place + which is suitable to be saved into this manager. + + Only place details that are supported by this manager is + present in the modified version. Manager specific data such + as the place id, is not copied over from the \a original. +*/ +QPlace QPlaceManager::compatiblePlace(const QPlace &original) +{ + return d->compatiblePlace(original); +} + +/*! + Returns a reply which contains a list of places which correspond/match those + specified in the \a request. The places specified in the request come from a + different manager. +*/ +QPlaceMatchReply *QPlaceManager::matchingPlaces(const QPlaceMatchRequest &request) const +{ + return d->matchingPlaces(request); +} + +/*! + \fn void QPlaceManager::finished(QPlaceReply *reply) + + This signal is emitted when \a reply has finished processing. + + If reply->error() equals QPlaceReply::NoError then the processing + finished successfully. + + This signal and QPlaceReply::finished() will be emitted at the same time. + + \note Do not delete the \a reply object in the slot connected to this signal. + Use deleteLater() instead. +*/ + +/*! + \fn void QPlaceManager::error(QPlaceReply *reply, QPlaceReply::Error error, const QString &errorString) + + This signal is emitted when an error has been detected in the processing of + \a reply. The QPlaceManager::finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error meant for developers + and not end users. + + This signal and QPlaceReply::error() will be emitted at the same time. + + \note Do not delete the \a reply object in the slot connected to this signal. + Use deleteLater() instead. +*/ + +/*! + \fn void QPlaceManager::placeAdded(const QString &placeId) + + This signal is emitted if a place has been added to the manager engine's datastore. + The particular added place is specified by \a placeId. + + This signal is only emitted by managers that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManager::placeUpdated(const QString &placeId) + + This signal is emitted if a place has been modified in the manager's datastore. + The particular modified place is specified by \a placeId. + + This signal is only emitted by managers that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManager::placeRemoved(const QString &placeId) + + This signal is emitted if a place has been removed from the manager's datastore. + The particular place that has been removed is specified by \a placeId. + + This signal is only emitted by managers that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManager::categoryAdded(const QPlaceCategory &category, const QString &parentId) + + This signal is emitted if a \a category has been added to the manager's datastore. + The parent of the \a category is specified by \a parentId. + + This signal is only emitted by managers that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManager::categoryUpdated(const QPlaceCategory &category, const QString &parentId) + + This signal is emitted if a \a category has been modified in the manager's datastore. + The parent of the modified category is specified by \a parentId. + + This signal is only emitted by managers that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManager::categoryRemoved(const QString &categoryId, const QString &parentId) + + This signal is emitted when the category corresponding to \a categoryId has + been removed from the manager's datastore. The parent of the removed category + is specified by \a parentId. + + This signal is only emitted by managers that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn QPlaceManager::dataChanged() + This signal is emitted by the manager if there are large scale changes to its + underlying datastore and the manager considers these changes radical enough + to require clients to reload all data. + + If the signal is emitted, no other signals will be emitted for the associated changes. + + This signal is only emitted by managers that support the QPlaceManager::NotificationsFeature. +*/ + +QT_END_NAMESPACE diff --git a/src/location/places/qplacemanager.h b/src/location/places/qplacemanager.h new file mode 100644 index 0000000..146bdd0 --- /dev/null +++ b/src/location/places/qplacemanager.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMANAGER_H +#define QPLACEMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManagerEngine; +class QPlaceSearchRequest; +class QPlaceSearchReply; + +class Q_LOCATION_EXPORT QPlaceManager : public QObject +{ + Q_OBJECT +public: + ~QPlaceManager(); + + QString managerName() const; + int managerVersion() const; + + QPlaceDetailsReply *getPlaceDetails(const QString &placeId) const; + + QPlaceContentReply *getPlaceContent(const QPlaceContentRequest &request) const; + + QPlaceSearchReply *search(const QPlaceSearchRequest &query) const; + + QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &request) const; + + QPlaceIdReply *savePlace(const QPlace &place); + QPlaceIdReply *removePlace(const QString &placeId); + + QPlaceIdReply *saveCategory(const QPlaceCategory &category, const QString &parentId = QString()); + QPlaceIdReply *removeCategory(const QString &categoryId); + + QPlaceReply *initializeCategories(); + QString parentCategoryId(const QString &categoryId) const; + QStringList childCategoryIds(const QString &parentId = QString()) const; + + QPlaceCategory category(const QString &categoryId) const; + QList childCategories(const QString &parentId = QString()) const; + + QList locales() const; + void setLocale(const QLocale &locale); + void setLocales(const QList &locale); + + QPlace compatiblePlace(const QPlace &place); + + QPlaceMatchReply *matchingPlaces(const QPlaceMatchRequest &request) const; + +Q_SIGNALS: + void finished(QPlaceReply *reply); + void error(QPlaceReply *, QPlaceReply::Error error, const QString &errorString = QString()); + + void placeAdded(const QString &placeId); + void placeUpdated(const QString &placeId); + void placeRemoved(const QString &placeId); + + void categoryAdded(const QPlaceCategory &category, const QString &parentId); + void categoryUpdated(const QPlaceCategory &category, const QString &parentId); + void categoryRemoved(const QString &categoryId, const QString &parentId); + void dataChanged(); + +private: + explicit QPlaceManager(QPlaceManagerEngine *engine, QObject *parent = nullptr); + Q_DISABLE_COPY(QPlaceManager) + + QPlaceManagerEngine *d; + + friend class QGeoServiceProvider; + friend class QGeoServiceProviderPrivate; + friend class QPlaceIcon; +}; + +QT_END_NAMESPACE + +#endif // QPLACEMANAGER_H diff --git a/src/location/places/qplacemanagerengine.cpp b/src/location/places/qplacemanagerengine.cpp new file mode 100644 index 0000000..d14b7e6 --- /dev/null +++ b/src/location/places/qplacemanagerengine.cpp @@ -0,0 +1,460 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacemanagerengine.h" +#include "qplacemanagerengine_p.h" +#include "unsupportedreplies_p.h" + +#include + +#include "qplaceicon.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QPlaceManagerEngine + \inmodule QtLocation + \ingroup QtLocation-impl + \ingroup QtLocation-places + \ingroup QtLocation-places-manager + \since 5.6 + + \brief The QPlaceManagerEngine class provides an interface for + implementers of QGeoServiceProvider plugins who want to provide access to place + functionality. + + Application developers need not concern themselves with the QPlaceManagerEngine. + Backend implementers however will need to derive from QPlaceManagerEngine and provide + implementations for the abstract virtual functions. + + For more information on writing a backend see the \l {Places Backend} documentation. + + \sa QPlaceManager +*/ + +/*! + Constructs a new engine with the specified \a parent, using \a parameters to pass any + implementation specific data to the engine. +*/ +QPlaceManagerEngine::QPlaceManagerEngine(const QVariantMap ¶meters, + QObject *parent) +: QObject(parent), d_ptr(new QPlaceManagerEnginePrivate) +{ + qRegisterMetaType(); + qRegisterMetaType(); + Q_UNUSED(parameters) +} + +/*! + Destroys this engine. +*/ +QPlaceManagerEngine::~QPlaceManagerEngine() +{ + delete d_ptr; +} + +/*! + \internal + Sets the name which this engine implementation uses to distinguish itself + from the implementations provided by other plugins to \a managerName. + + This function does not need to be called by engine implementers, + it is implicitly called by QGeoServiceProvider to set the manager + name to be the same as the provider's. +*/ +void QPlaceManagerEngine::setManagerName(const QString &managerName) +{ + d_ptr->managerName = managerName; +} + +/*! + Returns the name which this engine implementation uses to distinguish + itself from the implementations provided by other plugins. + + The manager name is automatically set to be the same + as the QGeoServiceProviderFactory::providerName(). +*/ +QString QPlaceManagerEngine::managerName() const +{ + return d_ptr->managerName; +} + +/*! + \internal + Sets the version of this engine implementation to \a managerVersion. + + The combination of managerName() and managerVersion() should be unique + amongst plugin implementations. +*/ +void QPlaceManagerEngine::setManagerVersion(int managerVersion) +{ + d_ptr->managerVersion = managerVersion; +} + +/*! + Returns the version of this engine implementation. + + The manager version is automatically set to be the same + as the QGeoServiceProviderFactory::providerVersion(). +*/ +int QPlaceManagerEngine::managerVersion() const +{ + return d_ptr->managerVersion; +} + +/*! + Retrieves details of place corresponding to the given \a placeId. +*/ +QPlaceDetailsReply *QPlaceManagerEngine::getPlaceDetails(const QString &placeId) +{ + Q_UNUSED(placeId) + + return new QPlaceDetailsReplyUnsupported(this); +} + +/*! + Retrieves content for a place according to the parameters specified in \a request. +*/ +QPlaceContentReply *QPlaceManagerEngine::getPlaceContent(const QPlaceContentRequest &request) +{ + Q_UNUSED(request) + + return new QPlaceContentReplyUnsupported(this); +} + +/*! + Searches for places according to the parameters specified in \a request. +*/ +QPlaceSearchReply *QPlaceManagerEngine::search(const QPlaceSearchRequest &request) +{ + Q_UNUSED(request) + + return new QPlaceSearchReplyUnsupported(QPlaceReply::UnsupportedError, + QStringLiteral("Place search is not supported."), this); +} + +/*! + Requests a set of search term suggestions according to the parameters specified in \a request. +*/ +QPlaceSearchSuggestionReply *QPlaceManagerEngine::searchSuggestions( + const QPlaceSearchRequest &request) +{ + Q_UNUSED(request) + + return new QPlaceSearchSuggestionReplyUnsupported(this); +} + +/*! + Saves a specified \a place to the manager engine's datastore. +*/ +QPlaceIdReply *QPlaceManagerEngine::savePlace(const QPlace &place) +{ + Q_UNUSED(place) + + return new QPlaceIdReplyUnsupported(QStringLiteral("Save place is not supported"), + QPlaceIdReply::SavePlace, this); +} + +/*! + Removes the place corresponding to \a placeId from the manager engine's datastore. +*/ +QPlaceIdReply *QPlaceManagerEngine::removePlace(const QString &placeId) +{ + Q_UNUSED(placeId) + + return new QPlaceIdReplyUnsupported(QStringLiteral("Remove place is not supported"), + QPlaceIdReply::RemovePlace, this); +} + +/*! + Saves a \a category that is a child of the category specified by \a parentId. An empty + \a parentId means \a category is saved as a top level category. +*/ +QPlaceIdReply *QPlaceManagerEngine::saveCategory(const QPlaceCategory &category, + const QString &parentId) +{ + Q_UNUSED(category) + Q_UNUSED(parentId) + + return new QPlaceIdReplyUnsupported(QStringLiteral("Save category is not supported"), + QPlaceIdReply::SaveCategory, this); +} + +/*! + Removes the category corresponding to \a categoryId from the manager engine's datastore. +*/ + +QPlaceIdReply *QPlaceManagerEngine::removeCategory(const QString &categoryId) +{ + Q_UNUSED(categoryId) + + return new QPlaceIdReplyUnsupported(QStringLiteral("Remove category is not supported"), + QPlaceIdReply::RemoveCategory, this); +} + +/*! + Initializes the categories of the manager engine. +*/ +QPlaceReply *QPlaceManagerEngine::initializeCategories() +{ + return new QPlaceReplyUnsupported(QStringLiteral("Categories are not supported."), this); +} + +/*! + Returns the parent category identifier of the category corresponding to \a categoryId. +*/ +QString QPlaceManagerEngine::parentCategoryId(const QString &categoryId) const +{ + Q_UNUSED(categoryId) + + return QString(); +} + +/*! + Returns the child category identifiers of the category corresponding to \a categoryId. If + \a categoryId is empty then all top level category identifiers are returned. +*/ +QStringList QPlaceManagerEngine::childCategoryIds(const QString &categoryId) const +{ + Q_UNUSED(categoryId) + + return QStringList(); +} + +/*! + Returns the category corresponding to the given \a categoryId. +*/ +QPlaceCategory QPlaceManagerEngine::category(const QString &categoryId) const +{ + Q_UNUSED(categoryId) + + return QPlaceCategory(); +} + +/*! + Returns a list of categories that are children of the category corresponding to \a parentId. + If \a parentId is empty, all the top level categories are returned. +*/ +QList QPlaceManagerEngine::childCategories(const QString &parentId) const +{ + Q_UNUSED(parentId) + + return QList(); +} + +/*! + Returns a list of preferred locales. The locales are used as a hint to the manager engine for + what language place and category details should be returned in. + + If the first specified locale cannot be accommodated, the manager engine falls back to the next + and so forth. + + Support for locales may vary from provider to provider. For those that do support it, by + default, the \l {QLocale::setDefault()}{global default locale} will be used. If the manager + engine has no locales assigned to it, it implicitly uses the global default locale. For + engines that do not support locales, the locale list is always empty. +*/ +QList QPlaceManagerEngine::locales() const +{ + return QList(); +} + +/*! + Set the list of preferred \a locales. +*/ +void QPlaceManagerEngine::setLocales(const QList &locales) +{ + Q_UNUSED(locales) +} + +/*! + Returns the manager instance used to create this engine. +*/ +QPlaceManager *QPlaceManagerEngine::manager() const +{ + return d_ptr->manager; +} + +/*! + Returns a pruned or modified version of the \a original place + which is suitable to be saved by the manager engine. + + Only place details that are supported by this manager is + present in the modified version. Manager specific data such + as the place id, is not copied over from the \a original. +*/ +QPlace QPlaceManagerEngine::compatiblePlace(const QPlace &original) const +{ + Q_UNUSED(original); + return QPlace(); +} + +/*! + Returns a reply which contains a list of places which correspond/match those + specified in \a request. +*/ +QPlaceMatchReply * QPlaceManagerEngine::matchingPlaces(const QPlaceMatchRequest &request) +{ + Q_UNUSED(request) + + return new QPlaceMatchReplyUnsupported(this); +} + +/*! + QUrl QPlaceManagerEngine::constructIconUrl(const QPlaceIcon &icon, const QSize &size) + + Constructs an icon url from a given \a icon, \a size. The URL of the icon + image that most closely matches the given parameters is returned. +*/ +QUrl QPlaceManagerEngine::constructIconUrl(const QPlaceIcon &icon, const QSize &size) const +{ + Q_UNUSED(icon); + Q_UNUSED(size); + + return QUrl(); +} + +QPlaceManagerEnginePrivate::QPlaceManagerEnginePrivate() + : managerVersion(-1), manager(0) +{ +} + +QPlaceManagerEnginePrivate::~QPlaceManagerEnginePrivate() +{ +} + +/*! + \fn void QPlaceManagerEngine::finished(QPlaceReply *reply) + + This signal is emitted when \a reply has finished processing. + + If reply->error() equals QPlaceReply::NoError then the processing + finished successfully. + + This signal and QPlaceReply::finished() will be emitted at the same time. + + \note Do not delete the \a reply object in the slot connected to this signal. + Use deleteLater() instead. +*/ + +/*! + \fn void QPlaceManagerEngine::error(QPlaceReply * reply, QPlaceReply::Error error, const QString &errorString = QString()); + + This signal is emitted when an error has been detected in the processing of + \a reply. The QPlaceManager::finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error meant for developers + and not end users. + + This signal and QPlaceReply::error() will be emitted at the same time. + + \note Do not delete the \a reply object in the slot connected to this signal. + Use deleteLater() instead. +*/ + +/*! + \fn void QPlaceManagerEngine::placeAdded(const QString &placeId) + + This signal is emitted if a place has been added to the manager engine's datastore. + The particular added place is specified by \a placeId. + + This signal is only emitted by manager engines that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManagerEngine::placeUpdated(const QString &placeId) + + This signal is emitted if a place has been modified in the manager engine's datastore. + The particular modified place is specified by \a placeId. + + This signal is only emitted by manager engines that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManagerEngine::placeRemoved(const QString &placeId) + + This signal is emitted if a place has been removed from the manager engine's datastore. + The particular place that has been removed is specified by \a placeId. + + This signal is only emitted by manager engines that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManagerEngine::categoryAdded(const QPlaceCategory &category, const QString &parentId) + + This signal is emitted if a \a category has been added to the manager engine's datastore. + The parent of the \a category is specified by \a parentId. + + This signal is only emitted by manager engines that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManagerEngine::categoryUpdated(const QPlaceCategory &category, const QString &parentId) + + This signal is emitted if a \a category has been modified in the manager engine's datastore. + The parent of the modified category is specified by \a parentId. + + This signal is only emitted by manager engines that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + \fn void QPlaceManagerEngine::categoryRemoved(const QString &categoryId, const QString &parentId) + + This signal is emitted when the category corresponding to \a categoryId has + been removed from the manager engine's datastore. The parent of the removed category + is specified by \a parentId. + + This signal is only emitted by manager engines that support the QPlaceManager::NotificationsFeature. + \sa dataChanged() +*/ + +/*! + * \fn QPlaceManagerEngine::dataChanged() + + This signal is emitted by the engine if there are large scale changes to its + underlying datastore and the engine considers these changes radical enough + to require clients to reload all data. + + If the signal is emitted, no other signals will be emitted for the associated changes. +*/ + +QT_END_NAMESPACE diff --git a/src/location/places/qplacemanagerengine.h b/src/location/places/qplacemanagerengine.h new file mode 100644 index 0000000..9528cc2 --- /dev/null +++ b/src/location/places/qplacemanagerengine.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMANAGERENGINE_H +#define QPLACEMANAGERENGINE_H + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManagerEnginePrivate; +class QPlaceMatchReply; +class QPlaceMatchRequest; +class QPlaceSearchReply; +class QPlaceSearchRequest; +class QPlaceSearchSuggestionReply; + +class Q_LOCATION_EXPORT QPlaceManagerEngine : public QObject +{ + Q_OBJECT + +public: + explicit QPlaceManagerEngine(const QVariantMap ¶meters, QObject *parent = nullptr); + virtual ~QPlaceManagerEngine(); + + QString managerName() const; + int managerVersion() const; + + virtual QPlaceDetailsReply *getPlaceDetails(const QString &placeId); + + virtual QPlaceContentReply *getPlaceContent(const QPlaceContentRequest &request); + + virtual QPlaceSearchReply *search(const QPlaceSearchRequest &request); + + virtual QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &request); + + virtual QPlaceIdReply *savePlace(const QPlace &place); + virtual QPlaceIdReply *removePlace(const QString &placeId); + + virtual QPlaceIdReply *saveCategory(const QPlaceCategory &category, const QString &parentId); + virtual QPlaceIdReply *removeCategory(const QString &categoryId); + + virtual QPlaceReply *initializeCategories(); + virtual QString parentCategoryId(const QString &categoryId) const; + virtual QStringList childCategoryIds(const QString &categoryId) const; + virtual QPlaceCategory category(const QString &categoryId) const; + + virtual QList childCategories(const QString &parentId) const; + + virtual QList locales() const; + virtual void setLocales(const QList &locales); + + virtual QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const; + + virtual QPlace compatiblePlace(const QPlace &original) const; + + virtual QPlaceMatchReply *matchingPlaces(const QPlaceMatchRequest &request); + +Q_SIGNALS: + void finished(QPlaceReply *reply); + void error(QPlaceReply *, QPlaceReply::Error error, const QString &errorString = QString()); + + void placeAdded(const QString &placeId); + void placeUpdated(const QString &placeId); + void placeRemoved(const QString &placeId); + + void categoryAdded(const QPlaceCategory &category, const QString &parentCategoryId); + void categoryUpdated(const QPlaceCategory &category, const QString &parentCategoryId); + void categoryRemoved(const QString &categoryId, const QString &parentCategoryId); + void dataChanged(); + +protected: + QPlaceManager *manager() const; + +private: + void setManagerName(const QString &managerName); + void setManagerVersion(int managerVersion); + + QPlaceManagerEnginePrivate *d_ptr; + Q_DISABLE_COPY(QPlaceManagerEngine) + + friend class QGeoServiceProviderPrivate; + friend class QPlaceManager; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacemanagerengine_p.h b/src/location/places/qplacemanagerengine_p.h new file mode 100644 index 0000000..1a1359a --- /dev/null +++ b/src/location/places/qplacemanagerengine_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMANAGERENGINE_P_H +#define QPLACEMANAGERENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManagerEnginePrivate +{ +public: + QPlaceManagerEnginePrivate(); + ~QPlaceManagerEnginePrivate(); + + QString managerName; + int managerVersion; + QPlaceManager *manager; + +private: + Q_DISABLE_COPY(QPlaceManagerEnginePrivate) +}; + +QT_END_NAMESPACE + +#endif // QPLACEMANAGERENGINE_P_H diff --git a/src/location/places/qplacematchreply.cpp b/src/location/places/qplacematchreply.cpp new file mode 100644 index 0000000..9794b3f --- /dev/null +++ b/src/location/places/qplacematchreply.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacematchreply.h" +#include "qplacereply_p.h" + + +QT_BEGIN_NAMESPACE +class QPlaceMatchReplyPrivate : public QPlaceReplyPrivate +{ +public: + QPlaceMatchReplyPrivate(){} + QList places; + QPlaceMatchRequest request; +}; + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +/*! + \class QPlaceMatchReply + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-replies + \since 5.6 + + \brief The QPlaceMatchReply class manages a place matching operation started by an + instance of QPlaceManager. + + If the operation is successful, the number of places in the reply matches those + in the request. If a particular place in the request is not found, a default + constructed place is used as a place holder in the reply. In this way, there + is always a one is to one relationship between input places in the request, + and output places in the reply. + + If the operation is not successful the number of places is always zero. + + See \l {Matching places between managers} for an example on how to use + a match reply. + + \sa QPlaceMatchRequest, QPlaceManager +*/ + +/*! + Constructs a match reply with a given \a parent. +*/ +QPlaceMatchReply::QPlaceMatchReply(QObject *parent) + : QPlaceReply(new QPlaceMatchReplyPrivate, parent) +{ +} + +/*! + Destroys the match reply. +*/ +QPlaceMatchReply::~QPlaceMatchReply() +{ +} + +/*! + Returns the type of reply. +*/ +QPlaceReply::Type QPlaceMatchReply::type() const +{ + return QPlaceReply::MatchReply; +} + + /*! + Returns a list of matching places; +*/ +QList QPlaceMatchReply::places() const +{ + Q_D(const QPlaceMatchReply); + return d->places; +} + +/*! + Sets the list of matching \a places. +*/ +void QPlaceMatchReply::setPlaces(const QList &places) +{ + Q_D(QPlaceMatchReply); + d->places = places; +} + +/*! + Returns the match request that was used to generate this reply. +*/ +QPlaceMatchRequest QPlaceMatchReply::request() const +{ + Q_D(const QPlaceMatchReply); + return d->request; +} + +/*! + Sets the match \a request used to generate this reply. +*/ +void QPlaceMatchReply::setRequest(const QPlaceMatchRequest &request) +{ + Q_D(QPlaceMatchReply); + d->request = request; +} diff --git a/src/location/places/qplacematchreply.h b/src/location/places/qplacematchreply.h new file mode 100644 index 0000000..8e19566 --- /dev/null +++ b/src/location/places/qplacematchreply.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMATCHREPLY_H +#define QPLACEMATCHREPLY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceMatchReplyPrivate; +class Q_LOCATION_EXPORT QPlaceMatchReply : public QPlaceReply +{ + Q_OBJECT +public: + explicit QPlaceMatchReply(QObject *parent = nullptr); + ~QPlaceMatchReply(); + + QPlaceReply::Type type() const; + + QList places() const; + QPlaceMatchRequest request() const; + +protected: + void setPlaces(const QList &results); + void setRequest(const QPlaceMatchRequest &request); +private: + Q_DISABLE_COPY(QPlaceMatchReply) + Q_DECLARE_PRIVATE(QPlaceMatchReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacematchrequest.cpp b/src/location/places/qplacematchrequest.cpp new file mode 100644 index 0000000..89343dc --- /dev/null +++ b/src/location/places/qplacematchrequest.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacematchrequest.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceMatchRequestPrivate : public QSharedData +{ +public: + QPlaceMatchRequestPrivate(); + QPlaceMatchRequestPrivate(const QPlaceMatchRequestPrivate &other); + ~QPlaceMatchRequestPrivate(); + + QPlaceMatchRequestPrivate &operator=(const QPlaceMatchRequestPrivate &other); + bool operator==(const QPlaceMatchRequestPrivate &other) const; + + void clear(); + + QList places; + QVariantMap parameters; +}; + +QPlaceMatchRequestPrivate::QPlaceMatchRequestPrivate() + : QSharedData() +{ +} + +QPlaceMatchRequestPrivate::QPlaceMatchRequestPrivate(const QPlaceMatchRequestPrivate &other) + : QSharedData(other), + places(other.places), + parameters(other.parameters) +{ +} + +QPlaceMatchRequestPrivate::~QPlaceMatchRequestPrivate() +{ +} + +QPlaceMatchRequestPrivate &QPlaceMatchRequestPrivate::operator=(const QPlaceMatchRequestPrivate &other) +{ + if (this != &other) { + places = other.places; + parameters = other.parameters; + } + + return *this; +} + +bool QPlaceMatchRequestPrivate::operator==(const QPlaceMatchRequestPrivate &other) const +{ + return (places == other.places + && parameters == other.parameters); +} + +void QPlaceMatchRequestPrivate::clear() +{ + places.clear(); + parameters.clear(); +} + +/*! + \class QPlaceMatchRequest + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-requests + \since 5.6 + + \brief The QPlaceMatchRequest class is used to find places from one manager that match those from another. It represents + a set of request parameters. + + Places from another manager that may have corresponding/matching places in the current manager are assigned using setPlaces() or setResults(). + A set of further parameters are specified which determines the criteria for matching. + + The typical key for matching is the QPlaceMatchRequest::AlternativeId, the value is an alternative identifier attribute type of the format + x_id_ for example x_id_here. The provider name is name supplied to the QGeoServiceProvider instance. + + See \l {Matching places between managers} for an example on how to use a match request. + + \sa QPlaceMatchReply, QPlaceManager +*/ + +/*! + \variable QPlaceMatchRequest::AlternativeId + The key to specify that matching is to be accomplished via an alternative place identifier. +*/ +const QString QPlaceMatchRequest::AlternativeId(QLatin1String("alternativeId")); + +/*! + Default constructor. Constructs a new request object. +*/ +QPlaceMatchRequest::QPlaceMatchRequest() + : d_ptr(new QPlaceMatchRequestPrivate()) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QPlaceMatchRequest::QPlaceMatchRequest(const QPlaceMatchRequest &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys the request object. +*/ +QPlaceMatchRequest::~QPlaceMatchRequest() +{ +} + +/*! + Assigns \a other to this search request and returns a reference + to this match request. +*/ +QPlaceMatchRequest &QPlaceMatchRequest::operator= (const QPlaceMatchRequest & other) +{ + if (this == &other) + return *this; + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if \a other is equal to this match request, + otherwise returns false. +*/ +bool QPlaceMatchRequest::operator== (const QPlaceMatchRequest &other) const +{ + Q_D(const QPlaceMatchRequest); + return *d == *other.d_func(); +} + +/*! + Returns true if \a other is not equal to this match request, + otherwise returns false. +*/ +bool QPlaceMatchRequest::operator!= (const QPlaceMatchRequest &other) const +{ + Q_D(const QPlaceMatchRequest); + return !(*d == *other.d_func()); +} + + +/*! + Returns a list of places which are to be matched. +*/ +QList QPlaceMatchRequest::places() const +{ + Q_D(const QPlaceMatchRequest); + return d->places; +} + +/*! + Sets a list of \a places which are to be matched. + + \sa setResults() +*/ +void QPlaceMatchRequest::setPlaces(const QList places) +{ + Q_D(QPlaceMatchRequest); + d->places = places; +} + +/*! + Convenience function which uses a set of search \a results to set + the places which should be matched. + + \sa setPlaces() +*/ +void QPlaceMatchRequest::setResults(const QList &results) +{ + Q_D(QPlaceMatchRequest); + QList places; + foreach (const QPlaceSearchResult &result, results) { + if (result.type() == QPlaceSearchResult::PlaceResult) { + QPlaceResult placeResult = result; + places.append(placeResult.place()); + } + } + + d->places = places; +} + +/*! + Returns the parameters for matching places. +*/ +QVariantMap QPlaceMatchRequest::parameters() const +{ + Q_D(const QPlaceMatchRequest); + return d->parameters; +} + +/*! + Sets the \a parameters for matching places. +*/ +void QPlaceMatchRequest::setParameters(const QVariantMap ¶meters) +{ + Q_D(QPlaceMatchRequest); + d->parameters = parameters; +} + +/*! + Clears the match request. +*/ +void QPlaceMatchRequest::clear() +{ + Q_D(QPlaceMatchRequest); + d->clear(); +} + +inline QPlaceMatchRequestPrivate *QPlaceMatchRequest::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QPlaceMatchRequestPrivate *QPlaceMatchRequest::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +QT_END_NAMESPACE diff --git a/src/location/places/qplacematchrequest.h b/src/location/places/qplacematchrequest.h new file mode 100644 index 0000000..24f9d9f --- /dev/null +++ b/src/location/places/qplacematchrequest.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMATCHREQUEST_H +#define QPLACEMATCHREQUEST_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceMatchRequestPrivate; + +class Q_LOCATION_EXPORT QPlaceMatchRequest +{ +public: + static const QString AlternativeId; + + QPlaceMatchRequest(); + QPlaceMatchRequest(const QPlaceMatchRequest &other); + + + QPlaceMatchRequest &operator=(const QPlaceMatchRequest &other); + + bool operator==(const QPlaceMatchRequest &other) const; + bool operator!=(const QPlaceMatchRequest &other) const; + + ~QPlaceMatchRequest(); + + QList places() const; + void setPlaces(const QList places); + + void setResults(const QList &results); + + QVariantMap parameters() const; + void setParameters(const QVariantMap ¶meters); + + void clear(); + +private: + QSharedDataPointer d_ptr; + inline QPlaceMatchRequestPrivate *d_func(); + inline const QPlaceMatchRequestPrivate *d_func() const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplaceproposedsearchresult.cpp b/src/location/places/qplaceproposedsearchresult.cpp new file mode 100644 index 0000000..4acc66c --- /dev/null +++ b/src/location/places/qplaceproposedsearchresult.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Aaron McCarthy +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceproposedsearchresult.h" +#include "qplaceproposedsearchresult_p.h" + +QT_BEGIN_NAMESPACE + +QPlaceProposedSearchResultPrivate::QPlaceProposedSearchResultPrivate() +{ +} + +QPlaceProposedSearchResultPrivate::QPlaceProposedSearchResultPrivate(const QPlaceProposedSearchResultPrivate &other) +: QPlaceSearchResultPrivate(other), searchRequest(other.searchRequest) +{ +} + +QPlaceProposedSearchResultPrivate::~QPlaceProposedSearchResultPrivate() +{ +} + +bool QPlaceProposedSearchResultPrivate::compare(const QPlaceSearchResultPrivate *other) const +{ + const QPlaceProposedSearchResultPrivate *od = static_cast(other); + return QPlaceSearchResultPrivate::compare(other) && searchRequest == od->searchRequest; +} + +/*! + \class QPlaceProposedSearchResult + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since QtLocation 5.2 + + \brief The QPlaceProposedSearchResult class represents a search result containing a proposed search. + + \sa QPlaceSearchResult +*/ + +/*! + Constructs a new proposed search result. +*/ +QPlaceProposedSearchResult::QPlaceProposedSearchResult() +: QPlaceSearchResult(new QPlaceProposedSearchResultPrivate) +{ +} + +/*! + \fn QPlaceProposedSearchResult::QPlaceProposedSearchResult(const QPlaceSearchRequest &other) + + Contructs a copy of \a other if possible, otherwise constructs a default proposed search + result. +*/ +Q_IMPLEMENT_SEARCHRESULT_COPY_CTOR(QPlaceProposedSearchResult) + +Q_IMPLEMENT_SEARCHRESULT_D_FUNC(QPlaceProposedSearchResult) + +/*! + Destroys the proposed search result. +*/ +QPlaceProposedSearchResult::~QPlaceProposedSearchResult() +{ +} + +/*! + Returns a place search request that can be used to perform an additional proposed search. +*/ +QPlaceSearchRequest QPlaceProposedSearchResult::searchRequest() const +{ + Q_D(const QPlaceProposedSearchResult); + return d->searchRequest; +} + +/*! + Sets the proposed search request to \a request. +*/ +void QPlaceProposedSearchResult::setSearchRequest(const QPlaceSearchRequest &request) +{ + Q_D(QPlaceProposedSearchResult); + d->searchRequest = request; +} + +QT_END_NAMESPACE diff --git a/src/location/places/qplaceproposedsearchresult.h b/src/location/places/qplaceproposedsearchresult.h new file mode 100644 index 0000000..d949717 --- /dev/null +++ b/src/location/places/qplaceproposedsearchresult.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Aaron McCarthy +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPROPOSEDSEARCHRESULT_H +#define QPROPOSEDSEARCHRESULT_H + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceProposedSearchResultPrivate; + +class Q_LOCATION_EXPORT QPlaceProposedSearchResult : public QPlaceSearchResult +{ +public: + QPlaceProposedSearchResult(); + +#ifdef Q_QDOC + QPlaceProposedSearchResult(const QPlaceSearchRequest &other); +#else + Q_DECLARE_SEARCHRESULT_COPY_CTOR(QPlaceProposedSearchResult) +#endif + + ~QPlaceProposedSearchResult(); + + QPlaceSearchRequest searchRequest() const; + void setSearchRequest(const QPlaceSearchRequest &request); + +private: + Q_DECLARE_SEARCHRESULT_D_FUNC(QPlaceProposedSearchResult) +}; + +Q_DECLARE_TYPEINFO(QPlaceProposedSearchResult, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif // QPROPOSEDSEARCHRESULT_H diff --git a/src/location/places/qplaceproposedsearchresult_p.h b/src/location/places/qplaceproposedsearchresult_p.h new file mode 100644 index 0000000..b0972be --- /dev/null +++ b/src/location/places/qplaceproposedsearchresult_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Aaron McCarthy +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPROPOSEDSEARCHRESULT_P_H +#define QPROPOSEDSEARCHRESULT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplacesearchresult_p.h" + +QT_BEGIN_NAMESPACE + +class QPlaceProposedSearchResultPrivate : public QPlaceSearchResultPrivate +{ +public: + QPlaceProposedSearchResultPrivate(); + QPlaceProposedSearchResultPrivate(const QPlaceProposedSearchResultPrivate &other); + + ~QPlaceProposedSearchResultPrivate(); + + bool compare(const QPlaceSearchResultPrivate *other) const override; + + Q_DEFINE_SEARCHRESULT_PRIVATE_HELPER(QPlaceProposedSearchResult, QPlaceSearchResult::ProposedSearchResult) + + QPlaceSearchRequest searchRequest; +}; + +QT_END_NAMESPACE + +#endif // QPROPOSEDSEARCHRESULT_P_H diff --git a/src/location/places/qplaceratings.cpp b/src/location/places/qplaceratings.cpp new file mode 100644 index 0000000..391db37 --- /dev/null +++ b/src/location/places/qplaceratings.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceratings.h" +#include "qplaceratings_p.h" + +QT_USE_NAMESPACE + +QPlaceRatingsPrivate::QPlaceRatingsPrivate() + : QSharedData(), average(0), maximum(0), count(0) +{ +} + +QPlaceRatingsPrivate::QPlaceRatingsPrivate(const QPlaceRatingsPrivate &other) +: QSharedData(), average(0), maximum(other.maximum), count(other.count) +{ +} + +QPlaceRatingsPrivate::~QPlaceRatingsPrivate() +{ +} + +bool QPlaceRatingsPrivate::operator==(const QPlaceRatingsPrivate &other) const +{ + return average == other.average && maximum == other.maximum && count == other.count; +} + +bool QPlaceRatingsPrivate::isEmpty() const +{ + return count == 0 && average == 0 && maximum == 0; +} + +/*! + \class QPlaceRatings + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceRatings class holds rating information about a place. + + Rating information is used to describe how good a place is conceived to be. + Typically this information is visualized as a number of stars. + The average() function returns an aggregated ratings value out of a possible + maximum as given by the maximum() function. + + \snippet places/requesthandler.h Ratings +*/ + +/*! + Constructs a new ratings object. +*/ +QPlaceRatings::QPlaceRatings() + : d(new QPlaceRatingsPrivate) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QPlaceRatings::QPlaceRatings(const QPlaceRatings &other) + :d(other.d) +{ +} + +/*! + Destroys the ratings object. +*/ +QPlaceRatings::~QPlaceRatings() +{ +} + +/*! + Assigns \a other to this ratings object and returns + a reference to this ratings object. +*/ +QPlaceRatings &QPlaceRatings::operator=(const QPlaceRatings &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +/*! + Returns true if \a other is equal to this ratings object, + otherwise returns false. +*/ +bool QPlaceRatings::operator==(const QPlaceRatings &other) const +{ + return (*(d.constData()) == *(other.d.constData())); +} + +/*! + \fn bool QPlaceRatings::operator!=(const QPlaceRatings &other) const + + Returns true if \a other is not equal to this ratings object, + otherwise returns false. +*/ + +/*! + Returns the average value of individual ratings. +*/ +qreal QPlaceRatings::average() const +{ + return d->average; +} + +/*! + Sets the \a average value of the ratings. +*/ +void QPlaceRatings::setAverage(qreal average) +{ + d->average = average; +} + +/*! + Returns the maximum possible rating value. +*/ +qreal QPlaceRatings::maximum() const +{ + return d->maximum; +} + +/*! + Sets the maximum possible rating value to \a max. +*/ +void QPlaceRatings::setMaximum(qreal max) +{ + d->maximum = max; +} + +/*! + Returns the total number of individual ratings. +*/ +int QPlaceRatings::count() const +{ + return d->count; +} + +/*! + Sets the total number of individual ratings to \a count. +*/ +void QPlaceRatings::setCount(int count) +{ + d->count = count; +} + +/*! + Returns true if all fields of the place ratings are 0; otherwise returns false. +*/ +bool QPlaceRatings::isEmpty() const +{ + return d->isEmpty(); +} diff --git a/src/location/places/qplaceratings.h b/src/location/places/qplaceratings.h new file mode 100644 index 0000000..3cda5be --- /dev/null +++ b/src/location/places/qplaceratings.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACERATINGS_H +#define QPLACERATINGS_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceRatingsPrivate; + +class Q_LOCATION_EXPORT QPlaceRatings +{ +public: + QPlaceRatings(); + QPlaceRatings(const QPlaceRatings &other); + + ~QPlaceRatings(); + + QPlaceRatings &operator=(const QPlaceRatings &other); + + bool operator==(const QPlaceRatings &other) const; + bool operator!=(const QPlaceRatings &other) const { + return !(other == *this); + } + + qreal average() const; + void setAverage(qreal average); + + int count() const; + void setCount(int count); + + qreal maximum() const; + void setMaximum(qreal max); + + bool isEmpty() const; + +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceRatings) + +#endif diff --git a/src/location/places/qplaceratings_p.h b/src/location/places/qplaceratings_p.h new file mode 100644 index 0000000..21d441a --- /dev/null +++ b/src/location/places/qplaceratings_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACERATINGS_P_H +#define QPLACERATINGS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceRatingsPrivate : public QSharedData +{ +public: + QPlaceRatingsPrivate(); + QPlaceRatingsPrivate(const QPlaceRatingsPrivate &other); + + ~QPlaceRatingsPrivate(); + + bool operator==(const QPlaceRatingsPrivate &other) const; + + bool isEmpty() const; + + qreal average; + qreal maximum; + int count; +}; + +QT_END_NAMESPACE + +#endif // QPLACERATING_P_H diff --git a/src/location/places/qplacereply.cpp b/src/location/places/qplacereply.cpp new file mode 100644 index 0000000..55e67e4 --- /dev/null +++ b/src/location/places/qplacereply.cpp @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacereply.h" +#include "qplacereply_p.h" + +QT_USE_NAMESPACE + +/*! + \class QPlaceReply + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-replies + \since 5.6 + + \brief The QPlaceReply class manages an operation started by an instance of QPlaceManager and + serves as a base class for more specialized replies. + + The QPlaceReply and each of its specialized subclasses manage the + state and results of their corresponding operations. The QPlaceReply itself is used + for operations that have no results, that is, it only necessary to know if the operation + succeeded or failed. + + The finished() signal can be used to monitor the progress of an operation. + Once an operation is complete, the error() and errorString() methods provide information + on whether the operation completed successfully. If successful, the reply + will contain the results for that operation, that is, each subclass will have appropriate + functions to retrieve the results of an operation. + + \sa QPlaceManager +*/ + +/*! + \enum QPlaceReply::Error + + Describes an error which occurred during an operation. + \value NoError + No error has occurred + \value PlaceDoesNotExistError + A specified place could not be found + \value CategoryDoesNotExistError + A specified category could not be found + \value CommunicationError + An error occurred communicating with the service provider. + \value ParseError + The response from the service provider or an import file was in an unrecognizable format + \value PermissionsError + The operation failed because of insufficient permissions. + \value UnsupportedError + The operation was not supported by the service provider. + \value BadArgumentError. + A parameter that was provided was invalid. + \value CancelError + The operation was canceled. + \value UnknownError + An error occurred which does not fit into any of the other categories. +*/ + +/*! + \enum QPlaceReply::Type + + Describes the reply's type. + \value Reply + This is a generic reply. + \value DetailsReply + This is a reply for the retrieval of place details + \value SearchReply + This is a reply for the place search operation. + \value SearchSuggestionReply + This is a reply for a search suggestion operation. + \value ContentReply + This is a reply for content associated with a place. + \value IdReply + This is a reply that returns an identifier of a place or category. + Typically used for place or category save and remove operations. + \value MatchReply + This is a reply that returns places that match + those from another provider. +*/ + +/*! + Constructs a reply object with a given \a parent. +*/ +QPlaceReply::QPlaceReply(QObject *parent) + : QObject(parent),d_ptr(new QPlaceReplyPrivate) +{ +} + +/*! + \internal +*/ +QPlaceReply::QPlaceReply(QPlaceReplyPrivate *dd, QObject *parent) + : QObject(parent),d_ptr(dd) +{ +} + +/*! + Destroys the reply object. +*/ +QPlaceReply::~QPlaceReply() +{ + if (!isFinished()) { + abort(); + } + delete d_ptr; +} + +/*! + Return true if the reply has completed. +*/ +bool QPlaceReply::isFinished() const +{ + return d_ptr->isFinished; +} + +/*! + Returns the type of the reply. +*/ +QPlaceReply::Type QPlaceReply::type() const +{ + return QPlaceReply::Reply; +} + +/*! + Sets the status of whether the reply is \a finished + or not. This function does not cause the finished() signal + to be emitted. +*/ +void QPlaceReply::setFinished(bool finished) +{ + d_ptr->isFinished = finished; +} + +/*! + Sets the \a error and \a errorString of the reply. + This function does not cause the QPlaceReply::error(QPlaceReply::Error, const QString &errorString) + signal to be emitted. +*/ +void QPlaceReply::setError(QPlaceReply::Error error, const QString &errorString) +{ + d_ptr->error = error; + d_ptr->errorString = errorString; +} + +/*! + Returns the error string of the reply. The error string is intended to be + used by developers only and is not fit to be displayed to an end user. + + If no error has occurred, the string is empty. +*/ +QString QPlaceReply::errorString() const +{ + return d_ptr->errorString; +} + +/*! + Returns the error code. +*/ +QPlaceReply::Error QPlaceReply::error() const +{ + return d_ptr->error; +} + +/*! + \fn void QPlaceReply::aborted() + \since 5.9 + + This signal is emitted when the operation has been cancelled. + + \sa abort() +*/ + +/*! + Cancels the operation immediately. + + \sa aborted() +*/ +void QPlaceReply::abort() +{ + emit aborted(); +} + +/*! + \fn void QPlaceReply::finished() + + This signal is emitted when this reply has finished processing. + + If error() equals QPlaceReply::NoError then the processing + finished successfully. + + This signal and QPlaceManager::finished() will be + emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ + +/*! + \fn void QPlaceReply::error(QPlaceReply::Error error, const QString &errorString) + + This signal is emitted when an error has been detected in the processing of + this reply. The finished() signal will probably follow. + + The error will be described by the error code \a error. If \a errorString is + not empty it will contain a textual description of the error meant for + developers and not end users. + + This signal and QPlaceManager::error() will be emitted at the same time. + + \note Do not delete this reply object in the slot connected to this + signal. Use deleteLater() instead. +*/ diff --git a/src/location/places/qplacereply.h b/src/location/places/qplacereply.h new file mode 100644 index 0000000..8fd3464 --- /dev/null +++ b/src/location/places/qplacereply.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEREPLY_H +#define QPLACEREPLY_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceReplyPrivate; +class Q_LOCATION_EXPORT QPlaceReply : public QObject +{ + Q_OBJECT +public: + enum Error { + NoError, + PlaceDoesNotExistError, + CategoryDoesNotExistError, + CommunicationError, + ParseError, + PermissionsError, + UnsupportedError, + BadArgumentError, + CancelError, + UnknownError + }; + + enum Type { + Reply, + DetailsReply, + SearchReply, + SearchSuggestionReply, + ContentReply, + IdReply, + MatchReply + }; + + explicit QPlaceReply(QObject *parent = nullptr); + ~QPlaceReply(); + + bool isFinished() const; + + virtual Type type() const; + + QString errorString() const; + QPlaceReply::Error error() const; + +public Q_SLOTS: + virtual void abort(); + +Q_SIGNALS: + void finished(); + void aborted(); + void error(QPlaceReply::Error error, const QString &errorString = QString()); + +protected: + explicit QPlaceReply(QPlaceReplyPrivate *, QObject *parent = nullptr); + void setFinished(bool finished); + void setError(QPlaceReply::Error error, const QString &errorString); + QPlaceReplyPrivate *d_ptr; + +private: + Q_DISABLE_COPY(QPlaceReply) +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceReply::Error) +Q_DECLARE_METATYPE(QPlaceReply *) + +#endif // QPLACEREPLY_H diff --git a/src/location/places/qplacereply_p.h b/src/location/places/qplacereply_p.h new file mode 100644 index 0000000..9e6c096 --- /dev/null +++ b/src/location/places/qplacereply_p.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEREPLY_P_H +#define QPLACEREPLY_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplacereply.h" + +QT_BEGIN_NAMESPACE + +class QPlaceReplyPrivate +{ +public: + QPlaceReplyPrivate() : + isFinished(false), + error(QPlaceReply::NoError), + errorString(QString()){} + virtual ~QPlaceReplyPrivate(){} + bool isFinished; + QPlaceReply::Error error; + QString errorString; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplaceresult.cpp b/src/location/places/qplaceresult.cpp new file mode 100644 index 0000000..fe89eff --- /dev/null +++ b/src/location/places/qplaceresult.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceresult.h" +#include "qplaceresult_p.h" +#include + +QT_USE_NAMESPACE + +QPlaceResultPrivate::QPlaceResultPrivate() + : QPlaceSearchResultPrivate(), distance(qQNaN()), sponsored(false) +{ +} + +QPlaceResultPrivate::QPlaceResultPrivate(const QPlaceResultPrivate &other) +: QPlaceSearchResultPrivate(other), distance(other.distance), place(other.place), + sponsored(other.sponsored) +{ +} + +QPlaceResultPrivate::~QPlaceResultPrivate() +{ +} + +bool QPlaceResultPrivate::compare(const QPlaceSearchResultPrivate *other) const +{ + const QPlaceResultPrivate *od = static_cast(other); + return QPlaceSearchResultPrivate::compare(other) + && ((qIsNaN(distance) && qIsNaN(od->distance)) + || qFuzzyCompare(distance, od->distance)) + && place == od->place + && sponsored == od->sponsored; +} + +/*! + \class QPlaceResult + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceResult class represents a search result containing a place. + + The PlaceResult holds the distance to the place from the center of the search request, + an instance of the place and an indication of whether the result is + sponsored or \l {http://en.wikipedia.org/wiki/Organic_search}{organic}. + + The intended usage is that a QPlaceSearchResult can be converted into a QPlaceResult + like so: + + \snippet places/requesthandler.h Convert search result + + The implementation is handled in such a way that object slicing is not an issue. + + \sa QPlaceSearchResult +*/ + +/*! + Constructs a new place result object. +*/ +QPlaceResult::QPlaceResult() +: QPlaceSearchResult(new QPlaceResultPrivate) +{ +} + +/*! + Destructor. +*/ +QPlaceResult::~QPlaceResult() +{ +} + +/*! + \fn QPlaceResult::QPlaceResult(const QPlaceSearchResult &other) + Constructs a copy of \a other if possible, otherwise constructs a default place result. +*/ +Q_IMPLEMENT_SEARCHRESULT_COPY_CTOR(QPlaceResult) + +Q_IMPLEMENT_SEARCHRESULT_D_FUNC(QPlaceResult) + +/*! + Returns the distance of the place to the search center. This + field is only relevant provided the search request contained + a search area with a search center. Otherwise, + the distance is NaN indicating an undefined distance. The default value + for distance is NaN. +*/ +qreal QPlaceResult::distance() const +{ + Q_D(const QPlaceResult); + return d->distance; +} + +/*! + Set the \a distance of the search result's place from a search center. +*/ +void QPlaceResult::setDistance(qreal distance) +{ + Q_D(QPlaceResult); + d->distance = distance; +} + +/*! + Returns the place of the search result. +*/ +QPlace QPlaceResult::place() const +{ + Q_D(const QPlaceResult); + return d->place; +} + +/*! + Sets the \a place that this result refers to. +*/ +void QPlaceResult::setPlace(const QPlace &place) +{ + Q_D(QPlaceResult); + d->place = place; +} + +/*! + Returns true if the result is a sponsored result. + + \sa setSponsored() +*/ +bool QPlaceResult::isSponsored() const +{ + Q_D(const QPlaceResult); + return d->sponsored; +} + +/*! + Sets whether the result is a \a sponsored result or not. + + \sa isSponsored() +*/ +void QPlaceResult::setSponsored(bool sponsored) +{ + Q_D(QPlaceResult); + d->sponsored = sponsored; +} diff --git a/src/location/places/qplaceresult.h b/src/location/places/qplaceresult.h new file mode 100644 index 0000000..9d11724 --- /dev/null +++ b/src/location/places/qplaceresult.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACERESULT_H +#define QPLACERESULT_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceResultPrivate; + +class Q_LOCATION_EXPORT QPlaceResult : public QPlaceSearchResult +{ +public: + QPlaceResult(); + +#ifdef Q_QDOC + QPlaceResult::QPlaceResult(const QPlaceSearchResult &other); +#else + Q_DECLARE_SEARCHRESULT_COPY_CTOR(QPlaceResult) +#endif + + virtual ~QPlaceResult(); + + qreal distance() const; + void setDistance(qreal distance); + + QPlace place() const; + void setPlace(const QPlace &place); + + bool isSponsored() const; + void setSponsored(bool sponsored); + +private: + Q_DECLARE_SEARCHRESULT_D_FUNC(QPlaceResult) +}; + +Q_DECLARE_TYPEINFO(QPlaceResult, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplaceresult_p.h b/src/location/places/qplaceresult_p.h new file mode 100644 index 0000000..b7f4065 --- /dev/null +++ b/src/location/places/qplaceresult_p.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACERESULT_P_H +#define QPLACERESULT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplacesearchresult_p.h" + +QT_BEGIN_NAMESPACE + +class QPlaceResultPrivate : public QPlaceSearchResultPrivate +{ +public: + QPlaceResultPrivate(); + QPlaceResultPrivate(const QPlaceResultPrivate &other); + + ~QPlaceResultPrivate(); + + bool compare(const QPlaceSearchResultPrivate *other) const override; + + Q_DEFINE_SEARCHRESULT_PRIVATE_HELPER(QPlaceResult, QPlaceSearchResult::PlaceResult) + + qreal distance; + QPlace place; + bool sponsored; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacereview.cpp b/src/location/places/qplacereview.cpp new file mode 100644 index 0000000..ca79b07 --- /dev/null +++ b/src/location/places/qplacereview.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacereview.h" +#include "qplacereview_p.h" + +QT_BEGIN_NAMESPACE + +QPlaceReviewPrivate::QPlaceReviewPrivate() +: QPlaceContentPrivate(), rating(0) +{ +} + +QPlaceReviewPrivate::QPlaceReviewPrivate(const QPlaceReviewPrivate &other) + : QPlaceContentPrivate(other) +{ + dateTime = other.dateTime; + text = other.text; + language = other.language; + rating = other.rating; + reviewId = other.reviewId; + title = other.title; +} + +QPlaceReviewPrivate::~QPlaceReviewPrivate() +{ +} + +bool QPlaceReviewPrivate::compare(const QPlaceContentPrivate *other) const +{ + const QPlaceReviewPrivate *od = static_cast(other); + return QPlaceContentPrivate::compare(other) && + dateTime == od->dateTime && + text == od->text && + language == od->language && + rating == od->rating && + reviewId == od->reviewId && + title == od->title; +} + +/*! + \class QPlaceReview + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceReview class represents a review of a place. + + Each QPlaceReview has a number of properties such as + a title, text, date of submission and rating; in addition to those properties + inherited from QPlaceContent. + + Note: The Places API only supports reviews as 'retrieve-only' objects. Submitting reviews + to a provider is not a supported use case. + + \sa QPlaceContent, QPlaceEditorial +*/ + +/*! + Constructs a new review object. +*/ +QPlaceReview::QPlaceReview() + : QPlaceContent(new QPlaceReviewPrivate) +{ +} + +/*! + \fn QPlaceReview::QPlaceReview(const QPlaceContent &other) + Constructs a copy of \a other, otherwise constructs a default review object. +*/ +Q_IMPLEMENT_CONTENT_COPY_CTOR(QPlaceReview) + + +/*! + Destroys the review. +*/ +QPlaceReview::~QPlaceReview() +{ +} + +Q_IMPLEMENT_CONTENT_D_FUNC(QPlaceReview) + +/*! + Returns the date and time that the review was submitted. +*/ +QDateTime QPlaceReview::dateTime() const +{ + Q_D(const QPlaceReview); + return d->dateTime; +} + +/*! + Sets the date and time that the review was submitted to \a dateTime. +*/ +void QPlaceReview::setDateTime(const QDateTime &dateTime) +{ + Q_D(QPlaceReview); + d->dateTime = dateTime; +} + +/*! + Returns a textual description of the place. + + Depending on the provider the text could be rich (HTML based) or plain text. +*/ +QString QPlaceReview::text() const +{ + Q_D(const QPlaceReview); + return d->text; +} + +/*! + Sets \a text of the review. +*/ +void QPlaceReview::setText(const QString &text) +{ + Q_D(QPlaceReview); + d->text = text; +} + +/*! + Returns the language of the review. Typically this would be a language code + in the 2 letter ISO 639-1 format. +*/ +QString QPlaceReview::language() const +{ + Q_D(const QPlaceReview); + return d->language; +} + +/*! + Sets the \a language of the review. Typically this would be a language code + in the 2 letter ISO 639-1 format. +*/ +void QPlaceReview::setLanguage(const QString &language) +{ + Q_D(QPlaceReview); + d->language = language; +} + +/*! + Returns this review's rating of the place. +*/ +qreal QPlaceReview::rating() const +{ + Q_D(const QPlaceReview); + return d->rating; +} + +/*! + Sets the review's \a rating of the place. +*/ +void QPlaceReview::setRating(qreal rating) +{ + Q_D(QPlaceReview); + d->rating = rating; +} + +/*! + Returns the review's identifier. +*/ +QString QPlaceReview::reviewId() const +{ + Q_D(const QPlaceReview); + return d->reviewId; +} + +/*! + Sets the \a identifier of the review. +*/ +void QPlaceReview::setReviewId(const QString &identifier) +{ + Q_D(QPlaceReview); + d->reviewId = identifier; +} + +/*! + Returns the title of the review. +*/ +QString QPlaceReview::title() const +{ + Q_D(const QPlaceReview); + return d->title; +} + +/*! + Sets the \a title of the review. +*/ +void QPlaceReview::setTitle(const QString &title) +{ + Q_D(QPlaceReview); + d->title = title; +} + +QT_END_NAMESPACE diff --git a/src/location/places/qplacereview.h b/src/location/places/qplacereview.h new file mode 100644 index 0000000..2c9791c --- /dev/null +++ b/src/location/places/qplacereview.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEREVIEW_H +#define QPLACEREVIEW_H + +#include + +QT_BEGIN_NAMESPACE + +class QDateTime; +class QPlaceReviewPrivate; + +class Q_LOCATION_EXPORT QPlaceReview : public QPlaceContent +{ +public: + QPlaceReview(); +#ifdef Q_QDOC + QPlaceReview(const QPlaceContent &other); +#else + Q_DECLARE_CONTENT_COPY_CTOR(QPlaceReview) +#endif + virtual ~QPlaceReview(); + + QDateTime dateTime() const; + void setDateTime(const QDateTime &dt); + QString text() const; + void setText(const QString &text); + QString language() const; + void setLanguage(const QString &data); + + qreal rating() const; + void setRating(qreal data); + QString reviewId() const; + void setReviewId(const QString &identifier); + QString title() const; + void setTitle(const QString &data); + +private: + Q_DECLARE_CONTENT_D_FUNC(QPlaceReview) +}; + +QT_END_NAMESPACE + +#endif // QPLACEREVIEW_H diff --git a/src/location/places/qplacereview_p.h b/src/location/places/qplacereview_p.h new file mode 100644 index 0000000..345aa19 --- /dev/null +++ b/src/location/places/qplacereview_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEREVIEW_P_H +#define QPLACEREVIEW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +#include "qplacecontent_p.h" + +QT_BEGIN_NAMESPACE + +class QPlaceReviewPrivate : public QPlaceContentPrivate +{ +public: + QPlaceReviewPrivate(); + QPlaceReviewPrivate(const QPlaceReviewPrivate &other); + + ~QPlaceReviewPrivate(); + + bool compare(const QPlaceContentPrivate *other) const; + + Q_DEFINE_CONTENT_PRIVATE_HELPER(QPlaceReview, QPlaceContent::ReviewType); + + QDateTime dateTime; + QString text; + QString language; + qreal rating; + QString reviewId; + QString title; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacesearchreply.cpp b/src/location/places/qplacesearchreply.cpp new file mode 100644 index 0000000..06eef4e --- /dev/null +++ b/src/location/places/qplacesearchreply.cpp @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceSearchReplyPrivate : public QPlaceReplyPrivate +{ +public: + QPlaceSearchReplyPrivate(){} + QList results; + QPlaceSearchRequest searchRequest; + QPlaceSearchRequest previousPageRequest; + QPlaceSearchRequest nextPageRequest; +}; + +/*! + \class QPlaceSearchReply + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-replies + \since 5.6 + + \brief The QPlaceSearchReply class manages a place search operation started by an + instance of QPlaceManager. + + See \l {Discovery/Search} for an example on how to use a search reply. + \sa QPlaceSearchRequest, QPlaceManager +*/ + +/*! + Constructs a search reply with a given \a parent. +*/ +QPlaceSearchReply::QPlaceSearchReply(QObject *parent) + : QPlaceReply(new QPlaceSearchReplyPrivate, parent) +{ +} + +/*! + Destroys the search reply. +*/ +QPlaceSearchReply::~QPlaceSearchReply() +{ +} + +/*! + Returns the type of reply. +*/ +QPlaceReply::Type QPlaceSearchReply::type() const +{ + return QPlaceReply::SearchReply; +} + + /*! + Returns a list of search results; +*/ +QList QPlaceSearchReply::results() const +{ + Q_D(const QPlaceSearchReply); + return d->results; +} + +/*! + Sets the list of search \a results. +*/ +void QPlaceSearchReply::setResults(const QList &results) +{ + Q_D(QPlaceSearchReply); + d->results = results; +} + +/*! + Returns the search request that was used to generate this reply. +*/ +QPlaceSearchRequest QPlaceSearchReply::request() const +{ + Q_D(const QPlaceSearchReply); + return d->searchRequest; +} + +/*! + Returns a place search request which can be used to request the previous page of search + results. An empty place search request is returned if there is no previous page of results. + + \sa nextPageRequest(), setPreviousPageRequest() +*/ +QPlaceSearchRequest QPlaceSearchReply::previousPageRequest() const +{ + Q_D(const QPlaceSearchReply); + return d->previousPageRequest; +} + +/*! + Returns a place search request which can be used to request the next page of search results. An + empty place search request is returned if there is no next page of results. + + \sa previousPageRequest(), setNextPageRequest() +*/ +QPlaceSearchRequest QPlaceSearchReply::nextPageRequest() const +{ + Q_D(const QPlaceSearchReply); + return d->nextPageRequest; +} + +/*! + Sets the search \a request used to generate this reply. +*/ +void QPlaceSearchReply::setRequest(const QPlaceSearchRequest &request) +{ + Q_D(QPlaceSearchReply); + d->searchRequest = request; +} + +/*! + Sets the previous page of search results request to \a previous. + + \sa previousPageRequest() +*/ +void QPlaceSearchReply::setPreviousPageRequest(const QPlaceSearchRequest &previous) +{ + Q_D(QPlaceSearchReply); + d->previousPageRequest = previous; +} + +/*! + Sets the next page of search results request to \a next. + + \sa nextPageRequest() +*/ +void QPlaceSearchReply::setNextPageRequest(const QPlaceSearchRequest &next) +{ + Q_D(QPlaceSearchReply); + d->nextPageRequest = next; +} + +QT_END_NAMESPACE diff --git a/src/location/places/qplacesearchreply.h b/src/location/places/qplacesearchreply.h new file mode 100644 index 0000000..3562aed --- /dev/null +++ b/src/location/places/qplacesearchreply.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHREPLY_H +#define QPLACESEARCHREPLY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceSearchResult; +class QPlaceSearchReplyPrivate; + +class Q_LOCATION_EXPORT QPlaceSearchReply : public QPlaceReply +{ + Q_OBJECT +public: + explicit QPlaceSearchReply(QObject *parent = nullptr); + ~QPlaceSearchReply(); + + QPlaceReply::Type type() const; + + QList results() const; + QPlaceSearchRequest request() const; + + QPlaceSearchRequest previousPageRequest() const; + QPlaceSearchRequest nextPageRequest() const; + +protected: + void setResults(const QList &results); + void setRequest(const QPlaceSearchRequest &request); + void setPreviousPageRequest(const QPlaceSearchRequest &previous); + void setNextPageRequest(const QPlaceSearchRequest &next); + +private: + Q_DISABLE_COPY(QPlaceSearchReply) + Q_DECLARE_PRIVATE(QPlaceSearchReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacesearchrequest.cpp b/src/location/places/qplacesearchrequest.cpp new file mode 100644 index 0000000..c2d993e --- /dev/null +++ b/src/location/places/qplacesearchrequest.cpp @@ -0,0 +1,440 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchrequest.h" +#include "qgeocoordinate.h" +#include "qgeoshape.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceSearchRequestPrivate : public QSharedData +{ +public: + QPlaceSearchRequestPrivate(); + QPlaceSearchRequestPrivate(const QPlaceSearchRequestPrivate &other); + ~QPlaceSearchRequestPrivate(); + + QPlaceSearchRequestPrivate &operator=(const QPlaceSearchRequestPrivate &other); + bool operator==(const QPlaceSearchRequestPrivate &other) const; + + void clear(); + + QString searchTerm; + QList categories; + QGeoShape searchArea; + QString recommendationId; + QLocation::VisibilityScope visibilityScope; + QPlaceSearchRequest::RelevanceHint relevanceHint; + int limit; + QVariant searchContext; +}; + +QPlaceSearchRequestPrivate::QPlaceSearchRequestPrivate() +: QSharedData(), + visibilityScope(QLocation::UnspecifiedVisibility), + relevanceHint(QPlaceSearchRequest::UnspecifiedHint), + limit(-1) +{ +} + +QPlaceSearchRequestPrivate::QPlaceSearchRequestPrivate(const QPlaceSearchRequestPrivate &other) + : QSharedData(other), + searchTerm(other.searchTerm), + categories(other.categories), + searchArea(other.searchArea), + recommendationId(other.recommendationId), + visibilityScope(other.visibilityScope), + relevanceHint(other.relevanceHint), + limit(other.limit), + searchContext(other.searchContext) +{ +} + +QPlaceSearchRequestPrivate::~QPlaceSearchRequestPrivate() +{ +} + +QPlaceSearchRequestPrivate &QPlaceSearchRequestPrivate::operator=(const QPlaceSearchRequestPrivate &other) +{ + if (this != &other) { + searchTerm = other.searchTerm; + categories = other.categories; + searchArea = other.searchArea; + recommendationId = other.recommendationId; + visibilityScope = other.visibilityScope; + relevanceHint = other.relevanceHint; + limit = other.limit; + searchContext = other.searchContext; + } + + return *this; +} + +bool QPlaceSearchRequestPrivate::operator==(const QPlaceSearchRequestPrivate &other) const +{ + return searchTerm == other.searchTerm && + categories == other.categories && + searchArea == other.searchArea && + recommendationId == other.recommendationId && + visibilityScope == other.visibilityScope && + relevanceHint == other.relevanceHint && + limit == other.limit && + searchContext == other.searchContext; +} + +void QPlaceSearchRequestPrivate::clear() +{ + limit = -1; + searchTerm.clear(); + categories.clear(); + searchArea = QGeoShape(); + recommendationId.clear(); + visibilityScope = QLocation::UnspecifiedVisibility; + relevanceHint = QPlaceSearchRequest::UnspecifiedHint; + searchContext.clear(); +} + +/*! + \class QPlaceSearchRequest + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-requests + \since 5.6 + + \brief The QPlaceSearchRequest class represents the set of parameters for a search request. + + A typical search request may look like the following: + \snippet places/requesthandler.h Search request + + Note that specifying a search center can be done by setting a circular search area that has + a center but no radius. The default radius is set to -1, which indicates an undefined radius. The provider will + interpret this as being free to choose its own default radius. + + The QPlaceSearchRequest is primarily used with the QPlaceManager to + \l {QPlaceManager::search()} {search for places}, however it is also + used to provide parameters for \l {QPlaceManager::searchSuggestions()}{generating search term suggestions}. + Note that in this context only some of the parameters may be relevant. For example, the search area + is useful in narrowing down relevant search suggestions, while other parameters such as relevance hint + are not so applicable. + + Also be aware that providers may vary by which parameters they support for example some providers may not support + paging while others do, some providers may honor relevance hints while others may completely ignore them, + see the \l {Qt Location#Plugin References and Parameters}{plugin documentation} for more + details. +*/ + +/*! + \enum QPlaceSearchRequest::RelevanceHint + + Defines hints to help rank place results. + \value UnspecifiedHint + No explicit hint has been specified. + \value DistanceHint + Distance to a search center is relevant for the user. Closer places + are more highly weighted. This hint is only useful + if a circular search area is used in the query. + \value LexicalPlaceNameHint + Alphabetic ordering of places according to name is relevant to the user. +*/ + +/*! + Default constructor. Constructs an new request object. +*/ +QPlaceSearchRequest::QPlaceSearchRequest() + : d_ptr(new QPlaceSearchRequestPrivate()) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QPlaceSearchRequest::QPlaceSearchRequest(const QPlaceSearchRequest &other) + : d_ptr(other.d_ptr) +{ +} + +/*! + Destroys the request object. +*/ +QPlaceSearchRequest::~QPlaceSearchRequest() +{ +} + +/*! + Assigns \a other to this search request and returns a reference + to this search request. +*/ +QPlaceSearchRequest &QPlaceSearchRequest::operator= (const QPlaceSearchRequest & other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if \a other is equal to this search request, + otherwise returns false. +*/ +bool QPlaceSearchRequest::operator== (const QPlaceSearchRequest &other) const +{ + Q_D(const QPlaceSearchRequest); + return *d == *other.d_func(); +} + +/*! + Returns true if \a other is not equal to this search request, + otherwise returns false. +*/ +bool QPlaceSearchRequest::operator!= (const QPlaceSearchRequest &other) const +{ + Q_D(const QPlaceSearchRequest); + return !(*d == *other.d_func()); +} + +/*! + Returns the search term. +*/ +QString QPlaceSearchRequest::searchTerm() const +{ + Q_D(const QPlaceSearchRequest); + return d->searchTerm; +} + +/*! + Sets the search \a term. +*/ +void QPlaceSearchRequest::setSearchTerm(const QString &term) +{ + Q_D(QPlaceSearchRequest); + d->searchTerm = term; +} + +/*! + Return the categories to be used in the search request. + Places need only to belong to one of the categories + to be considered a match by the request. +*/ +QList QPlaceSearchRequest::categories() const +{ + Q_D(const QPlaceSearchRequest); + return d->categories; +} + +/*! + Sets the search request to search by a single \a category + + \sa setCategories() +*/ +void QPlaceSearchRequest::setCategory(const QPlaceCategory &category) +{ + Q_D(QPlaceSearchRequest); + d->categories.clear(); + + if (!category.categoryId().isEmpty()) + d->categories.append(category); +} + +/*! + Sets the search request to search from the list of given \a categories. + Any places returned during the search will match at least one of the \a + categories. + + \sa setCategory() +*/ +void QPlaceSearchRequest::setCategories(const QList &categories) +{ + Q_D(QPlaceSearchRequest); + d->categories = categories; +} + +/*! + Returns the search area which will be used to limit search results. The default search area is + an invalid QGeoShape, indicating that no specific search area is defined. +*/ +QGeoShape QPlaceSearchRequest::searchArea() const +{ + Q_D(const QPlaceSearchRequest); + return d->searchArea; +} + +/*! + Sets the search request to search within the given \a area. +*/ +void QPlaceSearchRequest::setSearchArea(const QGeoShape &area) +{ + Q_D(QPlaceSearchRequest); + d->searchArea = area; +} + +/*! + Returns the place id which will be used to search for recommendations + for similar places. +*/ +QString QPlaceSearchRequest::recommendationId() const +{ + Q_D(const QPlaceSearchRequest); + return d->recommendationId; +} + +/*! + Sets the \a placeId which will be used to search for recommendations. +*/ +void QPlaceSearchRequest::setRecommendationId(const QString &placeId) +{ + Q_D(QPlaceSearchRequest); + d->recommendationId = placeId; +} + +/*! + Returns backend specific additional search context associated with this place search request. + The search context is typically set as part of a + \l {QPlaceSearchResult::ProposedSearchResult}{proposed search results}. +*/ +QVariant QPlaceSearchRequest::searchContext() const +{ + Q_D(const QPlaceSearchRequest); + return d->searchContext; +} + +/*! + Sets the search context to \a context. + + \note This method is intended to be used by geo service plugins when returning search results + of type \l QPlaceSearchResult::ProposedSearchResult. + + The search context is used by backends to store additional search context related to the search + request. Other relevant fields should also be filled in. For example, if the search context + encodes a text search the search term should also be set with \l setSearchTerm(). The search + context allows additional search context to be kept which is not directly accessible via the + Qt Location API. + + The search context can be of any type storable in a QVariant. The value of the search context + is not intended to be use directly by applications. +*/ +void QPlaceSearchRequest::setSearchContext(const QVariant &context) +{ + Q_D(QPlaceSearchRequest); + d->searchContext = context; +} + +/*! + Returns the visibility scope used when searching for places. The default value is + QLocation::UnspecifiedVisibility meaning that no explicit scope has been assigned. + Places of any scope may be returned during the search. +*/ +QLocation::VisibilityScope QPlaceSearchRequest::visibilityScope() const +{ + Q_D(const QPlaceSearchRequest); + return d->visibilityScope; +} + +/*! + Sets the visibility \a scope used when searching for places. +*/ +void QPlaceSearchRequest::setVisibilityScope(QLocation::VisibilityScope scope) +{ + Q_D(QPlaceSearchRequest); + d->visibilityScope = scope; +} + +/*! + Returns the relevance hint of the request. The hint is given to the provider + to help but not dictate the ranking of results. For example providing a distance hint + may give closer places a higher ranking but it doesn't necessarily mean + that he results will be ordered strictly according to distance. +*/ +QPlaceSearchRequest::RelevanceHint QPlaceSearchRequest::relevanceHint() const +{ + Q_D(const QPlaceSearchRequest); + return d->relevanceHint; +} + +/*! + Sets the relevance \a hint to be used when searching for a place. +*/ +void QPlaceSearchRequest::setRelevanceHint(QPlaceSearchRequest::RelevanceHint hint) +{ + Q_D(QPlaceSearchRequest); + d->relevanceHint = hint; +} + +/*! + Returns the maximum number of search results to retrieve. + + A negative value for limit means that it is undefined. It is left up to the backend + provider to choose an appropriate number of results to return. The default limit is -1. +*/ +int QPlaceSearchRequest::limit() const +{ + Q_D(const QPlaceSearchRequest); + return d->limit; +} + +/*! + Set the maximum number of search results to retrieve to \a limit. +*/ +void QPlaceSearchRequest::setLimit(int limit) +{ + Q_D(QPlaceSearchRequest); + d->limit = limit; +} + +/*! + Clears the search request. +*/ +void QPlaceSearchRequest::clear() +{ + Q_D(QPlaceSearchRequest); + d->clear(); +} + +inline QPlaceSearchRequestPrivate *QPlaceSearchRequest::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QPlaceSearchRequestPrivate *QPlaceSearchRequest::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +QT_END_NAMESPACE diff --git a/src/location/places/qplacesearchrequest.h b/src/location/places/qplacesearchrequest.h new file mode 100644 index 0000000..0965450 --- /dev/null +++ b/src/location/places/qplacesearchrequest.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHREQUEST_H +#define QPLACESEARCHREQUEST_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoShape; +class QPlaceSearchRequestPrivate; + +class Q_LOCATION_EXPORT QPlaceSearchRequest +{ +public: + enum RelevanceHint { + UnspecifiedHint, + DistanceHint, + LexicalPlaceNameHint + }; + + QPlaceSearchRequest(); + QPlaceSearchRequest(const QPlaceSearchRequest &other); + + + QPlaceSearchRequest &operator=(const QPlaceSearchRequest &other); + + bool operator==(const QPlaceSearchRequest &other) const; + bool operator!=(const QPlaceSearchRequest &other) const; + + ~QPlaceSearchRequest(); + + QString searchTerm() const; + void setSearchTerm(const QString &term); + + QList categories() const; + void setCategory(const QPlaceCategory &category); + void setCategories(const QList &categories); + + QGeoShape searchArea() const; + void setSearchArea(const QGeoShape &area); + + QString recommendationId() const; + void setRecommendationId(const QString &recommendationId); + + QVariant searchContext() const; + void setSearchContext(const QVariant &context); + + QLocation::VisibilityScope visibilityScope() const; + void setVisibilityScope(QLocation::VisibilityScope visibilityScopes); + + RelevanceHint relevanceHint() const; + void setRelevanceHint(RelevanceHint hint); + + int limit() const; + void setLimit(int limit); + + void clear(); + +private: + QSharedDataPointer d_ptr; + inline QPlaceSearchRequestPrivate *d_func(); + inline const QPlaceSearchRequestPrivate *d_func() const; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceSearchRequest::RelevanceHint) + +#endif // QPLACESEARCHQUERY_H diff --git a/src/location/places/qplacesearchresult.cpp b/src/location/places/qplacesearchresult.cpp new file mode 100644 index 0000000..d8ddc50 --- /dev/null +++ b/src/location/places/qplacesearchresult.cpp @@ -0,0 +1,214 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchresult.h" +#include "qplacesearchresult_p.h" +#include "qplaceresult.h" +#include + +QT_USE_NAMESPACE + +template<> QPlaceSearchResultPrivate *QSharedDataPointer::clone() +{ + return d->clone(); +} + +inline QPlaceSearchResultPrivate *QPlaceSearchResult::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QPlaceSearchResultPrivate *QPlaceSearchResult::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +bool QPlaceSearchResultPrivate::compare(const QPlaceSearchResultPrivate *other) const +{ + return title == other->title + && icon == other->icon; +} + +/*! + \class QPlaceSearchResult + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceSearchResult class is the base class for search results. + + A list of search results can be retrieved from the QPlaceSearchReply after it has + successfully completed the request. Common to all search results are the + \l {QPlaceSearchResult::title()} {title} and \l {QPlaceSearchResult::icon()}{icon}, + which can be used to present the search result to the user. + + The intended usage is that depending on the \l {QPlaceSearchResult::type()} {type}, + the search result can be converted to a more detailed subclass like so: + + \snippet places/requesthandler.h Convert search result + + The implementation is handled in such a way that object slicing is not an issue. + It is not expected that client applications or backend plugins instantiate + a QPlaceSearchResult directly, but rather client applications simply convert + to search result subclasses and backend plugins only instantiate subclasses. + + \sa QPlaceResult +*/ + +/*! + \enum QPlaceSearchResult::SearchResultType + + Defines the type of search result + + \value UnknownSearchResult The contents of the search result are unknown. + \value PlaceResult The search result contains a place. + \value ProposedSearchResult The search result contains a proposed search which may be relevant. +*/ + +/*! + Constructs a new search result. +*/ +QPlaceSearchResult::QPlaceSearchResult() + : d_ptr(new QPlaceSearchResultPrivate) +{ +} + +/*! + Constructs a copy of \a other +*/ +QPlaceSearchResult::QPlaceSearchResult(const QPlaceSearchResult &other) + :d_ptr(other.d_ptr) +{ +} + +/*! + Destroys the search result. +*/ +QPlaceSearchResult::~QPlaceSearchResult() +{ +} + +/*! + Assigns \a other to this search result and returns a reference to this + search result. +*/ +QPlaceSearchResult &QPlaceSearchResult::operator =(const QPlaceSearchResult &other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns true if \a other is equal to this search result, otherwise + returns false. +*/ +bool QPlaceSearchResult::operator==(const QPlaceSearchResult &other) const +{ + // An unknown object is only equal to another unknown search result + if (!d_ptr) + return !other.d_ptr; + + if (type() != other.type()) + return false; + + return d_ptr->compare(other.d_ptr); +} + +/*! + \fn bool QPlaceSearchResult::operator!=(const QPlaceSearchResult &other) const + Returns true if \a other not equal to this search result, otherwise + returns false. +*/ + +/*! + Returns the result type. +*/ +QPlaceSearchResult::SearchResultType QPlaceSearchResult::type() const +{ + if (!d_ptr) + return UnknownSearchResult; + return d_ptr->type(); +} + +/*! + Returns the title of the search result. This string can be used to display the search result + to the user. +*/ +QString QPlaceSearchResult::title() const +{ + Q_D(const QPlaceSearchResult); + return d->title; +} + +/*! + Sets the title of the search result to \a title. +*/ +void QPlaceSearchResult::setTitle(const QString &title) +{ + Q_D(QPlaceSearchResult); + d->title = title; +} + +/*! + Returns an icon that can be used to represent the search result. +*/ +QPlaceIcon QPlaceSearchResult::icon() const +{ + Q_D(const QPlaceSearchResult); + return d->icon; +} + +/*! + Sets the icon of the search result to \a icon. +*/ +void QPlaceSearchResult::setIcon(const QPlaceIcon &icon) +{ + Q_D(QPlaceSearchResult); + d->icon = icon; +} + +/*! + \internal + Constructs a new search result from the given pointer \a d. +*/ +QPlaceSearchResult::QPlaceSearchResult(QPlaceSearchResultPrivate *d) + :d_ptr(d) +{ +} diff --git a/src/location/places/qplacesearchresult.h b/src/location/places/qplacesearchresult.h new file mode 100644 index 0000000..436060d --- /dev/null +++ b/src/location/places/qplacesearchresult.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHRESULT_H +#define QPLACESEARCHRESULT_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +#define Q_DECLARE_SEARCHRESULT_D_FUNC(Class) \ + inline Class##Private *d_func(); \ + inline const Class##Private *d_func() const;\ + friend class Class##Private; + +#define Q_DECLARE_SEARCHRESULT_COPY_CTOR(Class) \ + Class(const QPlaceSearchResult &other); + +class QPlaceSearchRequest; +class QPlaceSearchResultPrivate; +class QPlaceIcon; + +class Q_LOCATION_EXPORT QPlaceSearchResult +{ +public: + QPlaceSearchResult(); + QPlaceSearchResult(const QPlaceSearchResult &other); + + virtual ~QPlaceSearchResult(); + + QPlaceSearchResult &operator=(const QPlaceSearchResult &other); + + bool operator==(const QPlaceSearchResult &other) const; + bool operator!=(const QPlaceSearchResult &other) const { + return !(other == *this); + } + + enum SearchResultType { + UnknownSearchResult = 0, + PlaceResult, + ProposedSearchResult + }; + + SearchResultType type() const; + + QString title() const; + void setTitle(const QString &title); + + QPlaceIcon icon() const; + void setIcon(const QPlaceIcon &icon); + +protected: + explicit QPlaceSearchResult(QPlaceSearchResultPrivate *d); + QSharedDataPointer d_ptr; + +private: + inline QPlaceSearchResultPrivate *d_func(); + inline const QPlaceSearchResultPrivate *d_func() const; + + friend class QPlaceSearchResultPrivate; +}; + +Q_DECLARE_TYPEINFO(QPlaceSearchResult, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceSearchResult) +Q_DECLARE_METATYPE(QPlaceSearchResult::SearchResultType) + +#endif // QPLACESEARCHRESULT_H diff --git a/src/location/places/qplacesearchresult_p.h b/src/location/places/qplacesearchresult_p.h new file mode 100644 index 0000000..3d44469 --- /dev/null +++ b/src/location/places/qplacesearchresult_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHRESULT_P_H +#define QPLACESEARCHRESULT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qplacesearchresult.h" +#include "qplacesearchrequest.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +// defines must be in sync with class below +#define Q_IMPLEMENT_SEARCHRESULT_D_FUNC(Class) \ + Class##Private *Class::d_func() { return reinterpret_cast(d_ptr.data()); } \ + const Class##Private *Class::d_func() const { return reinterpret_cast(d_ptr.constData()); } \ + +#define Q_IMPLEMENT_SEARCHRESULT_COPY_CTOR(Class) \ + Class::Class(const QPlaceSearchResult &other) : QPlaceSearchResult() { Class##Private::copyIfPossible(d_ptr, other); } + +#define Q_DEFINE_SEARCHRESULT_PRIVATE_HELPER(Class, ResultType) \ + virtual QPlaceSearchResultPrivate *clone() const override { return new Class##Private(*this); } \ + virtual QPlaceSearchResult::SearchResultType type() const override {return ResultType;} \ + static void copyIfPossible(QSharedDataPointer &d_ptr, const QPlaceSearchResult &other) \ + { \ + if (other.type() == ResultType) \ + d_ptr = extract_d(other); \ + else \ + d_ptr = new Class##Private; \ + } + +class QPlaceSearchResultPrivate : public QSharedData +{ +public: + QPlaceSearchResultPrivate() {} + virtual ~QPlaceSearchResultPrivate() {} + + virtual bool compare(const QPlaceSearchResultPrivate *other) const; + + static const QSharedDataPointer + &extract_d(const QPlaceSearchResult &other) { return other.d_ptr; } + + virtual QPlaceSearchResultPrivate *clone() const { return new QPlaceSearchResultPrivate(*this); } + virtual QPlaceSearchResult::SearchResultType type() const { return QPlaceSearchResult::UnknownSearchResult; } + static void copyIfPossible(QSharedDataPointer &d_ptr, const QPlaceSearchResult &other) + { + if (other.type() == QPlaceSearchResult::UnknownSearchResult) + d_ptr = extract_d(other); + else + d_ptr = new QPlaceSearchResultPrivate; + } + + QString title; + QPlaceIcon icon; +}; + +template<> QPlaceSearchResultPrivate *QSharedDataPointer::clone(); + +QT_END_NAMESPACE + +#endif // QPLACESEARCHRESULT_P_H diff --git a/src/location/places/qplacesearchsuggestionreply.cpp b/src/location/places/qplacesearchsuggestionreply.cpp new file mode 100644 index 0000000..9bfc5f0 --- /dev/null +++ b/src/location/places/qplacesearchsuggestionreply.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchsuggestionreply.h" +#include "qplacereply_p.h" + +QT_BEGIN_NAMESPACE + +class QPlaceSearchSuggestionReplyPrivate : public QPlaceReplyPrivate +{ +public: + QPlaceSearchSuggestionReplyPrivate(){} + QStringList suggestions; +}; + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +/*! + \class QPlaceSearchSuggestionReply + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-replies + \since 5.6 + + \brief The QPlaceSearchSuggestionReply class manages a search suggestion operation started by an + instance of QPlaceManager. + + On successful completion of the operation, the reply will contain a list of search term + suggestions. + See \l {Search Suggestions} for an example on how to use a search suggestion reply. + + \sa QPlaceManager +*/ + +/*! + Constructs a search suggestion reply with a given \a parent. +*/ +QPlaceSearchSuggestionReply::QPlaceSearchSuggestionReply(QObject *parent) + : QPlaceReply(new QPlaceSearchSuggestionReplyPrivate, parent) +{ +} + +/*! + Destroys the reply. +*/ +QPlaceSearchSuggestionReply::~QPlaceSearchSuggestionReply() +{ +} + +/*! + Returns the search term suggestions. +*/ +QStringList QPlaceSearchSuggestionReply::suggestions() const +{ + Q_D(const QPlaceSearchSuggestionReply); + return d->suggestions; +} + +/*! + Returns type of reply. +*/ +QPlaceReply::Type QPlaceSearchSuggestionReply::type() const +{ + return QPlaceReply::SearchSuggestionReply; +} + +/*! + Sets the search term \a suggestions. +*/ +void QPlaceSearchSuggestionReply::setSuggestions(const QStringList &suggestions) +{ + Q_D(QPlaceSearchSuggestionReply); + d->suggestions = suggestions; +} diff --git a/src/location/places/qplacesearchsuggestionreply.h b/src/location/places/qplacesearchsuggestionreply.h new file mode 100644 index 0000000..4908887 --- /dev/null +++ b/src/location/places/qplacesearchsuggestionreply.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHSUGGESTIONREPLY_H +#define QPLACESEARCHSUGGESTIONREPLY_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceSearchSuggestionReplyPrivate; + +class Q_LOCATION_EXPORT QPlaceSearchSuggestionReply : public QPlaceReply +{ + Q_OBJECT +public: + explicit QPlaceSearchSuggestionReply(QObject *parent = nullptr); + ~QPlaceSearchSuggestionReply(); + + QStringList suggestions() const; + Type type() const; + +protected: + void setSuggestions(const QStringList &suggestions); + +private: + Q_DISABLE_COPY(QPlaceSearchSuggestionReply) + Q_DECLARE_PRIVATE(QPlaceSearchSuggestionReply) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/qplacesupplier.cpp b/src/location/places/qplacesupplier.cpp new file mode 100644 index 0000000..aa9e122 --- /dev/null +++ b/src/location/places/qplacesupplier.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesupplier.h" +#include "qplacesupplier_p.h" + +QT_USE_NAMESPACE + +QPlaceSupplierPrivate::QPlaceSupplierPrivate() : QSharedData() +{ +} + +QPlaceSupplierPrivate::QPlaceSupplierPrivate(const QPlaceSupplierPrivate &other) + : QSharedData() +{ + this->name = other.name; + this->supplierId = other.supplierId; + this->url = other.url; + this->icon = other.icon; +} + +QPlaceSupplierPrivate::~QPlaceSupplierPrivate() +{ +} + +bool QPlaceSupplierPrivate::operator==(const QPlaceSupplierPrivate &other) const +{ + return ( + this->name == other.name + && this->supplierId == other.supplierId + && this->url == other.url + && this->icon == other.icon + ); +} + +bool QPlaceSupplierPrivate::isEmpty() const +{ + return (name.isEmpty() + && supplierId.isEmpty() + && url.isEmpty() + && icon.isEmpty() + ); +} + +/*! + \class QPlaceSupplier + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceSupplier class represents a supplier of a place or content associated + with a place. + + Each instance represents a set of data about a supplier, which can include + supplier's name, url and icon. The supplier is typically a business or organization. + + Note: The Places API only supports suppliers as 'retrieve-only' objects. Submitting + suppliers to a provider is not a supported use case. +*/ + +/*! + Constructs a new supplier object. +*/ +QPlaceSupplier::QPlaceSupplier() + : d(new QPlaceSupplierPrivate) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QPlaceSupplier::QPlaceSupplier(const QPlaceSupplier &other) + :d(other.d) +{ +} + +/*! + Destroys the supplier object. +*/ +QPlaceSupplier::~QPlaceSupplier() +{ +} + +/*! + Assigns \a other to this supplier and returns a reference to this + supplier. +*/ +QPlaceSupplier &QPlaceSupplier::operator=(const QPlaceSupplier &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +/*! + Returns true if this supplier is equal to \a other, + otherwise returns false. +*/ +bool QPlaceSupplier::operator==(const QPlaceSupplier &other) const +{ + return (*(d.constData()) == *(other.d.constData())); +} + +/*! + \fn QPlaceSupplier::operator!=(const QPlaceSupplier &other) const + + Returns true if this supplier is not equal to \a other, + otherwise returns false. +*/ + +/*! + Returns the name of the supplier which can be displayed to the user. + + The name can potentially be localized. The language is dependent on the + entity that sets it, typically this is the QPlaceManager. + The QPlaceManager::locales() field defines what language is used. +*/ +QString QPlaceSupplier::name() const +{ + return d->name; +} + +/*! + Sets the \a name of the supplier. +*/ +void QPlaceSupplier::setName(const QString &name) +{ + d->name = name; +} + +/*! + Returns the identifier of the supplier. The identifier is unique + to the manager backend which provided the supplier and is generally + not suitable for displaying to the user. +*/ +QString QPlaceSupplier::supplierId() const +{ + return d->supplierId; +} + +/*! + Sets the \a identifier of the supplier. +*/ +void QPlaceSupplier::setSupplierId(const QString &identifier) +{ + d->supplierId = identifier; +} + +/*! + Returns the URL of the supplier's website. +*/ +QUrl QPlaceSupplier::url() const +{ + return d->url; +} + +/*! + Sets the \a url of the supplier's website. +*/ +void QPlaceSupplier::setUrl(const QUrl &url) +{ + d->url = url; +} + +/*! + Returns the icon of the supplier. +*/ +QPlaceIcon QPlaceSupplier::icon() const +{ + return d->icon; +} + +/*! + Sets the \a icon of the supplier. +*/ +void QPlaceSupplier::setIcon(const QPlaceIcon &icon) +{ + d->icon = icon; +} + +/*! + Returns true if all fields of the place supplier are 0; otherwise returns false. +*/ +bool QPlaceSupplier::isEmpty() const +{ + return d->isEmpty(); +} diff --git a/src/location/places/qplacesupplier.h b/src/location/places/qplacesupplier.h new file mode 100644 index 0000000..21fcf57 --- /dev/null +++ b/src/location/places/qplacesupplier.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESUPPLIER_H +#define QPLACESUPPLIER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QUrl; +class QPlaceSupplierPrivate; + +class Q_LOCATION_EXPORT QPlaceSupplier +{ +public: + QPlaceSupplier(); + QPlaceSupplier(const QPlaceSupplier &other); + ~QPlaceSupplier(); + + QPlaceSupplier &operator=(const QPlaceSupplier &other); + + bool operator==(const QPlaceSupplier &other) const; + bool operator!=(const QPlaceSupplier &other) const { + return !(other == *this); + } + + QString name() const; + void setName(const QString &data); + + QString supplierId() const; + void setSupplierId(const QString &identifier); + + QUrl url() const; + void setUrl(const QUrl &data); + + QPlaceIcon icon() const; + void setIcon(const QPlaceIcon &icon); + + bool isEmpty() const; + +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceSupplier) + +#endif // QPLACESUPPLIER_H diff --git a/src/location/places/qplacesupplier_p.h b/src/location/places/qplacesupplier_p.h new file mode 100644 index 0000000..ab09349 --- /dev/null +++ b/src/location/places/qplacesupplier_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESUPPLIER_P_H +#define QPLACESUPPLIER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include "qplaceicon.h" + +QT_BEGIN_NAMESPACE + +class QPlaceSupplierPrivate : public QSharedData +{ +public: + QPlaceSupplierPrivate(); + QPlaceSupplierPrivate(const QPlaceSupplierPrivate &other); + + ~QPlaceSupplierPrivate(); + + bool operator==(const QPlaceSupplierPrivate &other) const; + + bool isEmpty() const; + + QString name; + QString supplierId; + QUrl url; + QPlaceIcon icon; +}; + +QT_END_NAMESPACE + +#endif // QPLACESUPPLIER_P_H diff --git a/src/location/places/qplaceuser.cpp b/src/location/places/qplaceuser.cpp new file mode 100644 index 0000000..fbfba62 --- /dev/null +++ b/src/location/places/qplaceuser.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceuser.h" +#include "qplaceuser_p.h" + +QT_USE_NAMESPACE + +QPlaceUserPrivate::QPlaceUserPrivate() + : QSharedData() +{ +} + +QPlaceUserPrivate::QPlaceUserPrivate(const QPlaceUserPrivate &other) + : QSharedData(), userId(other.userId), name(other.name) +{ +} + +QPlaceUserPrivate::~QPlaceUserPrivate() +{ +} + +bool QPlaceUserPrivate::operator==(const QPlaceUserPrivate &other) const +{ + return userId == other.userId && name == other.name; +} + +/*! + \class QPlaceUser + \inmodule QtLocation + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.6 + + \brief The QPlaceUser class represents an individual user. +*/ + +/*! + Constructs a new user object. +*/ +QPlaceUser::QPlaceUser() + : d(new QPlaceUserPrivate) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QPlaceUser::QPlaceUser(const QPlaceUser &other) + :d(other.d) +{ +} + +/*! + Destroys the user object. +*/ +QPlaceUser::~QPlaceUser() +{ +} + +/*! + Assigns \a other to this user and returns a reference to this user. +*/ +QPlaceUser &QPlaceUser::operator=(const QPlaceUser &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +/*! + \fn bool QPlaceUser::operator!=(const QPlaceUser &other) const + + Returns true if \a other is not equal to this user, + otherwise returns false. +*/ + +/*! + Returns true if this user is equal to \a other. + Otherwise returns false. +*/ +bool QPlaceUser::operator==(const QPlaceUser &other) const +{ + return (*d) == *(other.d); +} + +/*! + Returns the identifier of the user. +*/ +QString QPlaceUser::userId() const +{ + return d->userId; +} + +/*! + Sets the \a identifier of the user. +*/ +void QPlaceUser::setUserId(const QString &identifier) +{ + d->userId = identifier; +} + +/*! + Returns the name of the user. +*/ +QString QPlaceUser::name() const +{ + return d->name; +} + +/*! + Sets the \a name of the user. +*/ + +void QPlaceUser::setName(const QString &name) +{ + d->name = name; +} diff --git a/src/location/places/qplaceuser.h b/src/location/places/qplaceuser.h new file mode 100644 index 0000000..1709783 --- /dev/null +++ b/src/location/places/qplaceuser.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEUSER_H +#define QPLACEUSER_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceUserPrivate; + +class Q_LOCATION_EXPORT QPlaceUser +{ +public: + QPlaceUser(); + QPlaceUser(const QPlaceUser &other); + ~QPlaceUser(); + + QPlaceUser &operator=(const QPlaceUser &other); + + bool operator==(const QPlaceUser &other) const; + bool operator!=(const QPlaceUser &other) const { + return !(other == *this); + } + + QString userId() const; + void setUserId(const QString &identifier); + + QString name() const; + void setName(const QString &name); + +private: + QSharedDataPointer d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QPlaceUser) + +#endif diff --git a/src/location/places/qplaceuser_p.h b/src/location/places/qplaceuser_p.h new file mode 100644 index 0000000..f49110b --- /dev/null +++ b/src/location/places/qplaceuser_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEUSER_P_H +#define QPLACEUSER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceUserPrivate : public QSharedData +{ +public: + QPlaceUserPrivate(); + QPlaceUserPrivate(const QPlaceUserPrivate &other); + + ~QPlaceUserPrivate(); + + bool operator==(const QPlaceUserPrivate &other) const; + + QString userId; + QString name; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/location/places/unsupportedreplies_p.h b/src/location/places/unsupportedreplies_p.h new file mode 100644 index 0000000..c1b4160 --- /dev/null +++ b/src/location/places/unsupportedreplies_p.h @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UNSUPPORTEDREPLIES_P_H +#define UNSUPPORTEDREPLIES_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qplacedetailsreply.h" +#include "qplacecontentreply.h" +#include "qplacesearchreply.h" +#include "qplacesearchsuggestionreply.h" +#include "qplaceidreply.h" + +#include "qplacematchreply.h" +#include "qplacemanagerengine.h" + +class Q_LOCATION_PRIVATE_EXPORT QPlaceDetailsReplyUnsupported : public QPlaceDetailsReply +{ + Q_OBJECT + +public: + QPlaceDetailsReplyUnsupported(QPlaceManagerEngine *parent) + : QPlaceDetailsReply(parent) + { + setError(QPlaceReply::UnsupportedError, + QStringLiteral("Getting place details is not supported.")); + setFinished(true); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(parent, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this), + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + QMetaObject::invokeMethod(parent, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this)); + } +}; + +class Q_LOCATION_PRIVATE_EXPORT QPlaceContentReplyUnsupported : public QPlaceContentReply +{ + Q_OBJECT + +public: + QPlaceContentReplyUnsupported(QPlaceManagerEngine *parent) + : QPlaceContentReply(parent) + { + setError(QPlaceReply::UnsupportedError, + QStringLiteral("Place content is not supported.")); + setFinished(true); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(parent, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this), + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + QMetaObject::invokeMethod(parent, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this)); + } +}; + +class Q_LOCATION_PRIVATE_EXPORT QPlaceSearchReplyUnsupported : public QPlaceSearchReply +{ + Q_OBJECT + +public: + QPlaceSearchReplyUnsupported(QPlaceReply::Error errorCode, const QString &message, + QPlaceManagerEngine *parent) + : QPlaceSearchReply(parent) + { + setError(errorCode, message); + setFinished(true); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(parent, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this), + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + QMetaObject::invokeMethod(parent, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this)); + } +}; + +class Q_LOCATION_PRIVATE_EXPORT QPlaceSearchSuggestionReplyUnsupported : public QPlaceSearchSuggestionReply +{ + Q_OBJECT + +public: + QPlaceSearchSuggestionReplyUnsupported(QPlaceManagerEngine *parent) + : QPlaceSearchSuggestionReply(parent) + { + setError(QPlaceReply::UnsupportedError, + QStringLiteral("Place search suggestions are not supported.")); + setFinished(true); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(parent, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this), + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + QMetaObject::invokeMethod(parent, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this)); + } +}; + +class Q_LOCATION_PRIVATE_EXPORT QPlaceIdReplyUnsupported : public QPlaceIdReply +{ + Q_OBJECT + +public: + QPlaceIdReplyUnsupported(const QString &message, QPlaceIdReply::OperationType type, + QPlaceManagerEngine *parent) + : QPlaceIdReply(type, parent) + { + setError(QPlaceReply::UnsupportedError, message); + setFinished(true); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(parent, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this), + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + QMetaObject::invokeMethod(parent, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this)); + } +}; + +class Q_LOCATION_PRIVATE_EXPORT QPlaceReplyUnsupported : public QPlaceReply +{ + Q_OBJECT + +public: + QPlaceReplyUnsupported(const QString &message, QPlaceManagerEngine *parent) + : QPlaceReply(parent) + { + setError(QPlaceReply::UnsupportedError, message); + setFinished(true); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(parent, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this), + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + QMetaObject::invokeMethod(parent, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this)); + } +}; + +class Q_LOCATION_PRIVATE_EXPORT QPlaceMatchReplyUnsupported : public QPlaceMatchReply +{ + Q_OBJECT + +public: + QPlaceMatchReplyUnsupported(QPlaceManagerEngine *parent) + : QPlaceMatchReply(parent) + { + setError(QPlaceReply::UnsupportedError, + QStringLiteral("Place matching is not supported.")); + setFinished(true); + QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(parent, "error", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this), + Q_ARG(QPlaceReply::Error, error()), + Q_ARG(QString, errorString())); + QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection); + QMetaObject::invokeMethod(parent, "finished", Qt::QueuedConnection, + Q_ARG(QPlaceReply *, this)); + } +}; + +#endif diff --git a/src/location/qlocation.cpp b/src/location/qlocation.cpp new file mode 100644 index 0000000..2afed10 --- /dev/null +++ b/src/location/qlocation.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +QT_BEGIN_NAMESPACE + +namespace QLocation { + +/*! + \namespace QLocation + \inmodule QtLocation + \keyword QLocation Namespace + + \brief The QLocation namespace contains miscellaneous identifiers used throughout the + QtLocation module. +*/ + +/*! + \enum QLocation::Visibility + + Defines the visibility of a QPlace or QPlaceCategory. + + \value UnspecifiedVisibility No explicit visibility has been defined. + \value DeviceVisibility Places and categories with DeviceVisibility are only stored on + the local device. + \value PrivateVisibility Places and categories with PrivateVisibility are only visible + to the current user. The data may be stored either locally or + on a remote service or both. + \value PublicVisibility Places and categories with PublicVisibility are visible to + everyone. + + A particular manager may support one or more visibility scopes. For example a manager from one provider may only provide places + that are public to everyone, whilst another may provide both public and private places. + + \note The meaning of unspecified visibility depends on the context it is used. + + When \e saving a place or category, the + default visibility is unspecified meaning that the manager chooses an appropriate visibility scope for the item. + + When \e searching for places, unspecified means that places of any scope is returned. +*/ +} + +QT_END_NAMESPACE diff --git a/src/location/qlocation.h b/src/location/qlocation.h new file mode 100644 index 0000000..d30a3a3 --- /dev/null +++ b/src/location/qlocation.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOCATION_H +#define QLOCATION_H + +#if 0 +#pragma qt_class(QLocation) +#endif + +#include + +QT_BEGIN_NAMESPACE + +namespace QLocation { + +enum Visibility { + UnspecifiedVisibility = 0x00, + DeviceVisibility = 0x01, + PrivateVisibility = 0x02, + PublicVisibility = 0x04 +}; + +Q_DECLARE_FLAGS(VisibilityScope, Visibility) + +} + +Q_DECLARE_OPERATORS_FOR_FLAGS(QLocation::VisibilityScope) + +QT_END_NAMESPACE + +#endif // QLOCATION_H diff --git a/src/location/qlocationglobal.h b/src/location/qlocationglobal.h new file mode 100644 index 0000000..ee3d9c8 --- /dev/null +++ b/src/location/qlocationglobal.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QLOCATIONGLOBAL_H +#define QLOCATIONGLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_LOCATION_LIB) +# define Q_LOCATION_EXPORT Q_DECL_EXPORT +# else +# define Q_LOCATION_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_LOCATION_EXPORT +#endif + +#ifdef Q_CLANG_QDOC +#include "QtLocation/qlocation.h" +#endif + +QT_END_NAMESPACE + +#endif // QLOCATIONGLOBAL_H + diff --git a/src/location/qlocationglobal_p.h b/src/location/qlocationglobal_p.h new file mode 100644 index 0000000..afc0760 --- /dev/null +++ b/src/location/qlocationglobal_p.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOCATIONGLOBAL_P_H +#define QLOCATIONGLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qlocationglobal.h" + +QT_BEGIN_NAMESPACE + +#define Q_LOCATION_PRIVATE_EXPORT Q_LOCATION_EXPORT + +QT_END_NAMESPACE + + +#endif // QLOCATIONGLOBAL_P_H diff --git a/src/locationlabs/qlocationlabsglobal_p.h b/src/locationlabs/qlocationlabsglobal_p.h new file mode 100644 index 0000000..6dc2929 --- /dev/null +++ b/src/locationlabs/qlocationlabsglobal_p.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QLOCATIONLABSGLOBAL_P_H +#define QLOCATIONLABSGLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_LOCATIONLABS_LIB) +# define Q_LOCATIONLABS_EXPORT Q_DECL_EXPORT +# else +# define Q_LOCATIONLABS_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_LOCATIONLABS_EXPORT +#endif + +#define Q_LOCATIONLABS_PRIVATE_EXPORT Q_LOCATIONLABS_EXPORT + +QT_END_NAMESPACE + +#endif // QLOCATIONLABSGLOBAL_P_H + diff --git a/src/plugins/geoservices/esri/esri.pro b/src/plugins/geoservices/esri/esri.pro new file mode 100644 index 0000000..3a4da20 --- /dev/null +++ b/src/plugins/geoservices/esri/esri.pro @@ -0,0 +1,42 @@ +TARGET = qtgeoservices_esri + +QT += location-private positioning-private network + +QT_FOR_CONFIG += location-private +qtConfig(location-labs-plugin): DEFINES += LOCATIONLABS + +HEADERS += \ + geocodereply_esri.h \ + geocodingmanagerengine_esri.h \ + geomapsource.h \ + georoutejsonparser_esri.h \ + georoutereply_esri.h \ + georoutingmanagerengine_esri.h \ + geoserviceproviderfactory_esri.h \ + geotiledmap_esri.h \ + geotiledmappingmanagerengine_esri.h \ + geotiledmapreply_esri.h \ + geotilefetcher_esri.h + +SOURCES += \ + geocodereply_esri.cpp \ + geocodingmanagerengine_esri.cpp \ + geomapsource.cpp \ + georoutejsonparser_esri.cpp \ + georoutereply_esri.cpp \ + georoutingmanagerengine_esri.cpp \ + geoserviceproviderfactory_esri.cpp \ + geotiledmap_esri.cpp \ + geotiledmappingmanagerengine_esri.cpp \ + geotiledmapreply_esri.cpp \ + geotilefetcher_esri.cpp + +RESOURCES += \ + esri.qrc + +OTHER_FILES += \ + esri_plugin.json + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = GeoServiceProviderFactoryEsri +load(qt_plugin) diff --git a/src/plugins/geoservices/esri/esri.qrc b/src/plugins/geoservices/esri/esri.qrc new file mode 100644 index 0000000..d085b09 --- /dev/null +++ b/src/plugins/geoservices/esri/esri.qrc @@ -0,0 +1,5 @@ + + + maps.json + + diff --git a/src/plugins/geoservices/esri/esri_plugin.json b/src/plugins/geoservices/esri/esri_plugin.json new file mode 100644 index 0000000..3398648 --- /dev/null +++ b/src/plugins/geoservices/esri/esri_plugin.json @@ -0,0 +1,13 @@ +{ + "Keys": ["esri"], + "Provider": "esri", + "Version": 100, + "Experimental": false, + "Features": [ + "OnlineMappingFeature", + "OnlineGeocodingFeature", + "ReverseGeocodingFeature", + "OnlineRoutingFeature" + ], + "Priority": 1000 +} diff --git a/src/plugins/geoservices/esri/geocodereply_esri.cpp b/src/plugins/geoservices/esri/geocodereply_esri.cpp new file mode 100644 index 0000000..f1dac18 --- /dev/null +++ b/src/plugins/geoservices/esri/geocodereply_esri.cpp @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geocodereply_esri.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +GeoCodeReplyEsri::GeoCodeReplyEsri(QNetworkReply *reply, OperationType operationType, + QObject *parent) : + QGeoCodeReply(parent), m_operationType(operationType) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + + setLimit(1); + setOffset(0); +} + +GeoCodeReplyEsri::~GeoCodeReplyEsri() +{ +} + +void GeoCodeReplyEsri::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QGeoCodeReply::CommunicationError, reply->errorString()); +} + +void GeoCodeReplyEsri::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + + if (document.isObject()) { + QJsonObject object = document.object(); + + switch (operationType()) { + case Geocode: + { + QJsonArray candidates = object.value(QStringLiteral("candidates")).toArray(); + + QList locations; + + for (int i = 0; i < candidates.count(); i++) { + if (!candidates.at(i).isObject()) + continue; + + QJsonObject candidate = candidates.at(i).toObject(); + + QGeoLocation location = parseCandidate(candidate); + locations.append(location); + } + + setLocations(locations); + setFinished(true); + } + break; + + case ReverseGeocode: + { + QGeoLocation location = parseAddress(object); + + QList locations; + locations.append(location); + + setLocations(locations); + setFinished(true); + } + break; + } + + } else { + setError(QGeoCodeReply::CommunicationError, QStringLiteral("Unknown document")); + } +} + +QGeoLocation GeoCodeReplyEsri::parseAddress(const QJsonObject& object) +{ + QJsonObject addressObject = object.value(QStringLiteral("address")).toObject(); + + QGeoAddress address; + + address.setCountryCode(addressObject.value(QStringLiteral("CountryCode")).toString()); + address.setState(addressObject.value(QStringLiteral("Region")).toString()); + address.setCity(addressObject.value(QStringLiteral("City")).toString()); + address.setDistrict(addressObject.value(QStringLiteral("Subregion")).toString()); + address.setPostalCode(addressObject.value(QStringLiteral("Postal")).toString()); + address.setStreet(addressObject.value(QStringLiteral("Address")).toString()); + + QGeoCoordinate coordinate; + + QJsonObject locationObject = object.value(QStringLiteral("location")).toObject(); + + coordinate.setLongitude(locationObject.value(QStringLiteral("x")).toDouble()); + coordinate.setLatitude(locationObject.value(QStringLiteral("y")).toDouble()); + + QGeoLocation location; + + location.setCoordinate(coordinate); + location.setAddress(address); + + return location; +} + +QGeoLocation GeoCodeReplyEsri::parseCandidate(const QJsonObject& candidate) +{ + QGeoCoordinate coordinate; + + QJsonObject locationObject = candidate.value(QStringLiteral("location")).toObject(); + + coordinate.setLongitude(locationObject.value(QStringLiteral("x")).toDouble()); + coordinate.setLatitude(locationObject.value(QStringLiteral("y")).toDouble()); + + QGeoRectangle extent; + + if (candidate.contains(QStringLiteral("extent"))) { + QJsonObject extentObject = candidate.value(QStringLiteral("extent")).toObject(); + + extent.setTopLeft(QGeoCoordinate(extentObject.value(QStringLiteral("ymin")).toDouble(), + extentObject.value(QStringLiteral("xmin")).toDouble())); + + extent.setBottomRight(QGeoCoordinate(extentObject.value(QStringLiteral("ymax")).toDouble(), + extentObject.value(QStringLiteral("xmax")).toDouble())); + } + + QJsonObject attributesObject = candidate.value(QStringLiteral("attributes")).toObject(); + + QGeoAddress address; + + address.setText(candidate.value(QStringLiteral("address")).toString()); + + address.setCountry(attributesObject.value(QStringLiteral("Country")).toString()); + address.setCountryCode(attributesObject.value(QStringLiteral("Country")).toString()); + address.setState(attributesObject.value(QStringLiteral("Region")).toString()); + address.setCity(attributesObject.value(QStringLiteral("City")).toString()); + address.setDistrict(attributesObject.value(QStringLiteral("Subregion")).toString()); + address.setPostalCode(attributesObject.value(QStringLiteral("Postal")).toString()); + + QGeoLocation location; + + location.setCoordinate(coordinate); + location.setBoundingBox(extent); + location.setAddress(address); + + return location; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geocodereply_esri.h b/src/plugins/geoservices/esri/geocodereply_esri.h new file mode 100644 index 0000000..76b416c --- /dev/null +++ b/src/plugins/geoservices/esri/geocodereply_esri.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOCODEREPLYESRI_H +#define GEOCODEREPLYESRI_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class GeoCodeReplyEsri : public QGeoCodeReply +{ + Q_OBJECT + +public: + enum OperationType + { + Geocode, + ReverseGeocode + }; + +public: + GeoCodeReplyEsri(QNetworkReply *reply, OperationType operationType, QObject *parent = nullptr); + ~GeoCodeReplyEsri(); + + inline OperationType operationType() const; + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); + + QGeoLocation parseAddress(const QJsonObject &object); + QGeoLocation parseCandidate(const QJsonObject &candidate); + +private: + OperationType m_operationType; +}; + +inline GeoCodeReplyEsri::OperationType GeoCodeReplyEsri::operationType() const +{ + return m_operationType; +} + +QT_END_NAMESPACE + +#endif // GEOCODEREPLYESRI_H diff --git a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp new file mode 100644 index 0000000..976c51c --- /dev/null +++ b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geocodingmanagerengine_esri.h" +#include "geocodereply_esri.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-find-address-candidates.htm +// https://developers.arcgis.com/rest/geocode/api-reference/geocoding-reverse-geocode.htm + +static const QString kPrefixEsri(QStringLiteral("esri.")); +static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); + +static const QString kUrlGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/findAddressCandidates")); +static const QString kUrlReverseGeocode(QStringLiteral("http://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode")); + +static QString addressToQuery(const QGeoAddress &address) +{ + return address.street() + QStringLiteral(", ") + + address.district() + QStringLiteral(", ") + + address.city() + QStringLiteral(", ") + + address.state() + QStringLiteral(", ") + + address.country(); +} + +static QString boundingBoxToLtrb(const QGeoRectangle &rect) +{ + return QString::number(rect.topLeft().longitude()) + QLatin1Char(',') + + QString::number(rect.topLeft().latitude()) + QLatin1Char(',') + + QString::number(rect.bottomRight().longitude()) + QLatin1Char(',') + + QString::number(rect.bottomRight().latitude()); +} + +GeoCodingManagerEngineEsri::GeoCodingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) +: QGeoCodingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(kParamUserAgent)) + m_userAgent = parameters.value(kParamUserAgent).toString().toLatin1(); + else + m_userAgent = QByteArrayLiteral("Qt Location based application"); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +GeoCodingManagerEngineEsri::~GeoCodingManagerEngineEsri() +{ +} + +QGeoCodeReply *GeoCodingManagerEngineEsri::geocode(const QGeoAddress &address, + const QGeoShape &bounds) +{ + return geocode(addressToQuery(address), 1, -1, bounds); +} + +QGeoCodeReply *GeoCodingManagerEngineEsri::geocode(const QString &address, int limit, int offset, + const QGeoShape &bounds) +{ + Q_UNUSED(offset) + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QUrl url(kUrlGeocode); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("singleLine"), address); + query.addQueryItem(QStringLiteral("f"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("outFields"), "*"); + + if (bounds.type() != QGeoShape::UnknownType) + query.addQueryItem(QStringLiteral("searchExtent"), boundingBoxToLtrb(bounds.boundingGeoRectangle())); + + if (limit != -1) + query.addQueryItem(QStringLiteral("maxLocations"), QString::number(limit)); + + url.setQuery(query); + request.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(request); + GeoCodeReplyEsri *geocodeReply = new GeoCodeReplyEsri(reply, GeoCodeReplyEsri::Geocode, this); + + connect(geocodeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), + this, SLOT(replyError(QGeoCodeReply::Error,QString))); + + return geocodeReply; +} + +QGeoCodeReply *GeoCodingManagerEngineEsri::reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) +{ + Q_UNUSED(bounds) + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QUrl url(kUrlReverseGeocode); + + QUrlQuery query; + + query.addQueryItem(QStringLiteral("f"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("langCode"), locale().name().left(2)); + query.addQueryItem(QStringLiteral("location"), QString::number(coordinate.longitude()) + QLatin1Char(',') + + QString::number(coordinate.latitude())); + + url.setQuery(query); + request.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(request); + GeoCodeReplyEsri *geocodeReply = new GeoCodeReplyEsri(reply, GeoCodeReplyEsri::ReverseGeocode, + this); + + connect(geocodeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), + this, SLOT(replyError(QGeoCodeReply::Error,QString))); + + return geocodeReply; +} + +void GeoCodingManagerEngineEsri::replyFinished() +{ + QGeoCodeReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void GeoCodingManagerEngineEsri::replyError(QGeoCodeReply::Error errorCode, + const QString &errorString) +{ + QGeoCodeReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h new file mode 100644 index 0000000..ff7bf88 --- /dev/null +++ b/src/plugins/geoservices/esri/geocodingmanagerengine_esri.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOCODINGMANAGERENGINEESRI_H +#define GEOCODINGMANAGERENGINEESRI_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class GeoCodingManagerEngineEsri : public QGeoCodingManagerEngine +{ + Q_OBJECT + +public: + GeoCodingManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString); + virtual ~GeoCodingManagerEngineEsri(); + + QGeoCodeReply *geocode(const QGeoAddress &address, const QGeoShape &bounds) override; + QGeoCodeReply *geocode(const QString &address, int limit, int offset, + const QGeoShape &bounds) override; + QGeoCodeReply *reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) override; + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoCodeReply::Error errorCode, const QString &errorString); + +private: + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; +}; + +QT_END_NAMESPACE + +#endif // GEOCODINGMANAGERENGINEESRI_H diff --git a/src/plugins/geoservices/esri/geomapsource.cpp b/src/plugins/geoservices/esri/geomapsource.cpp new file mode 100644 index 0000000..7ec63e2 --- /dev/null +++ b/src/plugins/geoservices/esri/geomapsource.cpp @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geomapsource.h" + +#include + +QT_BEGIN_NAMESPACE + +static const QString kArcGISTileScheme(QStringLiteral("/tile/${z}/${y}/${x}")); + +struct MapStyleData +{ + QString name; + QGeoMapType::MapStyle style; +}; + +static const MapStyleData mapStyles[] = +{ + { QStringLiteral("StreetMap"), QGeoMapType::StreetMap }, + { QStringLiteral("SatelliteMapDay"), QGeoMapType::SatelliteMapDay }, + { QStringLiteral("SatelliteMapNight"), QGeoMapType::SatelliteMapNight }, + { QStringLiteral("TerrainMap"), QGeoMapType::TerrainMap }, + { QStringLiteral("HybridMap"), QGeoMapType::HybridMap }, + { QStringLiteral("TransitMap"), QGeoMapType::TransitMap }, + { QStringLiteral("GrayStreetMap"), QGeoMapType::GrayStreetMap }, + { QStringLiteral("PedestrianMap"), QGeoMapType::PedestrianMap }, + { QStringLiteral("CarNavigationMap"), QGeoMapType::CarNavigationMap }, + { QStringLiteral("CustomMap"), QGeoMapType::CustomMap } +}; + +GeoMapSource::GeoMapSource(QGeoMapType::MapStyle style, const QString &name, + const QString &description, bool mobile, bool night, int mapId, + const QString &url, const QString ©right, const QGeoCameraCapabilities &cameraCapabilities) : + QGeoMapType(style, name, description, mobile, night, mapId, "esri", cameraCapabilities), + m_url(url), m_copyright(copyright) +{ +} + +QString GeoMapSource::toFormat(const QString &url) +{ + QString format = url; + + if (!format.contains(QLatin1String("${"))) + format += kArcGISTileScheme; + + format.replace(QLatin1String("${z}"), QLatin1String("%1")); + format.replace(QLatin1String("${x}"), QLatin1String("%2")); + format.replace(QLatin1String("${y}"), QLatin1String("%3")); + format.replace(QLatin1String("${token}"), QLatin1String("%4")); + + return format; +} + +QGeoMapType::MapStyle GeoMapSource::mapStyle(const QString &styleString) +{ + for (unsigned int i = 0; i < sizeof(mapStyles)/sizeof(MapStyle); i++) { + const MapStyleData &mapStyle = mapStyles[i]; + + if (styleString.compare(mapStyle.name, Qt::CaseInsensitive) == 0) + return mapStyle.style; + } + + QGeoMapType::MapStyle style = static_cast(styleString.toInt()); + if (style <= QGeoMapType::NoMap) + style = QGeoMapType::CustomMap; + + return style; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geomapsource.h b/src/plugins/geoservices/esri/geomapsource.h new file mode 100644 index 0000000..86258d2 --- /dev/null +++ b/src/plugins/geoservices/esri/geomapsource.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOMAPSOURCE_H +#define GEOMAPSOURCE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class GeoMapSource : public QGeoMapType +{ +public: + GeoMapSource(QGeoMapType::MapStyle style, const QString &name, + const QString &description, bool mobile, bool night, int mapId, + const QString &url, const QString ©right, const QGeoCameraCapabilities &cameraCapabilities); + + inline const QString &url() const; + inline const QString ©right() const; + + static QString toFormat(const QString &url); + static QGeoMapType::MapStyle mapStyle(const QString &styleString); + +private: + QString m_url; + QString m_copyright; +}; + +inline const QString &GeoMapSource::url() const +{ + return m_url; +} + +inline const QString &GeoMapSource::copyright() const +{ + return m_copyright; +} + +QT_END_NAMESPACE + +#endif // GEOMAPSOURCE_H diff --git a/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp b/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp new file mode 100644 index 0000000..30db48f --- /dev/null +++ b/src/plugins/geoservices/esri/georoutejsonparser_esri.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "georoutejsonparser_esri.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ + +static const QString kErrorMessage(QStringLiteral("Error %1: %2.")); +static const QString kErrorJson(QStringLiteral("Error: invalide JSON document.")); + +static const QString kErrorKey(QStringLiteral("error")); +static const QString kErrorCodeKey(QStringLiteral("code")); +static const QString kErrorMessageKey(QStringLiteral("message")); +static const QString kErrorDetailsKey(QStringLiteral("details")); +static const QString kDirectionsKey(QStringLiteral("directions")); +static const QString kRoutesKey(QStringLiteral("routes")); +static const QString kBarriersKey(QStringLiteral("barriers")); +static const QString kMessagesKey(QStringLiteral("messages")); +static const QString kDirectionsRouteIdKey(QStringLiteral("routeId")); +static const QString kDirectionsRouteNameKey(QStringLiteral("routeName")); +static const QString kDirectionsSummaryKey(QStringLiteral("summary")); +static const QString kDirectionsTotalLengthKey(QStringLiteral("totalLength")); +static const QString kDirectionsTotalTimeKey(QStringLiteral("totalTime")); +static const QString kDirectionsTotalDriveTimeKey(QStringLiteral("totalDriveTime")); +static const QString kDirectionsEnvelopeKey(QStringLiteral("envelope")); +static const QString kDirectionsEnvelopeXminKey(QStringLiteral("xmin")); +static const QString kDirectionsEnvelopeYminKey(QStringLiteral("ymin")); +static const QString kDirectionsEnvelopeXmaxKey(QStringLiteral("xmax")); +static const QString kDirectionsEnvelopeYmaxKey(QStringLiteral("ymax")); +static const QString kDirectionsFeaturesKey(QStringLiteral("features")); +static const QString kDirectionsFeaturesAttributesKey(QStringLiteral("attributes")); +static const QString kDirectionsFeaturesCompressedGeometryKey(QStringLiteral("compressedGeometry")); +static const QString kDirectionsFeaturesAttributesLengthKey(QStringLiteral("length")); +static const QString kDirectionsFeaturesAttributesTimeKey(QStringLiteral("time")); +static const QString kDirectionsFeaturesAttributesTextKey(QStringLiteral("text")); +static const QString kDirectionsFeaturesAttributesEtaKey(QStringLiteral("ETA")); +static const QString kDirectionsFeaturesAttributesManeuverTypeKey(QStringLiteral("maneuverType")); +static const QString kRoutesFeaturesKey(QStringLiteral("features")); +static const QString kRoutesFeaturesAttributesKey(QStringLiteral("attributes")); +static const QString kRoutesFeaturesObjectIdKey(QStringLiteral("ObjectID")); +static const QString kRoutesFeaturesGeometryKey(QStringLiteral("geometry")); +static const QString kRoutesFeaturesGeometryPathsKey(QStringLiteral("paths")); + +GeoRouteJsonParserEsri::GeoRouteJsonParserEsri(const QJsonDocument &document) +{ + if (!document.isObject()) + { + m_error = kErrorJson; + return; + } + + m_json = document.object(); + if (m_json.contains(kErrorKey)) + { + QJsonObject error = m_json.value(kErrorKey).toObject(); + int code = error.value(kErrorCodeKey).toInt(); + QString message = error.value(kErrorMessageKey).toString(); + + m_error = kErrorMessage.arg(code).arg(message); + return; + } + + parseDirections(); + parseRoutes(); +} + +QList GeoRouteJsonParserEsri::routes() const +{ + return m_routes.values(); +} + +bool GeoRouteJsonParserEsri::isValid() const +{ + return (m_error.isEmpty()); +} + +QString GeoRouteJsonParserEsri::errorString() const +{ + return m_error; +} + +void GeoRouteJsonParserEsri::parseDirections() +{ + QJsonArray directions = m_json.value(kDirectionsKey).toArray(); + foreach (const QJsonValue &direction, directions) + parseDirection(direction.toObject()); +} + +void GeoRouteJsonParserEsri::parseDirection(const QJsonObject &direction) +{ + QGeoRoute &geoRoute = m_routes[direction.value(kDirectionsRouteIdKey).toInt()]; + + // parse summary + geoRoute.setRouteId(direction.value(kDirectionsRouteNameKey).toString()); + + QJsonObject summary = direction.value(kDirectionsSummaryKey).toObject(); + geoRoute.setDistance(summary.value(kDirectionsTotalLengthKey).toDouble()); + + geoRoute.setTravelTime(summary.value(kDirectionsTotalTimeKey).toDouble() * 60); + // default units is minutes, see directionsTimeAttributeName param + + geoRoute.setTravelMode(QGeoRouteRequest::CarTravel); + // default request is time for car, see directionsTimeAttributeName param + + QJsonObject enveloppe = summary.value(kDirectionsEnvelopeKey).toObject(); + + QGeoCoordinate topLeft(enveloppe.value(kDirectionsEnvelopeXminKey).toDouble(), + enveloppe.value(kDirectionsEnvelopeYmaxKey).toDouble()); + QGeoCoordinate bottomRight(enveloppe.value(kDirectionsEnvelopeXmaxKey).toDouble(), + enveloppe.value(kDirectionsEnvelopeYminKey).toDouble()); + geoRoute.setBounds(QGeoRectangle(topLeft, bottomRight)); + + // parse features + QJsonArray features = direction.value(kDirectionsFeaturesKey).toArray(); + + static const QMap esriDirectionsManeuverTypes + { + { QStringLiteral("esriDMTUnknown"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTStop"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTStraight"), QGeoManeuver::DirectionForward }, + { QStringLiteral("esriDMTBearLeft"), QGeoManeuver::DirectionBearLeft }, + { QStringLiteral("esriDMTBearRight"), QGeoManeuver::DirectionBearRight }, + { QStringLiteral("esriDMTTurnLeft"), QGeoManeuver::DirectionLeft }, + { QStringLiteral("esriDMTTurnRight"), QGeoManeuver::DirectionRight }, + { QStringLiteral("esriDMTSharpLeft"), QGeoManeuver::DirectionLightLeft }, + { QStringLiteral("esriDMTSharpRight"), QGeoManeuver::DirectionLightRight }, + { QStringLiteral("esriDMTUTurn"), QGeoManeuver::DirectionUTurnRight }, + { QStringLiteral("esriDMTFerry"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTRoundabout"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTHighwayMerge"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTHighwayExit"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTHighwayChange"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTForkCenter"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTForkLeft"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTForkRight"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTDepart"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTTripItem"), QGeoManeuver::NoDirection }, + { QStringLiteral("esriDMTEndOfFerry"), QGeoManeuver::NoDirection } + }; + + QGeoRouteSegment firstSegment; + for (int i = features.size() - 1; i >= 0; --i) + { + QJsonObject feature = features.at(i).toObject(); + QJsonObject attributes = feature.value(kDirectionsFeaturesAttributesKey).toObject(); + + QGeoRouteSegment segment; + double length = attributes.value(kDirectionsFeaturesAttributesLengthKey).toDouble(); + segment.setDistance(length); + + double time = attributes.value(kDirectionsFeaturesAttributesTimeKey).toDouble() * 60; + // default units is minutes, see directionsTimeAttributeName param + segment.setTravelTime(time); + + QGeoManeuver maneuver; + QString type = attributes.value(kDirectionsFeaturesAttributesManeuverTypeKey).toString(); + maneuver.setDirection(esriDirectionsManeuverTypes.value(type)); + + maneuver.setInstructionText(attributes.value(kDirectionsFeaturesAttributesTextKey).toString() + "."); + maneuver.setDistanceToNextInstruction(length); + maneuver.setTimeToNextInstruction(time); + + segment.setManeuver(maneuver); + + segment.setNextRouteSegment(firstSegment); + firstSegment = segment; + } + geoRoute.setFirstRouteSegment(firstSegment); +} + +void GeoRouteJsonParserEsri::parseRoutes() +{ + QJsonObject routes = m_json.value(kRoutesKey).toObject(); + QJsonArray features = routes.value(kRoutesFeaturesKey).toArray(); + foreach (const QJsonValue &feature, features) + parseRoute(feature.toObject()); +} + +void GeoRouteJsonParserEsri::parseRoute(const QJsonObject &route) +{ + QJsonObject attributes = route.value(kRoutesFeaturesAttributesKey).toObject(); + QGeoRoute &geoRoute = m_routes[attributes.value(kRoutesFeaturesObjectIdKey).toInt()]; + + QJsonObject geometry = route.value(kRoutesFeaturesGeometryKey).toObject(); + QJsonArray paths = geometry.value(kRoutesFeaturesGeometryPathsKey).toArray(); + + if (!paths.isEmpty()) + { + QList geoCoordinates; + foreach (const QJsonValue &value, paths.first().toArray()) // only first polyline? + { + QJsonArray geoCoordinate = value.toArray(); + if (geoCoordinate.size() == 2) // ignore 3rd coordinate + { + geoCoordinates.append(QGeoCoordinate(geoCoordinate[1].toDouble(), + geoCoordinate[0].toDouble())); + } + } + geoRoute.setPath(geoCoordinates); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/georoutejsonparser_esri.h b/src/plugins/geoservices/esri/georoutejsonparser_esri.h new file mode 100644 index 0000000..0511cf4 --- /dev/null +++ b/src/plugins/geoservices/esri/georoutejsonparser_esri.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOROUTEJSONPARSERESRI_H +#define GEOROUTEJSONPARSERESRI_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class GeoRouteJsonParserEsri +{ +public: + GeoRouteJsonParserEsri(const QJsonDocument &document); + + QList routes() const; + bool isValid() const; + QString errorString() const; + +private: + void parseDirections(); + void parseDirection(const QJsonObject &direction); + void parseRoutes(); + void parseRoute(const QJsonObject &route); + + QString m_error; + QMap m_routes; + QJsonObject m_json; +}; + +QT_END_NAMESPACE + +#endif // GEOROUTEJSONPARSERESRI_H diff --git a/src/plugins/geoservices/esri/georoutereply_esri.cpp b/src/plugins/geoservices/esri/georoutereply_esri.cpp new file mode 100644 index 0000000..811ffd0 --- /dev/null +++ b/src/plugins/geoservices/esri/georoutereply_esri.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "georoutereply_esri.h" +#include "georoutejsonparser_esri.h" + +#include + +QT_BEGIN_NAMESPACE + +// JSON reference: http://resources.arcgis.com/en/help/arcgis-rest-api/#/Route_service_with_synchronous_execution/02r300000036000000/ + +GeoRouteReplyEsri::GeoRouteReplyEsri(QNetworkReply *reply, const QGeoRouteRequest &request, + QObject *parent) : + QGeoRouteReply(request, parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +GeoRouteReplyEsri::~GeoRouteReplyEsri() +{ +} + +void GeoRouteReplyEsri::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + GeoRouteJsonParserEsri parser(document); + + if (parser.isValid()) + { + setRoutes(parser.routes()); + setFinished(true); + } else { + setError(QGeoRouteReply::ParseError, parser.errorString()); + } +} + +void GeoRouteReplyEsri::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QGeoRouteReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/georoutereply_esri.h b/src/plugins/geoservices/esri/georoutereply_esri.h new file mode 100644 index 0000000..960c90d --- /dev/null +++ b/src/plugins/geoservices/esri/georoutereply_esri.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOROUTEREPLYESRI_H +#define GEOROUTEREPLYESRI_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class GeoRouteReplyEsri : public QGeoRouteReply +{ + Q_OBJECT + +public: + GeoRouteReplyEsri(QNetworkReply *reply, const QGeoRouteRequest &request, QObject *parent = nullptr); + ~GeoRouteReplyEsri(); + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // GEOROUTEREPLYESRI_H diff --git a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp new file mode 100644 index 0000000..ae722e5 --- /dev/null +++ b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "georoutingmanagerengine_esri.h" +#include "georoutereply_esri.h" + +#include + +QT_BEGIN_NAMESPACE + +static const QString kPrefixEsri(QStringLiteral("esri.")); +static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); +static const QString kParamToken(kPrefixEsri + QStringLiteral("token")); + +static const QString kUrlRouting(QStringLiteral("http://route.arcgis.com/arcgis/rest/services/World/Route/NAServer/Route_World/solve")); + +GeoRoutingManagerEngineEsri::GeoRoutingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) : + QGeoRoutingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(kParamUserAgent)) + m_userAgent = parameters.value(kParamUserAgent).toString().toLatin1(); + else + m_userAgent = QByteArrayLiteral("Qt Location based application"); + + m_token = parameters.value(kParamToken).toString(); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +GeoRoutingManagerEngineEsri::~GeoRoutingManagerEngineEsri() +{ +} + +// REST reference: http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 + +QGeoRouteReply *GeoRoutingManagerEngineEsri::calculateRoute(const QGeoRouteRequest &request) +{ + QNetworkRequest networkRequest; + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QUrl url(kUrlRouting); + QUrlQuery query; + QString stops; + + foreach (const QGeoCoordinate &coordinate, request.waypoints()) + { + if (!stops.isEmpty()) + stops += "; "; + + stops += QString::number(coordinate.longitude()) + QLatin1Char(',') + + QString::number(coordinate.latitude()); + } + + query.addQueryItem(QStringLiteral("stops"), stops); + query.addQueryItem(QStringLiteral("f"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("directionsLanguage"), preferedDirectionLangage()); + query.addQueryItem(QStringLiteral("directionsLengthUnits"), preferedDirectionsLengthUnits()); + query.addQueryItem(QStringLiteral("token"), m_token); + + url.setQuery(query); + networkRequest.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(networkRequest); + GeoRouteReplyEsri *routeReply = new GeoRouteReplyEsri(reply, request, this); + + connect(routeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(routeReply, SIGNAL(error(QGeoRouteReply::Error,QString)), this, SLOT(replyError(QGeoRouteReply::Error,QString))); + + return routeReply; +} + +void GeoRoutingManagerEngineEsri::replyFinished() +{ + QGeoRouteReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void GeoRoutingManagerEngineEsri::replyError(QGeoRouteReply::Error errorCode, const QString &errorString) +{ + QGeoRouteReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QString GeoRoutingManagerEngineEsri::preferedDirectionLangage() +{ + // list of supported langages is defined in: + // http://resources.arcgis.com/en/help/arcgis-rest-api/index.html#//02r300000036000000 + const QStringList supportedLanguages = { + "ar", // Generate directions in Arabic + "cs", // Generate directions in Czech + "de", // Generate directions in German + "el", // Generate directions in Greek + "en", // Generate directions in English (default) + "es", // Generate directions in Spanish + "et", // Generate directions in Estonian + "fr", // Generate directions in French + "he", // Generate directions in Hebrew + "it", // Generate directions in Italian + "ja", // Generate directions in Japanese + "ko", // Generate directions in Korean + "lt", // Generate directions in Lithuanian + "lv", // Generate directions in Latvian + "nl", // Generate directions in Dutch + "pl", // Generate directions in Polish + "pt-BR", // Generate directions in Brazilian Portuguese + "pt-PT", // Generate directions in Portuguese (Portugal) + "ru", // Generate directions in Russian + "sv", // Generate directions in Swedish + "tr", // Generate directions in Turkish + "zh-CN" // Simplified Chinese + }; + + for (const QString &language: locale().uiLanguages()) + { + if (language.startsWith("pt_BR")) // Portuguese (Brazilian) + return QStringLiteral("pt-BR"); + if (language.startsWith("pt")) // Portuguese (Portugal) + return QStringLiteral("pt-PT"); + if (language.startsWith("zh")) // Portuguese (Portugal) + return QStringLiteral("zh-CN"); + + const QString country = language.left(2); + if (supportedLanguages.contains(country)) + return country; + } + return QStringLiteral("en"); // default value +} + +QString GeoRoutingManagerEngineEsri::preferedDirectionsLengthUnits() +{ + switch (measurementSystem()) + { + case QLocale::MetricSystem: + return QStringLiteral("esriNAUMeters"); + break; + case QLocale::ImperialUSSystem: + return QStringLiteral( "esriNAUFeet"); + break; + case QLocale::ImperialUKSystem: + return QStringLiteral("esriNAUFeet"); + break; + default: + return QStringLiteral("esriNAUMeters"); + break; + } + return QStringLiteral("esriNAUMeters"); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h new file mode 100644 index 0000000..17aaa3a --- /dev/null +++ b/src/plugins/geoservices/esri/georoutingmanagerengine_esri.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOROUTINGMANAGERENGINEESRI_H +#define GEOROUTINGMANAGERENGINEESRI_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class GeoRoutingManagerEngineEsri : public QGeoRoutingManagerEngine +{ + Q_OBJECT + +public: + GeoRoutingManagerEngineEsri(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString); + virtual ~GeoRoutingManagerEngineEsri(); + + QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request) override; + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoRouteReply::Error errorCode, const QString &errorString); + +private: + QString preferedDirectionLangage(); + QString preferedDirectionsLengthUnits(); + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_token; +}; + +QT_END_NAMESPACE + +#endif // GEOROUTINGMANAGERENGINEESRI_H diff --git a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp new file mode 100644 index 0000000..0a54e00 --- /dev/null +++ b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geoserviceproviderfactory_esri.h" +#include "geotiledmappingmanagerengine_esri.h" +#include "geocodingmanagerengine_esri.h" +#include "georoutingmanagerengine_esri.h" + +#include + +QT_BEGIN_NAMESPACE + +QGeoCodingManagerEngine *GeoServiceProviderFactoryEsri::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new GeoCodingManagerEngineEsri(parameters, error, errorString); +} + +QGeoMappingManagerEngine *GeoServiceProviderFactoryEsri::createMappingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new GeoTiledMappingManagerEngineEsri(parameters, error, errorString); +} + +QGeoRoutingManagerEngine *GeoServiceProviderFactoryEsri::createRoutingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + const QString token = parameters.value(QStringLiteral("esri.token")).toString(); + + if (!token.isEmpty()) { + return new GeoRoutingManagerEngineEsri(parameters, error, errorString); + } else { + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = tr("Esri plugin requires a 'esri.token' parameter.\n" + "Please visit https://developers.arcgis.com/authentication/accessing-arcgis-online-services/"); + return 0; + } +} + +QPlaceManagerEngine *GeoServiceProviderFactoryEsri::createPlaceManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h new file mode 100644 index 0000000..abd0d59 --- /dev/null +++ b/src/plugins/geoservices/esri/geoserviceproviderfactory_esri.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOSERVICEPROVIDERFACTORYESRI_H +#define GEOSERVICEPROVIDERFACTORYESRI_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class GeoServiceProviderFactoryEsri: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "esri_plugin.json") + +public: + QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const override; + QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const override; + QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const override; + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const override; +}; + +QT_END_NAMESPACE + +#endif // GEOSERVICEPROVIDERFACTORYESRI_H diff --git a/src/plugins/geoservices/esri/geotiledmap_esri.cpp b/src/plugins/geoservices/esri/geotiledmap_esri.cpp new file mode 100644 index 0000000..8feb961 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmap_esri.cpp @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geotiledmap_esri.h" +#include "geotiledmappingmanagerengine_esri.h" + +#include + +QT_BEGIN_NAMESPACE + +GeoTiledMapEsri::GeoTiledMapEsri(GeoTiledMappingManagerEngineEsri *engine, QObject *parent) : + Map(engine, parent), m_engine(engine), m_mapId(-1) +{ +} + +GeoTiledMapEsri::~GeoTiledMapEsri() +{ +} + +void GeoTiledMapEsri::evaluateCopyrights(const QSet &visibleTiles) +{ + if (visibleTiles.isEmpty()) + return; + + QGeoTileSpec tile = *(visibleTiles.constBegin()); + if (tile.mapId() == m_mapId) + return; + + m_mapId = tile.mapId(); + + GeoMapSource *mapSource = engine()->mapSource(m_mapId); + + if (mapSource) + emit copyrightsChanged(mapSource->copyright()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotiledmap_esri.h b/src/plugins/geoservices/esri/geotiledmap_esri.h new file mode 100644 index 0000000..7a21af9 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmap_esri.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOTILEDMAPESRI_H +#define GEOTILEDMAPESRI_H + +#include +#ifdef LOCATIONLABS +#include +typedef QGeoTiledMapLabs Map; +#else +typedef QGeoTiledMap Map; +#endif + +QT_BEGIN_NAMESPACE + +class GeoTiledMappingManagerEngineEsri; + +class GeoTiledMapEsri: public Map +{ + Q_OBJECT + +public: + explicit GeoTiledMapEsri(GeoTiledMappingManagerEngineEsri *engine, QObject *parent = nullptr); + virtual ~GeoTiledMapEsri(); + +protected: + void evaluateCopyrights(const QSet &visibleTiles) override; + + inline GeoTiledMappingManagerEngineEsri *engine() const; + +private: + GeoTiledMappingManagerEngineEsri *m_engine; + int m_mapId; +}; + +inline GeoTiledMappingManagerEngineEsri *GeoTiledMapEsri::engine() const +{ + return m_engine; +} + +QT_END_NAMESPACE + +#endif // GEOTILEDMAPESRI_H diff --git a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp new file mode 100644 index 0000000..3fa9a17 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.cpp @@ -0,0 +1,304 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geotiledmappingmanagerengine_esri.h" +#include "geotiledmap_esri.h" +#include "geotilefetcher_esri.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static void initResources() +{ + Q_INIT_RESOURCE(esri); +} + +QT_BEGIN_NAMESPACE + +static const QString kPrefixEsri(QStringLiteral("esri.")); +static const QString kParamUserAgent(kPrefixEsri + QStringLiteral("useragent")); +static const QString kParamToken(kPrefixEsri + QStringLiteral("token")); +static const QString kPrefixMapping(kPrefixEsri + QStringLiteral("mapping.")); +static const QString kParamMinimumZoomLevel(kPrefixMapping + QStringLiteral("minimumZoomLevel")); +static const QString kParamMaximumZoomLevel(kPrefixMapping + QStringLiteral("maximumZoomLevel")); + +static const QString kPropMapSources(QStringLiteral("mapSources")); +static const QString kPropStyle(QStringLiteral("style")); +static const QString kPropName(QStringLiteral("name")); +static const QString kPropDescription(QStringLiteral("description")); +static const QString kPropMobile(QStringLiteral("mobile")); +static const QString kPropNight(QStringLiteral("night")); +static const QString kPropUrl(QStringLiteral("url")); +static const QString kPropMapId(QStringLiteral("mapId")); +static const QString kPropCopyright(QStringLiteral("copyrightText")); + +GeoTiledMappingManagerEngineEsri::GeoTiledMappingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) : + QGeoTiledMappingManagerEngine() +{ + QGeoCameraCapabilities cameraCaps; + + double minimumZoomLevel = 0; + double maximumZoomLevel = 19; + + if (parameters.contains(kParamMinimumZoomLevel)) + minimumZoomLevel = parameters[kParamMinimumZoomLevel].toDouble(); + + if (parameters.contains(kParamMaximumZoomLevel)) + maximumZoomLevel = parameters[kParamMaximumZoomLevel].toDouble(); + + cameraCaps.setMinimumZoomLevel(minimumZoomLevel); + cameraCaps.setMaximumZoomLevel(maximumZoomLevel); + cameraCaps.setSupportsBearing(true); + cameraCaps.setSupportsTilting(true); + cameraCaps.setMinimumTilt(0); + cameraCaps.setMaximumTilt(80); + cameraCaps.setMinimumFieldOfView(20.0); + cameraCaps.setMaximumFieldOfView(120.0); + cameraCaps.setOverzoomEnabled(true); + setCameraCapabilities(cameraCaps); + + setTileSize(QSize(256, 256)); + + if (!initializeMapSources(error, errorString, cameraCaps)) + return; + + QList mapTypes; + + foreach (GeoMapSource *mapSource, m_mapSources) { + mapTypes << QGeoMapType( + mapSource->style(), + mapSource->name(), + mapSource->description(), + mapSource->mobile(), + mapSource->night(), + mapSource->mapId(), + "esri", + cameraCaps); + } + + setSupportedMapTypes(mapTypes); + + GeoTileFetcherEsri *tileFetcher = new GeoTileFetcherEsri(this); + + if (parameters.contains(kParamUserAgent)) + tileFetcher->setUserAgent(parameters.value(kParamUserAgent).toString().toLatin1()); + + if (parameters.contains(kParamToken)) + tileFetcher->setToken(parameters.value(kParamToken).toString()); + + setTileFetcher(tileFetcher); + + /* TILE CACHE */ + QString cacheDirectory; + if (parameters.contains(QStringLiteral("esri.mapping.cache.directory"))) { + cacheDirectory = parameters.value(QStringLiteral("esri.mapping.cache.directory")).toString(); + } else { + // managerName() is not yet set, we have to hardcode the plugin name below + cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String("esri"); + } + QGeoFileTileCache *tileCache = new QGeoFileTileCache(cacheDirectory); + + /* + * Disk cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("esri.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("esri.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("esri.mapping.cache.disk.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("esri.mapping.cache.disk.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxDiskUsage(cacheSize); + } + + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("esri.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("esri.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("esri.mapping.cache.memory.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("esri.mapping.cache.memory.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxMemoryUsage(cacheSize); + } + + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("esri.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("esri.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("esri.mapping.cache.texture.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("esri.mapping.cache.texture.size")).toString().toInt(&ok); + if (ok) + tileCache->setExtraTextureUsage(cacheSize); + } + + /* PREFETCHING */ + if (parameters.contains(QStringLiteral("esri.mapping.prefetching_style"))) { + const QString prefetchingMode = parameters.value(QStringLiteral("esri.mapping.prefetching_style")).toString(); + if (prefetchingMode == QStringLiteral("TwoNeighbourLayers")) + m_prefetchStyle = QGeoTiledMap::PrefetchTwoNeighbourLayers; + else if (prefetchingMode == QStringLiteral("OneNeighbourLayer")) + m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer; + else if (prefetchingMode == QStringLiteral("NoPrefetching")) + m_prefetchStyle = QGeoTiledMap::NoPrefetching; + } + + setTileCache(tileCache); + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +GeoTiledMappingManagerEngineEsri::~GeoTiledMappingManagerEngineEsri() +{ + qDeleteAll(m_mapSources); +} + +QGeoMap *GeoTiledMappingManagerEngineEsri::createMap() +{ + QGeoTiledMap *map = new GeoTiledMapEsri(this); + map->setPrefetchStyle(m_prefetchStyle); + return map; +} + +// ${z} = Zoom +// ${x} = X +// ${y} = Y +// ${token} = Token + +// template = 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{{z}}/{{y}}/{{x}}.png' + +bool GeoTiledMappingManagerEngineEsri::initializeMapSources(QGeoServiceProvider::Error *error, + QString *errorString, + const QGeoCameraCapabilities &cameraCaps) +{ + initResources(); + QFile mapsFile(":/esri/maps.json"); + + if (!mapsFile.open(QIODevice::ReadOnly)) { + *error = QGeoServiceProvider::NotSupportedError; + *errorString = Q_FUNC_INFO + QStringLiteral("Unable to open: ") + mapsFile.fileName(); + + return false; + } + + QByteArray mapsData = mapsFile.readAll(); + mapsFile.close(); + + QJsonParseError parseError; + + QJsonDocument mapsDocument = QJsonDocument::fromJson(mapsData, &parseError); + + if (!mapsDocument.isObject()) { + *error = QGeoServiceProvider::NotSupportedError; + *errorString = Q_FUNC_INFO + QStringLiteral("JSON error: ") + (int)parseError.error + + ", offset: " + parseError.offset + + ", details: " + parseError.errorString(); + return false; + } + + QVariantMap maps = mapsDocument.object().toVariantMap(); + + QVariantList mapSources = maps["mapSources"].toList(); + + foreach (QVariant mapSourceElement, mapSources) { + QVariantMap mapSource = mapSourceElement.toMap(); + + int mapId = m_mapSources.count() + 1; + + m_mapSources << new GeoMapSource( + GeoMapSource::mapStyle(mapSource[kPropStyle].toString()), + mapSource[kPropName].toString(), + mapSource[kPropDescription].toString(), + mapSource[kPropMobile].toBool(), + mapSource[kPropMapId].toBool(), + mapId, + GeoMapSource::toFormat(mapSource[kPropUrl].toString()), + mapSource[kPropCopyright].toString(), + cameraCaps + ); + } + + return true; +} + +GeoMapSource *GeoTiledMappingManagerEngineEsri::mapSource(int mapId) const +{ + foreach (GeoMapSource *mapSource, mapSources()) { + if (mapSource->mapId() == mapId) + return mapSource; + } + + return nullptr; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h new file mode 100644 index 0000000..6317238 --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmappingmanagerengine_esri.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOTILEDMAPPINGMANAGERENGINEESRI_H +#define GEOTILEDMAPPINGMANAGERENGINEESRI_H + +#include + +#include + +#include "geomapsource.h" + +QT_BEGIN_NAMESPACE + +class GeoTiledMappingManagerEngineEsri : public QGeoTiledMappingManagerEngine +{ + Q_OBJECT + +public: + GeoTiledMappingManagerEngineEsri(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString); + virtual ~GeoTiledMappingManagerEngineEsri(); + + QGeoMap *createMap() override; + + inline const QList& mapSources() const; + GeoMapSource *mapSource(int mapId) const; + +private: + bool initializeMapSources(QGeoServiceProvider::Error *error, QString *errorString, const QGeoCameraCapabilities &cameraCaps); + + QList m_mapSources; +}; + +inline const QList& GeoTiledMappingManagerEngineEsri::mapSources() const +{ + return m_mapSources; +} + +QT_END_NAMESPACE + +#endif // GEOTILEDMAPPINGMANAGERENGINEESRI_H diff --git a/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp new file mode 100644 index 0000000..f4431bf --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmapreply_esri.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geotiledmapreply_esri.h" + +#include + +QT_BEGIN_NAMESPACE + +static const unsigned char pngSignature[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, 0x00}; +static const unsigned char jpegSignature[] = {0xFF, 0xD8, 0xFF, 0x00}; +static const unsigned char gifSignature[] = {0x47, 0x49, 0x46, 0x38, 0x00}; + +GeoTiledMapReplyEsri::GeoTiledMapReplyEsri(QNetworkReply *reply, const QGeoTileSpec &spec, + QObject *parent) : + QGeoTiledMapReply(spec, parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +GeoTiledMapReplyEsri::~GeoTiledMapReplyEsri() +{ +} + +void GeoTiledMapReplyEsri::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QByteArray const& imageData = reply->readAll(); + + bool validFormat = true; + if (imageData.startsWith(reinterpret_cast(pngSignature))) + setMapImageFormat(QStringLiteral("png")); + else if (imageData.startsWith(reinterpret_cast(jpegSignature))) + setMapImageFormat(QStringLiteral("jpg")); + else if (imageData.startsWith(reinterpret_cast(gifSignature))) + setMapImageFormat(QStringLiteral("gif")); + else + validFormat = false; + + if (validFormat) + setMapImageData(imageData); + + setFinished(true); +} + +void GeoTiledMapReplyEsri::networkReplyError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) + setFinished(true); + else + setError(QGeoTiledMapReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotiledmapreply_esri.h b/src/plugins/geoservices/esri/geotiledmapreply_esri.h new file mode 100644 index 0000000..572431d --- /dev/null +++ b/src/plugins/geoservices/esri/geotiledmapreply_esri.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOTILEDMAPREPLYESRI_H +#define GEOTILEDMAPREPLYESRI_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class GeoTiledMapReplyEsri : public QGeoTiledMapReply +{ + Q_OBJECT + +public: + GeoTiledMapReplyEsri(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent = nullptr); + ~GeoTiledMapReplyEsri(); + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // GEOTILEDMAPREPLYESRI_H diff --git a/src/plugins/geoservices/esri/geotilefetcher_esri.cpp b/src/plugins/geoservices/esri/geotilefetcher_esri.cpp new file mode 100644 index 0000000..8ceba37 --- /dev/null +++ b/src/plugins/geoservices/esri/geotilefetcher_esri.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geotilefetcher_esri.h" +#include "geotiledmappingmanagerengine_esri.h" +#include "geotiledmapreply_esri.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +GeoTileFetcherEsri::GeoTileFetcherEsri(QGeoTiledMappingManagerEngine *parent) : + QGeoTileFetcher(parent), m_networkManager(new QNetworkAccessManager(this)), + m_userAgent(QByteArrayLiteral("Qt Location based application")) +{ +} + +QGeoTiledMapReply *GeoTileFetcherEsri::getTileImage(const QGeoTileSpec &spec) +{ + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, userAgent()); + + GeoTiledMappingManagerEngineEsri *engine = qobject_cast( + parent()); + + GeoMapSource *mapSource = engine->mapSource(spec.mapId()); + + if (!mapSource) + qWarning("Unknown mapId %d\n", spec.mapId()); + else + request.setUrl(mapSource->url().arg(spec.zoom()).arg(spec.x()).arg(spec.y())); + + QNetworkReply *reply = m_networkManager->get(request); + + return new GeoTiledMapReplyEsri(reply, spec); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/esri/geotilefetcher_esri.h b/src/plugins/geoservices/esri/geotilefetcher_esri.h new file mode 100644 index 0000000..5702d1c --- /dev/null +++ b/src/plugins/geoservices/esri/geotilefetcher_esri.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2013-2016 Esri +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOTILEFETCHERESRI_H +#define GEOTILEFETCHERESRI_H + +#include + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEngine; +class QNetworkAccessManager; + +class GeoTileFetcherEsri : public QGeoTileFetcher +{ + Q_OBJECT + +public: + explicit GeoTileFetcherEsri(QGeoTiledMappingManagerEngine *parent); + + inline const QByteArray &userAgent() const; + inline void setUserAgent(const QByteArray &userAgent); + + inline const QString &token() const; + inline void setToken(const QString &token); + +private: + QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec) override; + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_token; +}; + +inline const QByteArray &GeoTileFetcherEsri::userAgent() const +{ + return m_userAgent; +} + +inline void GeoTileFetcherEsri::setUserAgent(const QByteArray &userAgent) +{ + m_userAgent = userAgent; +} + +inline const QString &GeoTileFetcherEsri::token() const +{ + return m_token; +} + +inline void GeoTileFetcherEsri::setToken(const QString &token) +{ + m_token = token; +} + +QT_END_NAMESPACE + +#endif // GEOTILEFETCHERESRI_H diff --git a/src/plugins/geoservices/esri/maps.json b/src/plugins/geoservices/esri/maps.json new file mode 100644 index 0000000..8167ae7 --- /dev/null +++ b/src/plugins/geoservices/esri/maps.json @@ -0,0 +1,123 @@ +{ + "mapSources": [ + { + "style": "StreetMap", + "name": "World Street Map", + "description": "ArcGIS Online World Street Map", + "mobile": true, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "SatelliteMapDay", + "name": "World Imagery", + "": "ArcGIS Online World Imagery", + "mobile": true, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "TerrainMap", + "name": "World Terrain Base", + "description": "ArcGIS Online World Terrain Base", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Terrain_Base/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "CustomMap", + "name": "World Topography", + "description": "ArcGIS Online World Topography", + "mobile": true, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "CustomMap", + "name": "USA Topo Maps", + "description": "This map presents land cover and detailed topographic maps for the United States.", + "mobile": true, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/USA_Topo_Maps/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "CustomMap", + "name": "National Geographic World Map", + "description": "National Geographic World Map", + "mobile": false, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "GrayStreetMap", + "name": "Light Gray Canvas", + "description": "Thematic content providing a neutral background with minimal colors", + "mobile": true, + "night": false, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Light_Gray_Base/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "CustomMap", + "name": "World Physical Map", + "description": "Natural Earth physical map for the world", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "CustomMap", + "name": "World Shaded Relief", + "description": "Portrays surface elevation as shaded relief", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "CustomMap", + "name": "World Ocean Base", + "description": "This map is designed to be used as a basemap by marine GIS professionals and as a reference map by anyone interested in ocean data", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/arcgis/rest/services/Ocean/World_Ocean_Base/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "GrayStreetMap", + "name": "Dark Gray Canvas", + "description": "Thematic content providing a neutral background with minimal colors", + "mobile": false, + "night": true, + "url": "http://services.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer", + "copyrightText": "© Esri contributors" + }, + + { + "style": "CustomMap", + "name": "DeLorme World Basemap", + "description": "DeLorme’s topographic basemap is a seamless global data set that portrays transportation, hydrography, jurisdiction boundaries, and major geographic features", + "mobile": false, + "night": false, + "url": "http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer", + "copyrightText": "© Esri contributors" + } + ] +} diff --git a/src/plugins/geoservices/geoservices.pro b/src/plugins/geoservices/geoservices.pro new file mode 100644 index 0000000..b81ad34 --- /dev/null +++ b/src/plugins/geoservices/geoservices.pro @@ -0,0 +1,18 @@ +TEMPLATE = subdirs + +QT_FOR_CONFIG += location-private # pulls in the features defined in configure.json + +qtConfig(geoservices_here): SUBDIRS += nokia +qtConfig(geoservices_mapbox): SUBDIRS += mapbox +qtConfig(geoservices_esri): SUBDIRS += esri +qtConfig(geoservices_itemsoverlay): SUBDIRS += itemsoverlay +qtConfig(geoservices_osm): SUBDIRS += osm + +qtConfig(geoservices_mapboxgl) { + !exists(../../3rdparty/mapbox-gl-native/mapbox-gl-native.pro) { + warning("Submodule mapbox-gl-native does not exist. Run 'git submodule update --init' on qtlocation.") + } else { + SUBDIRS += mapboxgl ../../3rdparty/mapbox-gl-native + mapboxgl.depends = ../../3rdparty/mapbox-gl-native + } +} diff --git a/src/plugins/geoservices/itemsoverlay/itemsoverlay.pro b/src/plugins/geoservices/itemsoverlay/itemsoverlay.pro new file mode 100644 index 0000000..486ed97 --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/itemsoverlay.pro @@ -0,0 +1,24 @@ +TARGET = qtgeoservices_itemsoverlay + +QT += location-private positioning-private + +QT_FOR_CONFIG += location-private +qtConfig(location-labs-plugin): DEFINES += LOCATIONLABS + +HEADERS += \ + qgeomapitemsoverlay.h \ + qgeomappingmanagerengineitemsoverlay.h \ + qgeoserviceproviderpluginitemsoverlay.h + +SOURCES += \ + qgeoserviceproviderpluginitemsoverlay.cpp \ + qgeomappingmanagerengineitemsoverlay.cpp \ + qgeomapitemsoverlay.cpp + +OTHER_FILES += \ + itemsoverlay_plugin.json + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = QGeoServiceProviderFactoryItemsOverlay +load(qt_plugin) + diff --git a/src/plugins/geoservices/itemsoverlay/itemsoverlay_plugin.json b/src/plugins/geoservices/itemsoverlay/itemsoverlay_plugin.json new file mode 100644 index 0000000..8b79082 --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/itemsoverlay_plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["itemsoverlay"], + "Provider": "itemsoverlay", + "Version": 100, + "Experimental": false, + "Features": [ + "OfflineMappingFeature" + ] +} diff --git a/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp b/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp new file mode 100644 index 0000000..af0e263 --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapitemsoverlay.h" +#include "qgeomappingmanagerengineitemsoverlay.h" +#include +#include +#include +#include + +#ifdef LOCATIONLABS +#include +#include +#include +#include +#include +#include +#include +#endif + +QT_BEGIN_NAMESPACE + +class QGeoMapItemsOverlayPrivate : public QGeoMapPrivate +{ + Q_DECLARE_PUBLIC(QGeoMapItemsOverlay) +public: + QGeoMapItemsOverlayPrivate(QGeoMappingManagerEngineItemsOverlay *engine, QGeoMapItemsOverlay *map); + virtual ~QGeoMapItemsOverlayPrivate(); + +#ifdef LOCATIONLABS + QGeoMapObjectPrivate *createMapObjectImplementation(QGeoMapObject *obj) override; + virtual QList mapObjects() const override; + void removeMapObject(QGeoMapObject *obj); + void updateMapObjects(QSGNode *root, QQuickWindow *window); + + QGeoMapObjectQSGSupport m_qsgSupport; +#endif + + void updateObjectsGeometry(); +protected: + void changeViewportSize(const QSize &size) override; + void changeCameraData(const QGeoCameraData &oldCameraData) override; + void changeActiveMapType(const QGeoMapType mapType) override; +}; + +QGeoMapItemsOverlay::QGeoMapItemsOverlay(QGeoMappingManagerEngineItemsOverlay *engine, QObject *parent) + : QGeoMap(*(new QGeoMapItemsOverlayPrivate(engine, this)), parent) +{ + +} + +QGeoMapItemsOverlay::~QGeoMapItemsOverlay() +{ +} + +QGeoMap::Capabilities QGeoMapItemsOverlay::capabilities() const +{ + return Capabilities(SupportsVisibleRegion + | SupportsSetBearing + | SupportsAnchoringCoordinate); +} + +bool QGeoMapItemsOverlay::createMapObjectImplementation(QGeoMapObject *obj) +{ +#ifndef LOCATIONLABS + return false; +#else + Q_D(QGeoMapItemsOverlay); + return d->m_qsgSupport.createMapObjectImplementation(obj, d); +#endif +} + +QSGNode *QGeoMapItemsOverlay::updateSceneGraph(QSGNode *node, QQuickWindow *window) +{ +#ifndef LOCATIONLABS + Q_UNUSED(window) + return node; +#else + Q_D(QGeoMapItemsOverlay); + + QSGRectangleNode *mapRoot = static_cast(node); + if (!mapRoot) + mapRoot = window->createRectangleNode(); + + mapRoot->setRect(QRect(0, 0, viewportWidth(), viewportHeight())); + mapRoot->setColor(QColor(0,0,0,0)); + + d->updateMapObjects(mapRoot, window); + return mapRoot; +#endif +} + +void QGeoMapItemsOverlay::removeMapObject(QGeoMapObject *obj) +{ +#ifdef LOCATIONLABS + Q_D(QGeoMapItemsOverlay); + d->removeMapObject(obj); +#endif +} + +QGeoMapItemsOverlayPrivate::QGeoMapItemsOverlayPrivate(QGeoMappingManagerEngineItemsOverlay *engine, QGeoMapItemsOverlay *map) + : QGeoMapPrivate(engine, new QGeoProjectionWebMercator) +{ + m_qsgSupport.m_map = map; +} + +QGeoMapItemsOverlayPrivate::~QGeoMapItemsOverlayPrivate() +{ +} + +#ifdef LOCATIONLABS +QGeoMapObjectPrivate *QGeoMapItemsOverlayPrivate::createMapObjectImplementation(QGeoMapObject *obj) +{ + return m_qsgSupport.createMapObjectImplementationPrivate(obj); +} + +QList QGeoMapItemsOverlayPrivate::mapObjects() const +{ + return m_qsgSupport.mapObjects(); +} + +void QGeoMapItemsOverlayPrivate::removeMapObject(QGeoMapObject *obj) +{ + m_qsgSupport.removeMapObject(obj); +} + +void QGeoMapItemsOverlayPrivate::updateMapObjects(QSGNode *root, QQuickWindow *window) +{ + m_qsgSupport.updateMapObjects(root, window); +} +#endif + +void QGeoMapItemsOverlayPrivate::updateObjectsGeometry() +{ +#ifdef LOCATIONLABS + m_qsgSupport.updateObjectsGeometry(); +#endif +} + +void QGeoMapItemsOverlayPrivate::changeViewportSize(const QSize &/*size*/) +{ + updateObjectsGeometry(); +} + +void QGeoMapItemsOverlayPrivate::changeCameraData(const QGeoCameraData &/*oldCameraData*/) +{ + updateObjectsGeometry(); +} + +void QGeoMapItemsOverlayPrivate::changeActiveMapType(const QGeoMapType /*mapType*/) +{ + updateObjectsGeometry(); +} + +QT_END_NAMESPACE + + + + diff --git a/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.h b/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.h new file mode 100644 index 0000000..1594ffb --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/qgeomapitemsoverlay.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGEOMAPITEMSOVERLAY_H +#define QGEOMAPITEMSOVERLAY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMappingManagerEngineItemsOverlay; +class QGeoMapItemsOverlayPrivate; +class QGeoMapItemsOverlay: public QGeoMap +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoMapItemsOverlay) +public: + QGeoMapItemsOverlay(QGeoMappingManagerEngineItemsOverlay *engine, QObject *parent); + virtual ~QGeoMapItemsOverlay(); + + QGeoMap::Capabilities capabilities() const override; + bool createMapObjectImplementation(QGeoMapObject *obj) override; + void removeMapObject(QGeoMapObject *obj) override; + +protected: + QSGNode *updateSceneGraph(QSGNode *node, QQuickWindow *window) override; + +private: + Q_DISABLE_COPY(QGeoMapItemsOverlay) +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.cpp b/src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.cpp new file mode 100644 index 0000000..c19815e --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomappingmanagerengineitemsoverlay.h" + +#include +#include +#include "qgeomapitemsoverlay.h" + +QT_BEGIN_NAMESPACE + + + +QGeoMappingManagerEngineItemsOverlay::QGeoMappingManagerEngineItemsOverlay(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) +: QGeoMappingManagerEngine() +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + QGeoCameraCapabilities cameraCaps; + cameraCaps.setMinimumZoomLevel(0.0); + cameraCaps.setMaximumZoomLevel(30.0); + cameraCaps.setSupportsBearing(true); + cameraCaps.setSupportsTilting(true); + cameraCaps.setMinimumTilt(0); + cameraCaps.setMaximumTilt(89); + cameraCaps.setMinimumFieldOfView(1.0); + cameraCaps.setMaximumFieldOfView(179.0); + setCameraCapabilities(cameraCaps); + + QList mapTypes; + mapTypes << QGeoMapType(QGeoMapType::NoMap, tr("Empty Map"), tr("Empty Map"), false, false, 1, "itemsoverlay", cameraCaps); + setSupportedMapTypes(mapTypes); + + engineInitialized(); +} + +QGeoMappingManagerEngineItemsOverlay::~QGeoMappingManagerEngineItemsOverlay() +{ +} + +QGeoMap *QGeoMappingManagerEngineItemsOverlay::createMap() +{ + return new QGeoMapItemsOverlay(this, this); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.h b/src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.h new file mode 100644 index 0000000..02bc300 --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/qgeomappingmanagerengineitemsoverlay.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPPINGMANAGERENGINEITEMSOVERLAY_H +#define QGEOMAPPINGMANAGERENGINEITEMSOVERLAY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMappingManagerEngineItemsOverlay : public QGeoMappingManagerEngine +{ + Q_OBJECT + +public: + QGeoMappingManagerEngineItemsOverlay(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString); + ~QGeoMappingManagerEngineItemsOverlay(); + + QGeoMap *createMap() override; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPPINGMANAGERENGINENOMAP_H diff --git a/src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.cpp b/src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.cpp new file mode 100644 index 0000000..b83a5eb --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderpluginitemsoverlay.h" +#include "qgeomappingmanagerengineitemsoverlay.h" + +QT_BEGIN_NAMESPACE + +QGeoCodingManagerEngine *QGeoServiceProviderFactoryItemsOverlay::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +QGeoMappingManagerEngine *QGeoServiceProviderFactoryItemsOverlay::createMappingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new QGeoMappingManagerEngineItemsOverlay(parameters, error, errorString); +} + +QGeoRoutingManagerEngine *QGeoServiceProviderFactoryItemsOverlay::createRoutingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +QPlaceManagerEngine *QGeoServiceProviderFactoryItemsOverlay::createPlaceManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.h b/src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.h new file mode 100644 index 0000000..8ed1da8 --- /dev/null +++ b/src/plugins/geoservices/itemsoverlay/qgeoserviceproviderpluginitemsoverlay.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_ITEMSOVERLAY_H +#define QGEOSERVICEPROVIDER_ITEMSOVERLAY_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoServiceProviderFactoryItemsOverlay: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "itemsoverlay_plugin.json") + +public: + QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/LICENSE.txt b/src/plugins/geoservices/mapbox/maki-4.0.0/LICENSE.txt new file mode 100644 index 0000000..670154e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/LICENSE.txt @@ -0,0 +1,116 @@ +CC0 1.0 Universal + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator and +subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for the +purpose of contributing to a commons of creative, cultural and scientific +works ("Commons") that the public can reliably and without fear of later +claims of infringement build upon, modify, incorporate in other works, reuse +and redistribute as freely as possible in any form whatsoever and for any +purposes, including without limitation commercial purposes. These owners may +contribute to the Commons to promote the ideal of a free culture and the +further production of creative, cultural and scientific works, or to gain +reputation or greater distribution for their Work in part through the use and +efforts of others. + +For these and/or other purposes and motivations, and without any expectation +of additional consideration or compensation, the person associating CC0 with a +Work (the "Affirmer"), to the extent that he or she is an owner of Copyright +and Related Rights in the Work, voluntarily elects to apply CC0 to the Work +and publicly distribute the Work under its terms, with knowledge of his or her +Copyright and Related Rights in the Work and the meaning and intended legal +effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not limited +to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, communicate, + and translate a Work; + + ii. moral rights retained by the original author(s) and/or performer(s); + + iii. publicity and privacy rights pertaining to a person's image or likeness + depicted in a Work; + + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + + v. rights protecting the extraction, dissemination, use and reuse of data in + a Work; + + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation thereof, + including any amended or successor version of such directive); and + + vii. other similar, equivalent or corresponding rights throughout the world + based on applicable law or treaty, and any national implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention of, +applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and +unconditionally waives, abandons, and surrenders all of Affirmer's Copyright +and Related Rights and associated claims and causes of action, whether now +known or unknown (including existing as well as future claims and causes of +action), in the Work (i) in all territories worldwide, (ii) for the maximum +duration provided by applicable law or treaty (including future time +extensions), (iii) in any current or future medium and for any number of +copies, and (iv) for any purpose whatsoever, including without limitation +commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes +the Waiver for the benefit of each member of the public at large and to the +detriment of Affirmer's heirs and successors, fully intending that such Waiver +shall not be subject to revocation, rescission, cancellation, termination, or +any other legal or equitable action to disrupt the quiet enjoyment of the Work +by the public as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason be +judged legally invalid or ineffective under applicable law, then the Waiver +shall be preserved to the maximum extent permitted taking into account +Affirmer's express Statement of Purpose. In addition, to the extent the Waiver +is so judged Affirmer hereby grants to each affected person a royalty-free, +non transferable, non sublicensable, non exclusive, irrevocable and +unconditional license to exercise Affirmer's Copyright and Related Rights in +the Work (i) in all territories worldwide, (ii) for the maximum duration +provided by applicable law or treaty (including future time extensions), (iii) +in any current or future medium and for any number of copies, and (iv) for any +purpose whatsoever, including without limitation commercial, advertising or +promotional purposes (the "License"). The License shall be deemed effective as +of the date CC0 was applied by Affirmer to the Work. Should any part of the +License for any reason be judged legally invalid or ineffective under +applicable law, such partial invalidity or ineffectiveness shall not +invalidate the remainder of the License, and in such case Affirmer hereby +affirms that he or she will not (i) exercise any of his or her remaining +Copyright and Related Rights in the Work or (ii) assert any associated claims +and causes of action with respect to the Work, in either case contrary to +Affirmer's express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + + b. Affirmer offers the Work as-is and makes no representations or warranties + of any kind concerning the Work, express, implied, statutory or otherwise, + including without limitation warranties of title, merchantability, fitness + for a particular purpose, non infringement, or the absence of latent or + other defects, accuracy, or the present or absence of errors, whether or not + discoverable, all to the greatest extent permissible under applicable law. + + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without limitation + any person's Copyright and Related Rights in the Work. Further, Affirmer + disclaims responsibility for obtaining any necessary consents, permissions + or other rights required for any use of the Work. + + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to this + CC0 or use of the Work. + +For more information, please see + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/aerialway.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/aerialway.svg new file mode 100644 index 0000000..3997c5c --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/aerialway.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/airfield.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/airfield.svg new file mode 100644 index 0000000..0c96da6 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/airfield.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/airport.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/airport.svg new file mode 100644 index 0000000..75c31b3 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/airport.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/alcohol-shop.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/alcohol-shop.svg new file mode 100644 index 0000000..f61dd9a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/alcohol-shop.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/america-football.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/america-football.svg new file mode 100644 index 0000000..31003ea --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/america-football.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/amusement-park.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/amusement-park.svg new file mode 100644 index 0000000..4841cb4 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/amusement-park.svg @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/aquarium.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/aquarium.svg new file mode 100644 index 0000000..1a5c645 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/aquarium.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/art-gallery.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/art-gallery.svg new file mode 100644 index 0000000..aed0ca2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/art-gallery.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/attraction.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/attraction.svg new file mode 100644 index 0000000..1345b6f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/attraction.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/bakery.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/bakery.svg new file mode 100644 index 0000000..28d8081 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/bakery.svg @@ -0,0 +1,15 @@ + + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/bank.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/bank.svg new file mode 100644 index 0000000..1efa9ed --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/bank.svg @@ -0,0 +1,19 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/bar.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/bar.svg new file mode 100644 index 0000000..6333311 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/bar.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/barrier.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/barrier.svg new file mode 100644 index 0000000..1a0eb4d --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/barrier.svg @@ -0,0 +1,4 @@ + + barrier-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/baseball.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/baseball.svg new file mode 100644 index 0000000..572bc0b --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/baseball.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/basketball.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/basketball.svg new file mode 100644 index 0000000..eb98a24 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/basketball.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/bbq.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/bbq.svg new file mode 100644 index 0000000..3865e7e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/bbq.svg @@ -0,0 +1,17 @@ + + grill-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/beer.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/beer.svg new file mode 100644 index 0000000..04fbce5 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/beer.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/bicycle-share.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/bicycle-share.svg new file mode 100644 index 0000000..15fbbe2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/bicycle-share.svg @@ -0,0 +1,20 @@ + + + + + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/bicycle.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/bicycle.svg new file mode 100644 index 0000000..4e7cf64 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/bicycle.svg @@ -0,0 +1,21 @@ + + + + + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/blood-bank.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/blood-bank.svg new file mode 100644 index 0000000..b1a1b11 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/blood-bank.svg @@ -0,0 +1,8 @@ + + + +blood-bank-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/buddhism.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/buddhism.svg new file mode 100644 index 0000000..83b222c --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/buddhism.svg @@ -0,0 +1,24 @@ + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/building-alt1.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/building-alt1.svg new file mode 100644 index 0000000..e9ff5ab --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/building-alt1.svg @@ -0,0 +1,7 @@ + + + +buildings + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/building.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/building.svg new file mode 100644 index 0000000..3c12da7 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/building.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/bus.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/bus.svg new file mode 100644 index 0000000..49dca84 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/bus.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/cafe.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/cafe.svg new file mode 100644 index 0000000..d333ac8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/cafe.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/campsite.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/campsite.svg new file mode 100644 index 0000000..74cbe9f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/campsite.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/car.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/car.svg new file mode 100644 index 0000000..9e08b70 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/car.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/castle.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/castle.svg new file mode 100644 index 0000000..f3994d8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/castle.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/cemetery.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/cemetery.svg new file mode 100644 index 0000000..8ae9103 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/cemetery.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/cinema.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/cinema.svg new file mode 100644 index 0000000..b38733e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/cinema.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/circle-stroked.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/circle-stroked.svg new file mode 100644 index 0000000..92997dd --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/circle-stroked.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/circle.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/circle.svg new file mode 100644 index 0000000..cf07ba4 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/circle.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/city.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/city.svg new file mode 100644 index 0000000..08cb18e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/city.svg @@ -0,0 +1,14 @@ + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/clothing-store.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/clothing-store.svg new file mode 100644 index 0000000..63fb1ab --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/clothing-store.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/college.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/college.svg new file mode 100644 index 0000000..f584f73 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/college.svg @@ -0,0 +1,5 @@ + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/commercial.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/commercial.svg new file mode 100644 index 0000000..316a578 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/commercial.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/cricket.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/cricket.svg new file mode 100644 index 0000000..80230cb --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/cricket.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/cross.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/cross.svg new file mode 100644 index 0000000..7d302d8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/cross.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/dam.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/dam.svg new file mode 100644 index 0000000..a9be122 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/dam.svg @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/danger.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/danger.svg new file mode 100644 index 0000000..e003692 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/danger.svg @@ -0,0 +1,18 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/defibrillator.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/defibrillator.svg new file mode 100644 index 0000000..559174f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/defibrillator.svg @@ -0,0 +1,4 @@ + + defibrillator-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/dentist.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/dentist.svg new file mode 100644 index 0000000..743f907 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/dentist.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/doctor.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/doctor.svg new file mode 100644 index 0000000..da88245 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/doctor.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/dog-park.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/dog-park.svg new file mode 100644 index 0000000..63735f9 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/dog-park.svg @@ -0,0 +1,49 @@ + + + +image/svg+xml diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/drinking-water.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/drinking-water.svg new file mode 100644 index 0000000..e3b4e00 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/drinking-water.svg @@ -0,0 +1,5 @@ + + drinking-water-15 + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/embassy.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/embassy.svg new file mode 100644 index 0000000..bc3ea29 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/embassy.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/emergency-phone.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/emergency-phone.svg new file mode 100644 index 0000000..8b25a3a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/emergency-phone.svg @@ -0,0 +1,4 @@ + + emergency-phone-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/entrance-alt1.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/entrance-alt1.svg new file mode 100644 index 0000000..ec98d6f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/entrance-alt1.svg @@ -0,0 +1,4 @@ + + entrance-alt1-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/entrance.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/entrance.svg new file mode 100644 index 0000000..39cf2c3 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/entrance.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/farm.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/farm.svg new file mode 100644 index 0000000..b051c23 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/farm.svg @@ -0,0 +1,4 @@ + + farm-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/fast-food.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/fast-food.svg new file mode 100644 index 0000000..56a74d5 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/fast-food.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/fence.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/fence.svg new file mode 100644 index 0000000..eba92a7 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/fence.svg @@ -0,0 +1,4 @@ + + fence-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/ferry.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/ferry.svg new file mode 100644 index 0000000..4f1c159 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/ferry.svg @@ -0,0 +1,19 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/fire-station.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/fire-station.svg new file mode 100644 index 0000000..ee54ad2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/fire-station.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/florist.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/florist.svg new file mode 100644 index 0000000..7dcba9e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/florist.svg @@ -0,0 +1,4 @@ + + florist-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/fuel.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/fuel.svg new file mode 100644 index 0000000..ae082de --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/fuel.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/gaming.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/gaming.svg new file mode 100644 index 0000000..5a091d7 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/gaming.svg @@ -0,0 +1,11 @@ + + + +gaming + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/garden-center.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/garden-center.svg new file mode 100644 index 0000000..573f36a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/garden-center.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/garden.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/garden.svg new file mode 100644 index 0000000..2717b34 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/garden.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/gift.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/gift.svg new file mode 100644 index 0000000..3336ecb --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/gift.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/golf.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/golf.svg new file mode 100644 index 0000000..5a028ac --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/golf.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/grocery.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/grocery.svg new file mode 100644 index 0000000..de1448e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/grocery.svg @@ -0,0 +1,48 @@ + + + +image/svg+xml diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/hairdresser.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/hairdresser.svg new file mode 100644 index 0000000..2c92a56 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/hairdresser.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/harbor.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/harbor.svg new file mode 100644 index 0000000..ec120af --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/harbor.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/heart.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/heart.svg new file mode 100644 index 0000000..2341130 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/heart.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/heliport.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/heliport.svg new file mode 100644 index 0000000..dc7f59d --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/heliport.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/home.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/home.svg new file mode 100644 index 0000000..1c3578c --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/home.svg @@ -0,0 +1,8 @@ + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/horse-riding.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/horse-riding.svg new file mode 100644 index 0000000..6aeb88a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/horse-riding.svg @@ -0,0 +1,4 @@ + + horse-riding-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/hospital.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/hospital.svg new file mode 100644 index 0000000..dde77b9 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/hospital.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/ice-cream.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/ice-cream.svg new file mode 100644 index 0000000..4cb0a22 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/ice-cream.svg @@ -0,0 +1,11 @@ + + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/industry.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/industry.svg new file mode 100644 index 0000000..683d4c8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/industry.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/information.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/information.svg new file mode 100644 index 0000000..2ffda2e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/information.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/karaoke.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/karaoke.svg new file mode 100644 index 0000000..7641f08 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/karaoke.svg @@ -0,0 +1,7 @@ + + karaoke + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/landmark.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/landmark.svg new file mode 100644 index 0000000..4cf48cf --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/landmark.svg @@ -0,0 +1,9 @@ + + + + +landmark + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/landuse.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/landuse.svg new file mode 100644 index 0000000..a46b3fe --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/landuse.svg @@ -0,0 +1,4 @@ + + landuse-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/laundry.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/laundry.svg new file mode 100644 index 0000000..2a6810e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/laundry.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/library.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/library.svg new file mode 100644 index 0000000..8dd4084 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/library.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/lighthouse.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/lighthouse.svg new file mode 100644 index 0000000..d3bedc9 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/lighthouse.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/lodging.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/lodging.svg new file mode 100644 index 0000000..6feed37 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/lodging.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/logging.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/logging.svg new file mode 100644 index 0000000..d6c3be8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/logging.svg @@ -0,0 +1,4 @@ + + logging-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/marker-stroked.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/marker-stroked.svg new file mode 100644 index 0000000..d4e732b --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/marker-stroked.svg @@ -0,0 +1,4 @@ + + marker-stroked-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/marker.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/marker.svg new file mode 100644 index 0000000..1010dc8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/marker.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/mobile-phone.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/mobile-phone.svg new file mode 100644 index 0000000..c06ab69 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/mobile-phone.svg @@ -0,0 +1,4 @@ + + mobile-phone-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/monument.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/monument.svg new file mode 100644 index 0000000..1af6353 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/monument.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/mountain.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/mountain.svg new file mode 100644 index 0000000..5f5e5c4 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/mountain.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/museum.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/museum.svg new file mode 100644 index 0000000..f087f1d --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/museum.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/music.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/music.svg new file mode 100644 index 0000000..ebdba36 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/music.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/natural.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/natural.svg new file mode 100644 index 0000000..4e177ac --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/natural.svg @@ -0,0 +1,4 @@ + + natural-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/park-alt1.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/park-alt1.svg new file mode 100644 index 0000000..c13adae --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/park-alt1.svg @@ -0,0 +1,4 @@ + + park-alt1-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/park.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/park.svg new file mode 100644 index 0000000..4f208cc --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/park.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/parking-garage.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/parking-garage.svg new file mode 100644 index 0000000..d7c0b9a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/parking-garage.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/parking.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/parking.svg new file mode 100644 index 0000000..d4370e2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/parking.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/pharmacy.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/pharmacy.svg new file mode 100644 index 0000000..2efd6fa --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/pharmacy.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/picnic-site.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/picnic-site.svg new file mode 100644 index 0000000..87b49bd --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/picnic-site.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/pitch.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/pitch.svg new file mode 100644 index 0000000..2e2e3c9 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/pitch.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/place-of-worship.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/place-of-worship.svg new file mode 100644 index 0000000..d77ce06 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/place-of-worship.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/playground.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/playground.svg new file mode 100644 index 0000000..80d8866 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/playground.svg @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/police.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/police.svg new file mode 100644 index 0000000..be3c38f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/police.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/post.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/post.svg new file mode 100644 index 0000000..27fd010 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/post.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/prison.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/prison.svg new file mode 100644 index 0000000..e680b48 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/prison.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/rail-light.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/rail-light.svg new file mode 100644 index 0000000..6a67b19 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/rail-light.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/rail-metro.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/rail-metro.svg new file mode 100644 index 0000000..530da88 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/rail-metro.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/rail.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/rail.svg new file mode 100644 index 0000000..f57601e --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/rail.svg @@ -0,0 +1,49 @@ + + + +image/svg+xml diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/ranger-station.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/ranger-station.svg new file mode 100644 index 0000000..ba17035 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/ranger-station.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/recycling.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/recycling.svg new file mode 100644 index 0000000..c48d3d2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/recycling.svg @@ -0,0 +1,10 @@ + + recycling-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/religious-christian.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/religious-christian.svg new file mode 100644 index 0000000..1f9c7de --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/religious-christian.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/religious-jewish.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/religious-jewish.svg new file mode 100644 index 0000000..01963d2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/religious-jewish.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/religious-muslim.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/religious-muslim.svg new file mode 100644 index 0000000..214578b --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/religious-muslim.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/residential-community.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/residential-community.svg new file mode 100644 index 0000000..af5f8a2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/residential-community.svg @@ -0,0 +1,8 @@ + + + +buildings + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/restaurant.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/restaurant.svg new file mode 100644 index 0000000..e9af25b --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/restaurant.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/roadblock.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/roadblock.svg new file mode 100644 index 0000000..b6ac056 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/roadblock.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/rocket.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/rocket.svg new file mode 100644 index 0000000..687829f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/rocket.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/school.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/school.svg new file mode 100644 index 0000000..c2fba7c --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/school.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/scooter.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/scooter.svg new file mode 100644 index 0000000..1282927 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/scooter.svg @@ -0,0 +1,4 @@ + + scooter-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/shelter.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/shelter.svg new file mode 100644 index 0000000..8402fc4 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/shelter.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/shop.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/shop.svg new file mode 100644 index 0000000..321bf46 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/shop.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/skiing.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/skiing.svg new file mode 100644 index 0000000..d34597f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/skiing.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/slaughterhouse.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/slaughterhouse.svg new file mode 100644 index 0000000..f567985 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/slaughterhouse.svg @@ -0,0 +1,4 @@ + + slaughterhouse-15-01 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/snowmobile.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/snowmobile.svg new file mode 100644 index 0000000..428bd0c --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/snowmobile.svg @@ -0,0 +1,4 @@ + + snowmobile-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/soccer.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/soccer.svg new file mode 100644 index 0000000..374a2a0 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/soccer.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/square-stroked.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/square-stroked.svg new file mode 100644 index 0000000..bcc7119 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/square-stroked.svg @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/square.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/square.svg new file mode 100644 index 0000000..f1bddb2 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/square.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/stadium.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/stadium.svg new file mode 100644 index 0000000..a57cc53 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/stadium.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/star-stroked.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/star-stroked.svg new file mode 100644 index 0000000..f7847a6 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/star-stroked.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/star.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/star.svg new file mode 100644 index 0000000..8732de8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/star.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/suitcase.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/suitcase.svg new file mode 100644 index 0000000..c55d73c --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/suitcase.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/sushi.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/sushi.svg new file mode 100644 index 0000000..a99b528 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/sushi.svg @@ -0,0 +1,19 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/swimming.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/swimming.svg new file mode 100644 index 0000000..7227a4d --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/swimming.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/teahouse.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/teahouse.svg new file mode 100644 index 0000000..974ab1a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/teahouse.svg @@ -0,0 +1,8 @@ + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/telephone.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/telephone.svg new file mode 100644 index 0000000..a7dcf2f --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/telephone.svg @@ -0,0 +1,4 @@ + + telephone-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/tennis.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/tennis.svg new file mode 100644 index 0000000..0217e2a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/tennis.svg @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/theatre.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/theatre.svg new file mode 100644 index 0000000..3cf857a --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/theatre.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/toilet.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/toilet.svg new file mode 100644 index 0000000..b7f306b --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/toilet.svg @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/town-hall.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/town-hall.svg new file mode 100644 index 0000000..697b178 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/town-hall.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/town.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/town.svg new file mode 100644 index 0000000..ff8cb99 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/town.svg @@ -0,0 +1,4 @@ + + town-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/triangle-stroked.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/triangle-stroked.svg new file mode 100644 index 0000000..c87ff1d --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/triangle-stroked.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/triangle.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/triangle.svg new file mode 100644 index 0000000..75ffea8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/triangle.svg @@ -0,0 +1,11 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/veterinary.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/veterinary.svg new file mode 100644 index 0000000..1a34459 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/veterinary.svg @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/village.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/village.svg new file mode 100644 index 0000000..b06d0f0 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/village.svg @@ -0,0 +1,4 @@ + + village-15 + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/volcano.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/volcano.svg new file mode 100644 index 0000000..e196f69 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/volcano.svg @@ -0,0 +1,16 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/warehouse.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/warehouse.svg new file mode 100644 index 0000000..89d7794 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/warehouse.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/waste-basket.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/waste-basket.svg new file mode 100644 index 0000000..f128ef1 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/waste-basket.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/water.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/water.svg new file mode 100644 index 0000000..0fb64f8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/water.svg @@ -0,0 +1,7 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/wetland.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/wetland.svg new file mode 100644 index 0000000..5acc214 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/wetland.svg @@ -0,0 +1,21 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/wheelchair.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/wheelchair.svg new file mode 100644 index 0000000..3236ea5 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/wheelchair.svg @@ -0,0 +1,17 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/maki-4.0.0/zoo.svg b/src/plugins/geoservices/mapbox/maki-4.0.0/zoo.svg new file mode 100644 index 0000000..975cc85 --- /dev/null +++ b/src/plugins/geoservices/mapbox/maki-4.0.0/zoo.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/plugins/geoservices/mapbox/mapbox.pro b/src/plugins/geoservices/mapbox/mapbox.pro new file mode 100644 index 0000000..c4e7f67 --- /dev/null +++ b/src/plugins/geoservices/mapbox/mapbox.pro @@ -0,0 +1,47 @@ +TARGET = qtgeoservices_mapbox + +QT += location-private positioning-private network + +QT_FOR_CONFIG += location-private +qtConfig(location-labs-plugin): DEFINES += LOCATIONLABS + +HEADERS += \ + qgeoserviceproviderpluginmapbox.h \ + qgeotiledmappingmanagerenginemapbox.h \ + qgeotilefetchermapbox.h \ + qgeomapreplymapbox.h \ + qgeofiletilecachemapbox.h \ + qgeoroutingmanagerenginemapbox.h \ + qgeoroutereplymapbox.h \ + qplacecategoriesreplymapbox.h \ + qplacemanagerenginemapbox.h \ + qplacesearchsuggestionreplymapbox.h \ + qplacesearchreplymapbox.h \ + qgeocodingmanagerenginemapbox.h \ + qgeocodereplymapbox.h \ + qmapboxcommon.h + +SOURCES += \ + qgeoserviceproviderpluginmapbox.cpp \ + qgeotiledmappingmanagerenginemapbox.cpp \ + qgeotilefetchermapbox.cpp \ + qgeomapreplymapbox.cpp \ + qgeofiletilecachemapbox.cpp \ + qgeoroutingmanagerenginemapbox.cpp \ + qgeoroutereplymapbox.cpp \ + qplacecategoriesreplymapbox.cpp \ + qplacemanagerenginemapbox.cpp \ + qplacesearchsuggestionreplymapbox.cpp \ + qplacesearchreplymapbox.cpp \ + qgeocodingmanagerenginemapbox.cpp \ + qgeocodereplymapbox.cpp \ + qmapboxcommon.cpp + +RESOURCES += mapbox.qrc + +OTHER_FILES += \ + mapbox_plugin.json + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = QGeoServiceProviderFactoryMapbox +load(qt_plugin) diff --git a/src/plugins/geoservices/mapbox/mapbox.qrc b/src/plugins/geoservices/mapbox/mapbox.qrc new file mode 100644 index 0000000..217e27a --- /dev/null +++ b/src/plugins/geoservices/mapbox/mapbox.qrc @@ -0,0 +1,150 @@ + + + maki-4.0.0/aerialway.svg + maki-4.0.0/airfield.svg + maki-4.0.0/airport.svg + maki-4.0.0/alcohol-shop.svg + maki-4.0.0/america-football.svg + maki-4.0.0/amusement-park.svg + maki-4.0.0/aquarium.svg + maki-4.0.0/art-gallery.svg + maki-4.0.0/attraction.svg + maki-4.0.0/bakery.svg + maki-4.0.0/bank.svg + maki-4.0.0/barrier.svg + maki-4.0.0/bar.svg + maki-4.0.0/baseball.svg + maki-4.0.0/basketball.svg + maki-4.0.0/bbq.svg + maki-4.0.0/beer.svg + maki-4.0.0/bicycle-share.svg + maki-4.0.0/bicycle.svg + maki-4.0.0/blood-bank.svg + maki-4.0.0/buddhism.svg + maki-4.0.0/building-alt1.svg + maki-4.0.0/building.svg + maki-4.0.0/bus.svg + maki-4.0.0/cafe.svg + maki-4.0.0/campsite.svg + maki-4.0.0/car.svg + maki-4.0.0/castle.svg + maki-4.0.0/cemetery.svg + maki-4.0.0/cinema.svg + maki-4.0.0/circle-stroked.svg + maki-4.0.0/circle.svg + maki-4.0.0/city.svg + maki-4.0.0/clothing-store.svg + maki-4.0.0/college.svg + maki-4.0.0/commercial.svg + maki-4.0.0/cricket.svg + maki-4.0.0/cross.svg + maki-4.0.0/dam.svg + maki-4.0.0/danger.svg + maki-4.0.0/defibrillator.svg + maki-4.0.0/dentist.svg + maki-4.0.0/doctor.svg + maki-4.0.0/dog-park.svg + maki-4.0.0/drinking-water.svg + maki-4.0.0/embassy.svg + maki-4.0.0/emergency-phone.svg + maki-4.0.0/entrance-alt1.svg + maki-4.0.0/entrance.svg + maki-4.0.0/farm.svg + maki-4.0.0/fast-food.svg + maki-4.0.0/fence.svg + maki-4.0.0/ferry.svg + maki-4.0.0/fire-station.svg + maki-4.0.0/florist.svg + maki-4.0.0/fuel.svg + maki-4.0.0/gaming.svg + maki-4.0.0/garden-center.svg + maki-4.0.0/garden.svg + maki-4.0.0/gift.svg + maki-4.0.0/golf.svg + maki-4.0.0/grocery.svg + maki-4.0.0/hairdresser.svg + maki-4.0.0/harbor.svg + maki-4.0.0/heart.svg + maki-4.0.0/heliport.svg + maki-4.0.0/home.svg + maki-4.0.0/horse-riding.svg + maki-4.0.0/hospital.svg + maki-4.0.0/ice-cream.svg + maki-4.0.0/industry.svg + maki-4.0.0/information.svg + maki-4.0.0/karaoke.svg + maki-4.0.0/landmark.svg + maki-4.0.0/landuse.svg + maki-4.0.0/laundry.svg + maki-4.0.0/library.svg + maki-4.0.0/lighthouse.svg + maki-4.0.0/lodging.svg + maki-4.0.0/logging.svg + maki-4.0.0/marker-stroked.svg + maki-4.0.0/marker.svg + maki-4.0.0/mobile-phone.svg + maki-4.0.0/monument.svg + maki-4.0.0/mountain.svg + maki-4.0.0/museum.svg + maki-4.0.0/music.svg + maki-4.0.0/natural.svg + maki-4.0.0/park-alt1.svg + maki-4.0.0/parking-garage.svg + maki-4.0.0/parking.svg + maki-4.0.0/park.svg + maki-4.0.0/pharmacy.svg + maki-4.0.0/picnic-site.svg + maki-4.0.0/pitch.svg + maki-4.0.0/place-of-worship.svg + maki-4.0.0/playground.svg + maki-4.0.0/police.svg + maki-4.0.0/post.svg + maki-4.0.0/prison.svg + maki-4.0.0/rail-light.svg + maki-4.0.0/rail-metro.svg + maki-4.0.0/rail.svg + maki-4.0.0/ranger-station.svg + maki-4.0.0/recycling.svg + maki-4.0.0/religious-christian.svg + maki-4.0.0/religious-jewish.svg + maki-4.0.0/religious-muslim.svg + maki-4.0.0/residential-community.svg + maki-4.0.0/restaurant.svg + maki-4.0.0/roadblock.svg + maki-4.0.0/rocket.svg + maki-4.0.0/school.svg + maki-4.0.0/scooter.svg + maki-4.0.0/shelter.svg + maki-4.0.0/shop.svg + maki-4.0.0/skiing.svg + maki-4.0.0/slaughterhouse.svg + maki-4.0.0/snowmobile.svg + maki-4.0.0/soccer.svg + maki-4.0.0/square-stroked.svg + maki-4.0.0/square.svg + maki-4.0.0/stadium.svg + maki-4.0.0/star-stroked.svg + maki-4.0.0/star.svg + maki-4.0.0/suitcase.svg + maki-4.0.0/sushi.svg + maki-4.0.0/swimming.svg + maki-4.0.0/teahouse.svg + maki-4.0.0/telephone.svg + maki-4.0.0/tennis.svg + maki-4.0.0/theatre.svg + maki-4.0.0/toilet.svg + maki-4.0.0/town-hall.svg + maki-4.0.0/town.svg + maki-4.0.0/triangle-stroked.svg + maki-4.0.0/triangle.svg + maki-4.0.0/veterinary.svg + maki-4.0.0/village.svg + maki-4.0.0/volcano.svg + maki-4.0.0/warehouse.svg + maki-4.0.0/waste-basket.svg + maki-4.0.0/water.svg + maki-4.0.0/wetland.svg + maki-4.0.0/wheelchair.svg + maki-4.0.0/zoo.svg + + diff --git a/src/plugins/geoservices/mapbox/mapbox_plugin.json b/src/plugins/geoservices/mapbox/mapbox_plugin.json new file mode 100644 index 0000000..ed594ad --- /dev/null +++ b/src/plugins/geoservices/mapbox/mapbox_plugin.json @@ -0,0 +1,17 @@ +{ + "Keys": ["mapbox"], + "Provider": "mapbox", + "Version": 100, + "Experimental": false, + "Features": [ + "OnlineMappingFeature", + "OnlineRoutingFeature", + "OnlinePlacesFeature", + "PlaceRecommendationsFeature", + "SearchSuggestionsFeature", + "LocalizedPlacesFeature", + "OnlineGeocodingFeature", + "ReverseGeocodingFeature", + "LocalizedGeocodingFeature" + ] +} diff --git a/src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp new file mode 100644 index 0000000..db7a35c --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeocodereplymapbox.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodereplymapbox.h" +#include "qmapboxcommon.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoCodeReplyMapbox::QGeoCodeReplyMapbox(QNetworkReply *reply, QObject *parent) +: QGeoCodeReply(parent) +{ + Q_ASSERT(parent); + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + + connect(reply, &QNetworkReply::finished, this, &QGeoCodeReplyMapbox::onNetworkReplyFinished); + connect(reply, QOverload::of(&QNetworkReply::error), + this, &QGeoCodeReplyMapbox::onNetworkReplyError); + + connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QGeoCodeReplyMapbox::~QGeoCodeReplyMapbox() +{ +} + +void QGeoCodeReplyMapbox::onNetworkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QList locations; + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + setError(ParseError, tr("Response parse error")); + return; + } + + const QJsonArray features = document.object().value(QStringLiteral("features")).toArray(); + for (const QJsonValue &value : features) + locations.append(QMapboxCommon::parseGeoLocation(value.toObject())); + + setLocations(locations); + + setFinished(true); +} + +void QGeoCodeReplyMapbox::onNetworkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QGeoCodeReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeocodereplymapbox.h b/src/plugins/geoservices/mapbox/qgeocodereplymapbox.h new file mode 100644 index 0000000..156299f --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeocodereplymapbox.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODEREPLYMAPBOX_H +#define QGEOCODEREPLYMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCodeReplyMapbox : public QGeoCodeReply +{ + Q_OBJECT + +public: + explicit QGeoCodeReplyMapbox(QNetworkReply *reply, QObject *parent = 0); + ~QGeoCodeReplyMapbox(); + +private Q_SLOTS: + void onNetworkReplyFinished(); + void onNetworkReplyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QGEOCODEREPLYMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.cpp new file mode 100644 index 0000000..e0c4f6a --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.cpp @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodingmanagerenginemapbox.h" +#include "qgeocodereplymapbox.h" +#include "qmapboxcommon.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace { + static const QString allAddressTypes = QStringLiteral("address,district,locality,neighborhood,place,postcode,region,country"); +} + +QGeoCodingManagerEngineMapbox::QGeoCodingManagerEngineMapbox(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) +: QGeoCodingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(QStringLiteral("mapbox.useragent"))) + m_userAgent = parameters.value(QStringLiteral("mapbox.useragent")).toString().toLatin1(); + else + m_userAgent = QByteArrayLiteral("Qt Location based application"); + + m_accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + + m_isEnterprise = parameters.value(QStringLiteral("mapbox.enterprise")).toBool(); + m_urlPrefix = m_isEnterprise ? mapboxGeocodingEnterpriseApiPath : mapboxGeocodingApiPath; + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QGeoCodingManagerEngineMapbox::~QGeoCodingManagerEngineMapbox() +{ +} + +QGeoCodeReply *QGeoCodingManagerEngineMapbox::geocode(const QGeoAddress &address, const QGeoShape &bounds) +{ + QUrlQuery queryItems; + + // If address text() is not generated: a manual setText() has been made. + if (!address.isTextGenerated()) { + queryItems.addQueryItem(QStringLiteral("type"), allAddressTypes); + return doSearch(address.text().simplified(), queryItems, bounds); + } + + QStringList addressString; + QStringList typeString; + + if (!address.street().isEmpty()) { + addressString.append(address.street()); + typeString.append(QStringLiteral("address")); + } + + if (!address.district().isEmpty()) { + addressString.append(address.district()); + typeString.append(QStringLiteral("district")); + typeString.append(QStringLiteral("locality")); + typeString.append(QStringLiteral("neighborhood")); + } + + if (!address.city().isEmpty()) { + addressString.append(address.city()); + typeString.append(QStringLiteral("place")); + } + + if (!address.postalCode().isEmpty()) { + addressString.append(address.postalCode()); + typeString.append(QStringLiteral("postcode")); + } + + if (!address.state().isEmpty()) { + addressString.append(address.state()); + typeString.append(QStringLiteral("region")); + } + + if (!address.country().isEmpty()) { + addressString.append(address.country()); + typeString.append(QStringLiteral("country")); + } + + queryItems.addQueryItem(QStringLiteral("type"), typeString.join(QLatin1Char(','))); + queryItems.addQueryItem(QStringLiteral("limit"), QString::number(1)); + + return doSearch(addressString.join(QStringLiteral(", ")), queryItems, bounds); +} + +QGeoCodeReply *QGeoCodingManagerEngineMapbox::geocode(const QString &address, int limit, int offset, const QGeoShape &bounds) +{ + Q_UNUSED(offset) + + QUrlQuery queryItems; + queryItems.addQueryItem(QStringLiteral("type"), allAddressTypes); + queryItems.addQueryItem(QStringLiteral("limit"), QString::number(limit)); + + return doSearch(address, queryItems, bounds); +} + +QGeoCodeReply *QGeoCodingManagerEngineMapbox::reverseGeocode(const QGeoCoordinate &coordinate, const QGeoShape &bounds) +{ + const QString coordinateString = QString::number(coordinate.longitude()) + QLatin1Char(',') + QString::number(coordinate.latitude()); + + QUrlQuery queryItems; + queryItems.addQueryItem(QStringLiteral("limit"), QString::number(1)); + + return doSearch(coordinateString, queryItems, bounds); +} + +QGeoCodeReply *QGeoCodingManagerEngineMapbox::doSearch(const QString &request, QUrlQuery &queryItems, const QGeoShape &bounds) +{ + queryItems.addQueryItem(QStringLiteral("access_token"), m_accessToken); + + const QString &languageCode = QLocale::system().name().section(QLatin1Char('_'), 0, 0); + queryItems.addQueryItem(QStringLiteral("language"), languageCode); + + QGeoRectangle boundingBox = bounds.boundingGeoRectangle(); + if (!boundingBox.isEmpty()) { + queryItems.addQueryItem(QStringLiteral("bbox"), + QString::number(boundingBox.topLeft().longitude()) + QLatin1Char(',') + + QString::number(boundingBox.bottomRight().latitude()) + QLatin1Char(',') + + QString::number(boundingBox.bottomRight().longitude()) + QLatin1Char(',') + + QString::number(boundingBox.topLeft().latitude())); + } + + QUrl requestUrl(m_urlPrefix + request + QStringLiteral(".json")); + requestUrl.setQuery(queryItems); + + QNetworkRequest networkRequest(requestUrl); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QNetworkReply *networkReply = m_networkManager->get(networkRequest); + QGeoCodeReplyMapbox *reply = new QGeoCodeReplyMapbox(networkReply, this); + + connect(reply, &QGeoCodeReplyMapbox::finished, this, &QGeoCodingManagerEngineMapbox::onReplyFinished); + connect(reply, QOverload::of(&QGeoCodeReply::error), + this, &QGeoCodingManagerEngineMapbox::onReplyError); + + return reply; +} + +void QGeoCodingManagerEngineMapbox::onReplyFinished() +{ + QGeoCodeReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void QGeoCodingManagerEngineMapbox::onReplyError(QGeoCodeReply::Error errorCode, const QString &errorString) +{ + QGeoCodeReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.h b/src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.h new file mode 100644 index 0000000..e091345 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeocodingmanagerenginemapbox.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGERENGINEMAPBOX_H +#define QGEOCODINGMANAGERENGINEMAPBOX_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class QGeoCodingManagerEngineMapbox : public QGeoCodingManagerEngine +{ + Q_OBJECT + +public: + QGeoCodingManagerEngineMapbox(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoCodingManagerEngineMapbox(); + + QGeoCodeReply *geocode(const QGeoAddress &address, const QGeoShape &bounds) override; + QGeoCodeReply *geocode(const QString &address, int limit, int offset, + const QGeoShape &bounds) override; + QGeoCodeReply *reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) override; + +private slots: + void onReplyFinished(); + void onReplyError(QGeoCodeReply::Error errorCode, const QString &errorString); + +private: + QGeoCodeReply *doSearch(const QString &, QUrlQuery &, const QGeoShape &bounds); + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_accessToken; + QString m_urlPrefix; + bool m_isEnterprise; +}; + +QT_END_NAMESPACE + +#endif // QGEOCODINGMANAGERENGINEMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.cpp b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.cpp new file mode 100644 index 0000000..2792ee7 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeofiletilecachemapbox.h" +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoFileTileCacheMapbox::QGeoFileTileCacheMapbox(const QList &mapTypes, int scaleFactor, const QString &directory, QObject *parent) + :QGeoFileTileCache(directory, parent), m_mapTypes(mapTypes) +{ + m_scaleFactor = qBound(1, scaleFactor, 2); + for (int i=0; i < mapTypes.size(); i++) + m_mapNameToId.insert(mapTypes[i].name(), i+1); +} + +QGeoFileTileCacheMapbox::~QGeoFileTileCacheMapbox() +{ + +} + +QString QGeoFileTileCacheMapbox::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const +{ + QString filename = spec.plugin(); + filename += QLatin1String("-"); + filename += m_mapTypes[spec.mapId()-1].name(); + filename += QLatin1String("-"); + filename += QString::number(spec.zoom()); + filename += QLatin1String("-"); + filename += QString::number(spec.x()); + filename += QLatin1String("-"); + filename += QString::number(spec.y()); + + //Append version if real version number to ensure backwards compatibility and eviction of old tiles + if (spec.version() != -1) { + filename += QLatin1String("-"); + filename += QString::number(spec.version()); + } + + filename += QLatin1String("-@"); + filename += QString::number(m_scaleFactor); + filename += QLatin1Char('x'); + + filename += QLatin1String("."); + filename += format; + + QDir dir = QDir(directory); + + return dir.filePath(filename); +} + +QGeoTileSpec QGeoFileTileCacheMapbox::filenameToTileSpec(const QString &filename) const +{ + QStringList parts = filename.split('.'); + + if (parts.length() != 3) // 3 because the map name has always a dot in it. + return QGeoTileSpec(); + + QString name = parts.at(0) + parts.at(1); + QStringList fields = name.split('-'); + + int length = fields.length(); + if (length != 6 && length != 7) { + return QGeoTileSpec(); + } else { + int scaleIdx = fields.last().indexOf("@"); + if (scaleIdx < 0 || fields.last().size() <= (scaleIdx + 2)) + return QGeoTileSpec(); + int scaleFactor = fields.last()[scaleIdx + 1].digitValue(); + if (scaleFactor != m_scaleFactor) + return QGeoTileSpec(); + } + + QList numbers; + + bool ok = false; + for (int i = 2; i < length-1; ++i) { // skipping -@_X + ok = false; + int value = fields.at(i).toInt(&ok); + if (!ok) + return QGeoTileSpec(); + numbers.append(value); + } + + //File name without version, append default + if (numbers.length() < 4) + numbers.append(-1); + + return QGeoTileSpec(fields.at(0), + m_mapNameToId[fields.at(1)], + numbers.at(0), + numbers.at(1), + numbers.at(2), + numbers.at(3)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.h b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.h new file mode 100644 index 0000000..30a10bd --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeofiletilecachemapbox.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOFILETILECACHEMAPBOX_H +#define QGEOFILETILECACHEMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoFileTileCacheMapbox : public QGeoFileTileCache +{ + Q_OBJECT +public: + QGeoFileTileCacheMapbox(const QList &mapTypes, int scaleFactor, const QString &directory = QString(), QObject *parent = 0); + ~QGeoFileTileCacheMapbox(); + +protected: + QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const override; + QGeoTileSpec filenameToTileSpec(const QString &filename) const override; + + QList m_mapTypes; + QMap m_mapNameToId; + int m_scaleFactor; +}; + +QT_END_NAMESPACE + +#endif // QGEOFILETILECACHEMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp new file mode 100644 index 0000000..4b60231 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeomapreplymapbox.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapreplymapbox.h" + +#include + +QGeoMapReplyMapbox::QGeoMapReplyMapbox(QNetworkReply *reply, const QGeoTileSpec &spec, const QString &format, QObject *parent) +: QGeoTiledMapReply(spec, parent), m_format (format) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QGeoMapReplyMapbox::~QGeoMapReplyMapbox() +{ +} + +void QGeoMapReplyMapbox::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + setMapImageData(reply->readAll()); + setMapImageFormat(m_format); + setFinished(true); +} + +void QGeoMapReplyMapbox::networkReplyError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) + setFinished(true); + else + setError(QGeoTiledMapReply::CommunicationError, reply->errorString()); +} diff --git a/src/plugins/geoservices/mapbox/qgeomapreplymapbox.h b/src/plugins/geoservices/mapbox/qgeomapreplymapbox.h new file mode 100644 index 0000000..c4a1dd8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeomapreplymapbox.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPREPLYMAPBOX_H +#define QGEOMAPREPLYMAPBOX_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMapReplyMapbox : public QGeoTiledMapReply +{ + Q_OBJECT + +public: + explicit QGeoMapReplyMapbox(QNetworkReply *reply, const QGeoTileSpec &spec, const QString &format, QObject *parent = 0); + ~QGeoMapReplyMapbox(); + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); + +private: + QString m_format; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPREPLYMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp new file mode 100644 index 0000000..c5f9d38 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.cpp @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutereplymapbox.h" +#include "qgeoroutingmanagerenginemapbox.h" +#include +#include +#include +#include +#include +#include +#include + +namespace { + +class QGeoRouteMapbox : public QGeoRoute +{ +public: + QGeoRouteMapbox(const QGeoRoute &other, const QVariantMap &metadata); +}; + +class QGeoRoutePrivateMapbox : public QGeoRoutePrivateDefault +{ +public: + QGeoRoutePrivateMapbox(const QGeoRoutePrivateDefault &other, const QVariantMap &metadata); + + virtual QString engineName() const override; + virtual QVariantMap metadata() const override; + + QVariantMap m_metadata; +}; + +QGeoRouteMapbox::QGeoRouteMapbox(const QGeoRoute &other, const QVariantMap &metadata) + : QGeoRoute(QExplicitlySharedDataPointer(new QGeoRoutePrivateMapbox(*static_cast(QGeoRoutePrivate::routePrivateData(other)), metadata))) +{ +} + +QGeoRoutePrivateMapbox::QGeoRoutePrivateMapbox(const QGeoRoutePrivateDefault &other, const QVariantMap &metadata) + : QGeoRoutePrivateDefault(other) + , m_metadata(metadata) +{ +} + +QString QGeoRoutePrivateMapbox::engineName() const +{ + return QStringLiteral("mapbox"); +} + +QVariantMap QGeoRoutePrivateMapbox::metadata() const +{ + return m_metadata; +} + +} // namespace + +QT_BEGIN_NAMESPACE + +QGeoRouteReplyMapbox::QGeoRouteReplyMapbox(QNetworkReply *reply, const QGeoRouteRequest &request, + QObject *parent) +: QGeoRouteReply(request, parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QGeoRouteReplyMapbox::~QGeoRouteReplyMapbox() +{ +} + +void QGeoRouteReplyMapbox::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QGeoRoutingManagerEngineMapbox *engine = qobject_cast(parent()); + const QGeoRouteParser *parser = engine->routeParser(); + + QList routes; + QString errorString; + + QByteArray routeReply = reply->readAll(); + QGeoRouteReply::Error error = parser->parseReply(routes, errorString, routeReply); + + QVariantMap metadata; + metadata["osrm.reply-json"] = routeReply; + + QList mapboxRoutes; + for (const QGeoRoute &route : routes.mid(0, request().numberAlternativeRoutes() + 1)) { + QGeoRouteMapbox mapboxRoute(route, metadata); + mapboxRoutes.append(mapboxRoute); + } + + if (error == QGeoRouteReply::NoError) { + setRoutes(mapboxRoutes); + // setError(QGeoRouteReply::NoError, status); // can't do this, or NoError is emitted and does damages + setFinished(true); + } else { + setError(error, errorString); + } +} + +void QGeoRouteReplyMapbox::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QGeoRouteReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.h b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.h new file mode 100644 index 0000000..f19faee --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutereplymapbox.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREPLYMAPBOX_H +#define QGEOROUTEREPLYMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteReplyMapbox : public QGeoRouteReply +{ + Q_OBJECT + +public: + explicit QGeoRouteReplyMapbox(QObject *parent = 0); + QGeoRouteReplyMapbox(QNetworkReply *reply, const QGeoRouteRequest &request, QObject *parent = 0); + ~QGeoRouteReplyMapbox(); + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTEREPLYMAPBOX_H + diff --git a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp new file mode 100644 index 0000000..e8db635 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.cpp @@ -0,0 +1,309 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutingmanagerenginemapbox.h" +#include "qgeoroutereplymapbox.h" +#include "qmapboxcommon.h" +#include +#include +#include + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteParserOsrmV5ExtensionMapbox: public QGeoRouteParserOsrmV5Extension +{ +public: + QGeoRouteParserOsrmV5ExtensionMapbox(const QString &accessToken, bool useMapboxTextInstructions); + void updateQuery(QUrlQuery &query) const override; + void updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const override; + + QString m_accessToken; + bool m_useMapboxTextInstructions = false; +}; + +QGeoRouteParserOsrmV5ExtensionMapbox::QGeoRouteParserOsrmV5ExtensionMapbox(const QString &accessToken, bool useMapboxTextInstructions) + : QGeoRouteParserOsrmV5Extension(), m_accessToken(accessToken), m_useMapboxTextInstructions(useMapboxTextInstructions) +{ + +} + +void QGeoRouteParserOsrmV5ExtensionMapbox::updateQuery(QUrlQuery &query) const +{ + if (!m_accessToken.isEmpty()) + query.addQueryItem(QLatin1String("access_token"), m_accessToken); + + query.addQueryItem(QLatin1String("annotations"), QLatin1String("duration,distance,speed,congestion")); + + query.addQueryItem(QLatin1String("voice_instructions"), QLatin1String("true")); + query.addQueryItem(QLatin1String("banner_instructions"), QLatin1String("true")); + query.addQueryItem(QLatin1String("roundabout_exits"), QLatin1String("true")); + + QLocale::MeasurementSystem unit = QLocale::system().measurementSystem(); + query.addQueryItem(QLatin1String("voice_units"), unit == QLocale::MetricSystem ? QLatin1String("metric") : QLatin1String("imperial")); +} + +static QVariantMap parseMapboxVoiceInstruction(const QJsonObject &voiceInstruction) +{ + QVariantMap map; + + if (voiceInstruction.value(QLatin1String("distanceAlongGeometry")).isDouble()) + map.insert(QLatin1String("distance_along_geometry"), voiceInstruction.value(QLatin1String("distanceAlongGeometry")).toDouble()); + + if (voiceInstruction.value(QLatin1String("announcement")).isString()) + map.insert(QLatin1String("announcement"), voiceInstruction.value(QLatin1String("announcement")).toString()); + + if (voiceInstruction.value(QLatin1String("ssmlAnnouncement")).isString()) + map.insert(QLatin1String("ssml_announcement"), voiceInstruction.value(QLatin1String("ssmlAnnouncement")).toString()); + + return map; +} + +static QVariantList parseMapboxVoiceInstructions(const QJsonArray &voiceInstructions) +{ + QVariantList list; + for (const QJsonValue &voiceInstructionValue : voiceInstructions) { + if (voiceInstructionValue.isObject()) + list << parseMapboxVoiceInstruction(voiceInstructionValue.toObject()); + } + return list; +} + +static QVariantMap parseMapboxBannerComponent(const QJsonObject &bannerComponent) +{ + QVariantMap map; + + if (bannerComponent.value(QLatin1String("type")).isString()) + map.insert(QLatin1String("type"), bannerComponent.value(QLatin1String("type")).toString()); + + if (bannerComponent.value(QLatin1String("text")).isString()) + map.insert(QLatin1String("text"), bannerComponent.value(QLatin1String("text")).toString()); + + if (bannerComponent.value(QLatin1String("abbr")).isString()) + map.insert(QLatin1String("abbr"), bannerComponent.value(QLatin1String("abbr")).toString()); + + if (bannerComponent.value(QLatin1String("abbr_priority")).isDouble()) + map.insert(QLatin1String("abbr_priority"), bannerComponent.value(QLatin1String("abbr_priority")).toInt()); + + return map; +} + +static QVariantList parseMapboxBannerComponents(const QJsonArray &bannerComponents) +{ + QVariantList list; + for (const QJsonValue &bannerComponentValue : bannerComponents) { + if (bannerComponentValue.isObject()) + list << parseMapboxBannerComponent(bannerComponentValue.toObject()); + } + return list; +} + +static QVariantMap parseMapboxBanner(const QJsonObject &banner) +{ + QVariantMap map; + + if (banner.value(QLatin1String("text")).isString()) + map.insert(QLatin1String("text"), banner.value(QLatin1String("text")).toString()); + + if (banner.value(QLatin1String("components")).isArray()) + map.insert(QLatin1String("components"), parseMapboxBannerComponents(banner.value(QLatin1String("components")).toArray())); + + if (banner.value(QLatin1String("type")).isString()) + map.insert(QLatin1String("type"), banner.value(QLatin1String("type")).toString()); + + if (banner.value(QLatin1String("modifier")).isString()) + map.insert(QLatin1String("modifier"), banner.value(QLatin1String("modifier")).toString()); + + if (banner.value(QLatin1String("degrees")).isDouble()) + map.insert(QLatin1String("degrees"), banner.value(QLatin1String("degrees")).toDouble()); + + if (banner.value(QLatin1String("driving_side")).isString()) + map.insert(QLatin1String("driving_side"), banner.value(QLatin1String("driving_side")).toString()); + + return map; +} + +static QVariantMap parseMapboxBannerInstruction(const QJsonObject &bannerInstruction) +{ + QVariantMap map; + + if (bannerInstruction.value(QLatin1String("distanceAlongGeometry")).isDouble()) + map.insert(QLatin1String("distance_along_geometry"), bannerInstruction.value(QLatin1String("distanceAlongGeometry")).toDouble()); + + if (bannerInstruction.value(QLatin1String("primary")).isObject()) + map.insert(QLatin1String("primary"), parseMapboxBanner(bannerInstruction.value(QLatin1String("primary")).toObject())); + + if (bannerInstruction.value(QLatin1String("secondary")).isObject()) + map.insert(QLatin1String("secondary"), parseMapboxBanner(bannerInstruction.value(QLatin1String("secondary")).toObject())); + + if (bannerInstruction.value(QLatin1String("then")).isObject()) + map.insert(QLatin1String("then"), parseMapboxBanner(bannerInstruction.value(QLatin1String("then")).toObject())); + + return map; +} + +static QVariantList parseMapboxBannerInstructions(const QJsonArray &bannerInstructions) +{ + QVariantList list; + for (const QJsonValue &bannerInstructionValue : bannerInstructions) { + if (bannerInstructionValue.isObject()) + list << parseMapboxBannerInstruction(bannerInstructionValue.toObject()); + } + return list; +} + +void QGeoRouteParserOsrmV5ExtensionMapbox::updateSegment(QGeoRouteSegment &segment, const QJsonObject &step, const QJsonObject &maneuver) const +{ + QGeoManeuver m = segment.maneuver(); + QVariantMap extendedAttributes = m.extendedAttributes(); + if (m_useMapboxTextInstructions && maneuver.value(QLatin1String("instruction")).isString()) { + QString maneuverInstructionText = maneuver.value(QLatin1String("instruction")).toString(); + if (!maneuverInstructionText.isEmpty()) + m.setInstructionText(maneuverInstructionText); + } + + if (step.value(QLatin1String("voiceInstructions")).isArray()) + extendedAttributes.insert(QLatin1String("mapbox.voice_instructions"), + parseMapboxVoiceInstructions(step.value(QLatin1String("voiceInstructions")).toArray())); + if (step.value(QLatin1String("bannerInstructions")).isArray()) + extendedAttributes.insert(QLatin1String("mapbox.banner_instructions"), + parseMapboxBannerInstructions(step.value(QLatin1String("bannerInstructions")).toArray())); + + m.setExtendedAttributes(extendedAttributes); + segment.setManeuver(m); +} + + +QGeoRoutingManagerEngineMapbox::QGeoRoutingManagerEngineMapbox(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) + : QGeoRoutingManagerEngine(parameters), + m_networkManager(new QNetworkAccessManager(this)), + m_userAgent(mapboxDefaultUserAgent) +{ + if (parameters.contains(QStringLiteral("mapbox.useragent"))) { + m_userAgent = parameters.value(QStringLiteral("mapbox.useragent")).toString().toLatin1(); + } + + if (parameters.contains(QStringLiteral("mapbox.access_token"))) { + m_accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + } + + bool use_mapbox_text_instructions = true; + if (parameters.contains(QStringLiteral("mapbox.routing.use_mapbox_text_instructions"))) { + use_mapbox_text_instructions = parameters.value(QStringLiteral("mapbox.use_mapbox_text_instructions")).toBool(); + } + + QGeoRouteParserOsrmV5 *parser = new QGeoRouteParserOsrmV5(this); + parser->setExtension(new QGeoRouteParserOsrmV5ExtensionMapbox(m_accessToken, use_mapbox_text_instructions)); + + m_routeParser = parser; + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QGeoRoutingManagerEngineMapbox::~QGeoRoutingManagerEngineMapbox() +{ +} + +QGeoRouteReply* QGeoRoutingManagerEngineMapbox::calculateRoute(const QGeoRouteRequest &request) +{ + QNetworkRequest networkRequest; + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QString url = mapboxDirectionsApiPath; + + QGeoRouteRequest::TravelModes travelModes = request.travelModes(); + if (travelModes.testFlag(QGeoRouteRequest::PedestrianTravel)) { + url += QStringLiteral("walking/"); + } else if (travelModes.testFlag(QGeoRouteRequest::BicycleTravel)) { + url += QStringLiteral("cycling/"); + } else if (travelModes.testFlag(QGeoRouteRequest::CarTravel)) { + const QList &featureTypes = request.featureTypes(); + int trafficFeatureIdx = featureTypes.indexOf(QGeoRouteRequest::TrafficFeature); + QGeoRouteRequest::FeatureWeight trafficWeight = request.featureWeight(QGeoRouteRequest::TrafficFeature); + if (trafficFeatureIdx >= 0 && + (trafficWeight == QGeoRouteRequest::AvoidFeatureWeight || trafficWeight == QGeoRouteRequest::DisallowFeatureWeight)) { + url += QStringLiteral("driving-traffic/"); + } else { + url += QStringLiteral("driving/"); + } + } + + networkRequest.setUrl(m_routeParser->requestUrl(request, url)); + + QNetworkReply *reply = m_networkManager->get(networkRequest); + + QGeoRouteReplyMapbox *routeReply = new QGeoRouteReplyMapbox(reply, request, this); + + connect(routeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(routeReply, SIGNAL(error(QGeoRouteReply::Error,QString)), + this, SLOT(replyError(QGeoRouteReply::Error,QString))); + + return routeReply; +} + +const QGeoRouteParser *QGeoRoutingManagerEngineMapbox::routeParser() const +{ + return m_routeParser; +} + +void QGeoRoutingManagerEngineMapbox::replyFinished() +{ + QGeoRouteReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void QGeoRoutingManagerEngineMapbox::replyError(QGeoRouteReply::Error errorCode, + const QString &errorString) +{ + QGeoRouteReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h new file mode 100644 index 0000000..61ab9a4 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoroutingmanagerenginemapbox.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Vlad Seryakov +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGERENGINEMAPBOX_H +#define QGEOROUTINGMANAGERENGINEMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; +class QGeoRouteParser; + +class QGeoRoutingManagerEngineMapbox : public QGeoRoutingManagerEngine +{ + Q_OBJECT + +public: + QGeoRoutingManagerEngineMapbox(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoRoutingManagerEngineMapbox(); + + QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request); + const QGeoRouteParser *routeParser() const; + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoRouteReply::Error errorCode, const QString &errorString); + +private: + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_accessToken; + bool m_useMapboxText = false; + QGeoRouteParser *m_routeParser = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTINGMANAGERENGINEOSM_H + diff --git a/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp b/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp new file mode 100644 index 0000000..80d9098 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderpluginmapbox.h" +#include "qgeocodingmanagerenginemapbox.h" +#include "qgeotiledmappingmanagerenginemapbox.h" +#include "qgeoroutingmanagerenginemapbox.h" +#include "qplacemanagerenginemapbox.h" + +#include + +QT_BEGIN_NAMESPACE + +static inline QString msgAccessTokenParameter() +{ + return QGeoServiceProviderFactoryMapbox::tr("Mapbox plugin requires a 'mapbox.access_token' parameter.\n" + "Please visit https://www.mapbox.com"); +} + +QGeoCodingManagerEngine *QGeoServiceProviderFactoryMapbox::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + const QString accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + + if (!accessToken.isEmpty()) { + return new QGeoCodingManagerEngineMapbox(parameters, error, errorString); + } else { + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = msgAccessTokenParameter(); + return 0; + } +} + +QGeoMappingManagerEngine *QGeoServiceProviderFactoryMapbox::createMappingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + const QString accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + + if (!accessToken.isEmpty()) { + return new QGeoTiledMappingManagerEngineMapbox(parameters, error, errorString); + } else { + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = msgAccessTokenParameter(); + return 0; + } +} + +QGeoRoutingManagerEngine *QGeoServiceProviderFactoryMapbox::createRoutingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + const QString accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + + if (!accessToken.isEmpty()) { + return new QGeoRoutingManagerEngineMapbox(parameters, error, errorString); + } else { + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = msgAccessTokenParameter(); + return 0; + } +} + +QPlaceManagerEngine *QGeoServiceProviderFactoryMapbox::createPlaceManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + const QString accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + + if (!accessToken.isEmpty()) { + return new QPlaceManagerEngineMapbox(parameters, error, errorString); + } else { + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = msgAccessTokenParameter(); + return 0; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.h b/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.h new file mode 100644 index 0000000..30b4c6e --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeoserviceproviderpluginmapbox.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_MAPBOX_H +#define QGEOSERVICEPROVIDER_MAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoServiceProviderFactoryMapbox: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "mapbox_plugin.json") + +public: + QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp new file mode 100644 index 0000000..f2595d0 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.cpp @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmappingmanagerenginemapbox.h" +#include "qgeotilefetchermapbox.h" + +#include +#include +#include +#include "qgeofiletilecachemapbox.h" +#ifdef LOCATIONLABS +#include +typedef QGeoTiledMapLabs Map; +#else +typedef QGeoTiledMap Map; +#endif + +QT_BEGIN_NAMESPACE + +QGeoTiledMappingManagerEngineMapbox::QGeoTiledMappingManagerEngineMapbox(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) +: QGeoTiledMappingManagerEngine() +{ + QGeoCameraCapabilities cameraCaps; + cameraCaps.setMinimumZoomLevel(0.0); + cameraCaps.setMaximumZoomLevel(19.0); + cameraCaps.setSupportsBearing(true); + cameraCaps.setSupportsTilting(true); + cameraCaps.setMinimumTilt(0); + cameraCaps.setMaximumTilt(80); + cameraCaps.setMinimumFieldOfView(20.0); + cameraCaps.setMaximumFieldOfView(120.0); + cameraCaps.setOverzoomEnabled(true); + setCameraCapabilities(cameraCaps); + + setTileSize(QSize(256, 256)); + + const QByteArray pluginName = "mapbox"; + QList mapTypes; + // as index 0 to retain compatibility with the current API, that expects the passed map_id to be on by default. + if (parameters.contains(QStringLiteral("mapbox.mapping.map_id"))) { + const QString name = parameters.value(QStringLiteral("mapbox.mapping.map_id")).toString(); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, name, name, false, false, mapTypes.size() + 1, pluginName, cameraCaps); + } else if (parameters.contains(QStringLiteral("mapbox.map_id"))) { //deprecated + const QString name = parameters.value(QStringLiteral("mapbox.map_id")).toString(); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, name, name, false, false, mapTypes.size() + 1, pluginName, cameraCaps); + } + + // As of 2016.06.15, valid mapbox map_ids are documented at https://www.mapbox.com/api-documentation/#maps + //: Noun describing map type 'Street map' + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.streets"), tr("Street"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map using light colors (weak contrast) + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.light"), tr("Light"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map using dark colors + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.dark"), tr("Dark"), false, true, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map created by satellite + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, QStringLiteral("mapbox.satellite"), tr("Satellite"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a street map created by satellite + mapTypes << QGeoMapType(QGeoMapType::HybridMap, QStringLiteral("mapbox.streets-satellite"), tr("Streets Satellite"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map using wheat paste colors + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.wheatpaste"), tr("Wheatpaste"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a basic street map + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox.streets-basic"), tr("Streets Basic"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map using cartoon-style fonts + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.comic"), tr("Comic"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map for outdoor activities + mapTypes << QGeoMapType(QGeoMapType::PedestrianMap, QStringLiteral("mapbox.outdoors"), tr("Outdoors"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map for sports + mapTypes << QGeoMapType(QGeoMapType::CycleMap, QStringLiteral("mapbox.run-bike-hike"), tr("Run Bike Hike"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map drawn by pencil + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.pencil"), tr("Pencil"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a treasure map with pirate boat watermark + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.pirates"), tr("Pirates"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map using emerald colors + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.emerald"), tr("Emerald"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + //: Noun describing type of a map with high contrast + mapTypes << QGeoMapType(QGeoMapType::CustomMap, QStringLiteral("mapbox.high-contrast"), tr("High Contrast"), false, false, mapTypes.size() + 1, pluginName, cameraCaps); + + // New way to specify multiple customized map_ids via additional_map_ids + if (parameters.contains(QStringLiteral("mapbox.mapping.additional_map_ids"))) { + const QString ids = parameters.value(QStringLiteral("mapbox.mapping.additional_map_ids")).toString(); + const QStringList idList = ids.split(',', QString::SkipEmptyParts); + + for (const QString &name: idList) { + if (!name.isEmpty()) + mapTypes << QGeoMapType(QGeoMapType::CustomMap, name, name, false, false, mapTypes.size() + 1, pluginName, cameraCaps); + } + } + + QVector mapIds; + for (int i=0; i < mapTypes.size(); ++i) + mapIds.push_back(mapTypes[i].name()); + + setSupportedMapTypes(mapTypes); + + int scaleFactor = 1; + if (parameters.contains(QStringLiteral("mapbox.mapping.highdpi_tiles"))) { + const QString param = parameters.value(QStringLiteral("mapbox.mapping.highdpi_tiles")).toString().toLower(); + if (param == "true") + scaleFactor = 2; + } + + QGeoTileFetcherMapbox *tileFetcher = new QGeoTileFetcherMapbox(scaleFactor, this); + tileFetcher->setMapIds(mapIds); + + if (parameters.contains(QStringLiteral("useragent"))) { + const QByteArray ua = parameters.value(QStringLiteral("useragent")).toString().toLatin1(); + tileFetcher->setUserAgent(ua); + } + if (parameters.contains(QStringLiteral("mapbox.mapping.format"))) { + const QString format = parameters.value(QStringLiteral("mapbox.mapping.format")).toString(); + tileFetcher->setFormat(format); + } else if (parameters.contains(QStringLiteral("mapbox.format"))) { //deprecated + const QString format = parameters.value(QStringLiteral("mapbox.format")).toString(); + tileFetcher->setFormat(format); + } + if (parameters.contains(QStringLiteral("mapbox.access_token"))) { + const QString token = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + tileFetcher->setAccessToken(token); + } + + setTileFetcher(tileFetcher); + + // TODO: do this in a plugin-neutral way so that other tiled map plugins + // don't need this boilerplate or hardcode plugin name + + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.directory"))) { + m_cacheDirectory = parameters.value(QStringLiteral("mapbox.mapping.cache.directory")).toString(); + } else { + // managerName() is not yet set, we have to hardcode the plugin name below + m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String(pluginName); + } + + QGeoFileTileCache *tileCache = new QGeoFileTileCacheMapbox(mapTypes, scaleFactor, m_cacheDirectory); + + /* + * Disk cache setup -- defaults to Unitary since: + * + * The Mapbox free plan allows for 6000 tiles to be stored for offline uses, + * As of 2016.06.15, according to https://www.mapbox.com/help/mobile-offline/ . + * Thus defaulting to Unitary strategy, and setting 6000 tiles as default cache disk size + */ + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("mapbox.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.disk.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("mapbox.mapping.cache.disk.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxDiskUsage(cacheSize); + } else { + if (tileCache->costStrategyDisk() == QGeoFileTileCache::Unitary) + tileCache->setMaxDiskUsage(6000); // The maximum allowed with the free tier + } + + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("mapbox.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.memory.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("mapbox.mapping.cache.memory.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxMemoryUsage(cacheSize); + } + + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("mapbox.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("mapbox.mapping.cache.texture.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("mapbox.mapping.cache.texture.size")).toString().toInt(&ok); + if (ok) + tileCache->setExtraTextureUsage(cacheSize); + } + + /* PREFETCHING */ + if (parameters.contains(QStringLiteral("mapbox.mapping.prefetching_style"))) { + const QString prefetchingMode = parameters.value(QStringLiteral("mapbox.mapping.prefetching_style")).toString(); + if (prefetchingMode == QStringLiteral("TwoNeighbourLayers")) + m_prefetchStyle = QGeoTiledMap::PrefetchTwoNeighbourLayers; + else if (prefetchingMode == QStringLiteral("OneNeighbourLayer")) + m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer; + else if (prefetchingMode == QStringLiteral("NoPrefetching")) + m_prefetchStyle = QGeoTiledMap::NoPrefetching; + } + + setTileCache(tileCache); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QGeoTiledMappingManagerEngineMapbox::~QGeoTiledMappingManagerEngineMapbox() +{ +} + +QGeoMap *QGeoTiledMappingManagerEngineMapbox::createMap() +{ + QGeoTiledMap *map = new Map(this, 0); + map->setPrefetchStyle(m_prefetchStyle); + return map; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h new file mode 100644 index 0000000..292e421 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeotiledmappingmanagerenginemapbox.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPPINGMANAGERENGINEMAPBOX_H +#define QGEOTILEDMAPPINGMANAGERENGINEMAPBOX_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEngineMapbox : public QGeoTiledMappingManagerEngine +{ + Q_OBJECT + +public: + QGeoTiledMappingManagerEngineMapbox(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString); + ~QGeoTiledMappingManagerEngineMapbox(); + + QGeoMap *createMap(); + +private: + QString m_cacheDirectory; +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEDMAPPINGMANAGERENGINEMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp new file mode 100644 index 0000000..0b12855 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotilefetchermapbox.h" +#include "qgeomapreplymapbox.h" +#include "qmapboxcommon.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoTileFetcherMapbox::QGeoTileFetcherMapbox(int scaleFactor, QGeoTiledMappingManagerEngine *parent) +: QGeoTileFetcher(parent), m_networkManager(new QNetworkAccessManager(this)), + m_userAgent(mapboxDefaultUserAgent), + m_format("png"), + m_replyFormat("png"), + m_accessToken("") +{ + m_scaleFactor = qBound(1, scaleFactor, 2); +} + +void QGeoTileFetcherMapbox::setUserAgent(const QByteArray &userAgent) +{ + m_userAgent = userAgent; +} + +void QGeoTileFetcherMapbox::setMapIds(const QVector &mapIds) +{ + m_mapIds = mapIds; +} + +void QGeoTileFetcherMapbox::setFormat(const QString &format) +{ + m_format = format; + + if (m_format == "png" || m_format == "png32" || m_format == "png64" || m_format == "png128" || m_format == "png256") + m_replyFormat = "png"; + else if (m_format == "jpg70" || m_format == "jpg80" || m_format == "jpg90") + m_replyFormat = "jpg"; + else + qWarning() << "Unknown map format " << m_format; +} + +void QGeoTileFetcherMapbox::setAccessToken(const QString &accessToken) +{ + m_accessToken = accessToken; +} + +QGeoTiledMapReply *QGeoTileFetcherMapbox::getTileImage(const QGeoTileSpec &spec) +{ + QNetworkRequest request; + request.setRawHeader("User-Agent", m_userAgent); + + request.setUrl(QUrl(mapboxTilesApiPath + + ((spec.mapId() >= m_mapIds.size()) ? QStringLiteral("mapbox.streets") : m_mapIds[spec.mapId() - 1]) + QLatin1Char('/') + + QString::number(spec.zoom()) + QLatin1Char('/') + + QString::number(spec.x()) + QLatin1Char('/') + + QString::number(spec.y()) + + ((m_scaleFactor > 1) ? (QLatin1Char('@') + QString::number(m_scaleFactor) + QLatin1String("x.")) : QLatin1String(".")) + + m_format + QLatin1Char('?') + + QStringLiteral("access_token=") + m_accessToken)); + + QNetworkReply *reply = m_networkManager->get(request); + + return new QGeoMapReplyMapbox(reply, spec, m_replyFormat); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h new file mode 100644 index 0000000..47f3a8a --- /dev/null +++ b/src/plugins/geoservices/mapbox/qgeotilefetchermapbox.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Canonical Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEFETCHERMAPBOX_H +#define QGEOTILEFETCHERMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEngine; +class QNetworkAccessManager; + +class QGeoTileFetcherMapbox : public QGeoTileFetcher +{ + Q_OBJECT + +public: + QGeoTileFetcherMapbox(int scaleFactor, QGeoTiledMappingManagerEngine *parent); + + void setUserAgent(const QByteArray &userAgent); + void setMapIds(const QVector &mapIds); + void setFormat(const QString &format); + void setAccessToken(const QString &accessToken); + +private: + QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec); + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_format; + QString m_replyFormat; + QString m_accessToken; + QVector m_mapIds; + int m_scaleFactor; +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEFETCHERMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qmapboxcommon.cpp b/src/plugins/geoservices/mapbox/qmapboxcommon.cpp new file mode 100644 index 0000000..b88e8f5 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qmapboxcommon.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmapboxcommon.h" + +#include +#include +#include +#include + +QString QMapboxCommon::mapboxNameForCategory(const QString &category) +{ + if (category.isEmpty()) + return category; + + QString categoryName = category; + categoryName[0] = categoryName[0].toUpper(); + return categoryName; +} + +// https://www.mapbox.com/api-documentation/#response-object +QGeoLocation QMapboxCommon::parseGeoLocation(const QJsonObject &response) +{ + QGeoLocation location; + + QGeoAddress address; + + QString streetAddress = response.value(QStringLiteral("address")).toString(); + + // If place type is 'address', the street address is a combo of 'text' and + // 'address'. The former provides the street name, and the latter provides + // the street number in that case. + if (response.value(QStringLiteral("place_type")).isArray()) { + foreach (const QJsonValue &value, response.value(QStringLiteral("place_type")).toArray()) { + if (!value.isString()) + continue; + if (value.toString() == QStringLiteral("address")) { + streetAddress.prepend(response.value(QStringLiteral("text")).toString() + QLatin1Char(' ')); + break; + } + } + } + + if (response.value(QStringLiteral("properties")).isObject()) { + const QJsonObject properties = response.value(QStringLiteral("properties")).toObject(); + + // Prefer properties.address over address. + const QString addressString = properties.value(QStringLiteral("address")).toString(); + if (!addressString.isEmpty()) + streetAddress = addressString; + } + + address.setStreet(streetAddress); + + if (response.value(QStringLiteral("context")).isArray()) { + foreach (const QJsonValue &value, response.value(QStringLiteral("context")).toArray()) { + if (!value.isObject()) + continue; + + const QJsonObject object = value.toObject(); + const QString valueId = object.value(QStringLiteral("id")).toString(); + const QString valueText = object.value(QStringLiteral("text")).toString(); + + if (valueId.isEmpty() || valueText.isEmpty()) + continue; + + // XXX: locality, neighborhood, address, poi, poi.landmark + if (valueId.startsWith(QStringLiteral("country"))) { + address.setCountry(valueText); + const QString countryCode = object.value(QStringLiteral("short_code")).toString(); + if (!countryCode.isEmpty()) + address.setCountryCode(countryCode); + } else if (valueId.startsWith(QStringLiteral("region"))) { + address.setState(valueText); + } else if (valueId.startsWith(QStringLiteral("postcode"))) { + address.setPostalCode(valueText); + } else if (valueId.startsWith(QStringLiteral("district"))) { + address.setDistrict(valueText); + } else if (valueId.startsWith(QStringLiteral("place"))) { + address.setCity(valueText); + } + } + } else { + // Fallback to using information from place_name. + const QString placeName = response.value(QStringLiteral("place_name")).toString(); + + // Remove actual place name. + address.setText(placeName.mid(placeName.indexOf(QLatin1Char(',')) + 1)); + } + + location.setAddress(address); + + QJsonArray bbox = response.value(QStringLiteral("bbox")).toArray(); + double top = bbox.at(3).toDouble(); + double left = bbox.at(0).toDouble(); + double bottom = bbox.at(1).toDouble(); + double right = bbox.at(2).toDouble(); + location.setBoundingBox(QGeoRectangle(QGeoCoordinate(top, left), QGeoCoordinate(bottom, right))); + + QJsonArray center = response.value(QStringLiteral("center")).toArray(); + location.setCoordinate(QGeoCoordinate(center.at(1).toDouble(), center.at(0).toDouble())); + + return location; +} diff --git a/src/plugins/geoservices/mapbox/qmapboxcommon.h b/src/plugins/geoservices/mapbox/qmapboxcommon.h new file mode 100644 index 0000000..e60c4e8 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qmapboxcommon.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMAPBOXAPICOMMON_H +#define QMAPBOXAPICOMMON_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const QString mapboxTilesApiPath = QStringLiteral("http://api.tiles.mapbox.com/v4/"); + +// https://www.mapbox.com/api-documentation/#geocoding +static const QString mapboxGeocodingApiPath = QStringLiteral("https://api.mapbox.com/geocoding/v5/mapbox.places/"); +static const QString mapboxGeocodingEnterpriseApiPath = QStringLiteral("https://api.mapbox.com/geocoding/v5/mapbox.places-permanent/"); + +// https://www.mapbox.com/api-documentation/#directions +static const QString mapboxDirectionsApiPath = QStringLiteral("https://api.mapbox.com/directions/v5/mapbox/"); + +static const QByteArray mapboxDefaultUserAgent = QByteArrayLiteral("Qt Location based application"); + +static const qreal mapboxDefaultRadius = 50000; + +class QMapboxCommon +{ +public: + static QString mapboxNameForCategory(const QString &category); + static QGeoLocation parseGeoLocation(const QJsonObject &response); +}; + +QT_END_NAMESPACE + +#endif // QMAPBOXAPICOMMON_H diff --git a/src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.cpp b/src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.cpp new file mode 100644 index 0000000..8951efb --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecategoriesreplymapbox.h" + +QT_BEGIN_NAMESPACE + +QPlaceCategoriesReplyMapbox::QPlaceCategoriesReplyMapbox(QObject *parent) +: QPlaceReply(parent) +{ +} + +QPlaceCategoriesReplyMapbox::~QPlaceCategoriesReplyMapbox() +{ +} + +void QPlaceCategoriesReplyMapbox::finish() +{ + setFinished(true); + emit finished(); +} + +void QPlaceCategoriesReplyMapbox::setError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply::setError(errorCode, errorString); + emit error(errorCode, errorString); + finish(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.h b/src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.h new file mode 100644 index 0000000..c4618c0 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacecategoriesreplymapbox.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECATEGORIESREPLYMAPBOX_H +#define QPLACECATEGORIESREPLYMAPBOX_H + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceCategoriesReplyMapbox : public QPlaceReply +{ + Q_OBJECT + +public: + explicit QPlaceCategoriesReplyMapbox(QObject *parent = 0); + ~QPlaceCategoriesReplyMapbox(); + +public slots: + void finish(); + void setError(QPlaceReply::Error errorCode, const QString &errorString); +}; + +QT_END_NAMESPACE + +#endif // QPLACECATEGORIESREPLYMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.cpp b/src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.cpp new file mode 100644 index 0000000..56678a4 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacemanagerenginemapbox.h" +#include "qplacesearchreplymapbox.h" +#include "qplacesearchsuggestionreplymapbox.h" +#include "qplacecategoriesreplymapbox.h" +#include "qmapboxcommon.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +// https://www.mapbox.com/api-documentation/#poi-categories +static const QStringList categories = QStringList() + << QStringLiteral("bakery") + << QStringLiteral("bank") + << QStringLiteral("bar") + << QStringLiteral("cafe") + << QStringLiteral("church") + << QStringLiteral("cinema") + << QStringLiteral("coffee") + << QStringLiteral("concert") + << QStringLiteral("fast food") + << QStringLiteral("finance") + << QStringLiteral("gallery") + << QStringLiteral("historic") + << QStringLiteral("hotel") + << QStringLiteral("landmark") + << QStringLiteral("museum") + << QStringLiteral("music") + << QStringLiteral("park") + << QStringLiteral("pizza") + << QStringLiteral("restaurant") + << QStringLiteral("retail") + << QStringLiteral("school") + << QStringLiteral("shop") + << QStringLiteral("tea") + << QStringLiteral("theater") + << QStringLiteral("university"); + +} // namespace + +// Mapbox API does not provide support for paginated place queries. This +// implementation is a wrapper around its Geocoding service: +// https://www.mapbox.com/api-documentation/#geocoding +QPlaceManagerEngineMapbox::QPlaceManagerEngineMapbox(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) + : QPlaceManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(QStringLiteral("mapbox.useragent"))) + m_userAgent = parameters.value(QStringLiteral("mapbox.useragent")).toString().toLatin1(); + else + m_userAgent = mapboxDefaultUserAgent; + + m_accessToken = parameters.value(QStringLiteral("mapbox.access_token")).toString(); + + m_isEnterprise = parameters.value(QStringLiteral("mapbox.enterprise")).toBool(); + m_urlPrefix = m_isEnterprise ? mapboxGeocodingEnterpriseApiPath : mapboxGeocodingApiPath; + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QPlaceManagerEngineMapbox::~QPlaceManagerEngineMapbox() +{ +} + +QPlaceSearchReply *QPlaceManagerEngineMapbox::search(const QPlaceSearchRequest &request) +{ + return qobject_cast(doSearch(request, PlaceSearchType::CompleteSearch)); +} + +QPlaceSearchSuggestionReply *QPlaceManagerEngineMapbox::searchSuggestions(const QPlaceSearchRequest &request) +{ + return qobject_cast(doSearch(request, PlaceSearchType::SuggestionSearch)); +} + +QPlaceReply *QPlaceManagerEngineMapbox::doSearch(const QPlaceSearchRequest &request, PlaceSearchType searchType) +{ + const QGeoShape searchArea = request.searchArea(); + const QString searchTerm = request.searchTerm(); + const QString recommendationId = request.recommendationId(); + const QList placeCategories = request.categories(); + + bool invalidRequest = false; + + // QLocation::DeviceVisibility is not allowed for non-enterprise accounts. + if (!m_isEnterprise) + invalidRequest |= request.visibilityScope().testFlag(QLocation::DeviceVisibility); + + // Must provide either a search term, categories or recommendation. + invalidRequest |= searchTerm.isEmpty() && placeCategories.isEmpty() && recommendationId.isEmpty(); + + // Category search must not provide recommendation, and vice-versa. + invalidRequest |= searchTerm.isEmpty() && !placeCategories.isEmpty() && !recommendationId.isEmpty(); + + if (invalidRequest) { + QPlaceReply *reply; + if (searchType == PlaceSearchType::CompleteSearch) + reply = new QPlaceSearchReplyMapbox(request, 0, this); + else + reply = new QPlaceSearchSuggestionReplyMapbox(0, this); + + connect(reply, &QPlaceReply::finished, this, &QPlaceManagerEngineMapbox::onReplyFinished); + connect(reply, QOverload::of(&QPlaceReply::error), + this, &QPlaceManagerEngineMapbox::onReplyError); + + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError), + Q_ARG(QString, "Invalid request.")); + + return reply; + } + + QString queryString; + if (!searchTerm.isEmpty()) { + queryString = searchTerm; + } else if (!recommendationId.isEmpty()) { + queryString = recommendationId; + } else { + QStringList similarIds; + for (const QPlaceCategory &placeCategory : placeCategories) + similarIds.append(placeCategory.categoryId()); + queryString = similarIds.join(QLatin1Char(',')); + } + queryString.append(QStringLiteral(".json")); + + // https://www.mapbox.com/api-documentation/#request-format + QUrl requestUrl(m_urlPrefix + queryString); + + QUrlQuery queryItems; + queryItems.addQueryItem(QStringLiteral("access_token"), m_accessToken); + + // XXX: Investigate situations where we need to filter by 'country'. + + QStringList languageCodes; + for (const QLocale& locale: qAsConst(m_locales)) { + // Returns the language and country of this locale as a string of the + // form "language_country", where language is a lowercase, two-letter + // ISO 639 language code, and country is an uppercase, two- or + // three-letter ISO 3166 country code. + + if (locale.language() == QLocale::C) + continue; + + const QString languageCode = locale.name().section(QLatin1Char('_'), 0, 0); + if (!languageCodes.contains(languageCode)) + languageCodes.append(languageCode); + } + + if (!languageCodes.isEmpty()) + queryItems.addQueryItem(QStringLiteral("language"), languageCodes.join(QLatin1Char(','))); + + if (searchArea.type() != QGeoShape::UnknownType) { + const QGeoCoordinate center = searchArea.center(); + queryItems.addQueryItem(QStringLiteral("proximity"), + QString::number(center.longitude()) + QLatin1Char(',') + QString::number(center.latitude())); + } + + queryItems.addQueryItem(QStringLiteral("type"), QStringLiteral("poi")); + + // XXX: Investigate situations where 'autocomplete' should be disabled. + + QGeoRectangle boundingBox = searchArea.boundingGeoRectangle(); + if (!boundingBox.isEmpty()) { + queryItems.addQueryItem(QStringLiteral("bbox"), + QString::number(boundingBox.topLeft().longitude()) + QLatin1Char(',') + + QString::number(boundingBox.bottomRight().latitude()) + QLatin1Char(',') + + QString::number(boundingBox.bottomRight().longitude()) + QLatin1Char(',') + + QString::number(boundingBox.topLeft().latitude())); + } + + if (request.limit() > 0) + queryItems.addQueryItem(QStringLiteral("limit"), QString::number(request.limit())); + + // XXX: Investigate searchContext() use cases. + + requestUrl.setQuery(queryItems); + + QNetworkRequest networkRequest(requestUrl); + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + QNetworkReply *networkReply = m_networkManager->get(networkRequest); + QPlaceReply *reply; + if (searchType == PlaceSearchType::CompleteSearch) + reply = new QPlaceSearchReplyMapbox(request, networkReply, this); + else + reply = new QPlaceSearchSuggestionReplyMapbox(networkReply, this); + + connect(reply, &QPlaceReply::finished, this, &QPlaceManagerEngineMapbox::onReplyFinished); + connect(reply, QOverload::of(&QPlaceReply::error), + this, &QPlaceManagerEngineMapbox::onReplyError); + + return reply; +} + +QPlaceReply *QPlaceManagerEngineMapbox::initializeCategories() +{ + if (m_categories.isEmpty()) { + for (const QString &categoryId : categories) { + QPlaceCategory category; + category.setName(QMapboxCommon::mapboxNameForCategory(categoryId)); + category.setCategoryId(categoryId); + category.setVisibility(QLocation::PublicVisibility); + m_categories[categoryId] = category; + } + } + + QPlaceCategoriesReplyMapbox *reply = new QPlaceCategoriesReplyMapbox(this); + connect(reply, &QPlaceReply::finished, this, &QPlaceManagerEngineMapbox::onReplyFinished); + connect(reply, QOverload::of(&QPlaceReply::error), + this, &QPlaceManagerEngineMapbox::onReplyError); + + // Queue a future finished() emission from the reply. + QMetaObject::invokeMethod(reply, "finish", Qt::QueuedConnection); + + return reply; +} + +QString QPlaceManagerEngineMapbox::parentCategoryId(const QString &categoryId) const +{ + Q_UNUSED(categoryId) + + // Only a single category level. + return QString(); +} + +QStringList QPlaceManagerEngineMapbox::childCategoryIds(const QString &categoryId) const +{ + // Only a single category level. + if (categoryId.isEmpty()) + return m_categories.keys(); + + return QStringList(); +} + +QPlaceCategory QPlaceManagerEngineMapbox::category(const QString &categoryId) const +{ + return m_categories.value(categoryId); +} + +QList QPlaceManagerEngineMapbox::childCategories(const QString &parentId) const +{ + // Only a single category level. + if (parentId.isEmpty()) + return m_categories.values(); + + return QList(); +} + +QList QPlaceManagerEngineMapbox::locales() const +{ + return m_locales; +} + +void QPlaceManagerEngineMapbox::setLocales(const QList &locales) +{ + m_locales = locales; +} + +void QPlaceManagerEngineMapbox::onReplyFinished() +{ + QPlaceReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void QPlaceManagerEngineMapbox::onReplyError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} diff --git a/src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.h b/src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.h new file mode 100644 index 0000000..120fab7 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacemanagerenginemapbox.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMANAGERENGINEMAPBOX_H +#define QPLACEMANAGERENGINEMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class QPlaceManagerEngineMapbox : public QPlaceManagerEngine +{ + Q_OBJECT + +public: + QPlaceManagerEngineMapbox(const QVariantMap ¶meters, QGeoServiceProvider::Error *, + QString *errorString); + ~QPlaceManagerEngineMapbox(); + + QPlaceSearchReply *search(const QPlaceSearchRequest &) override; + + QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &) override; + + QPlaceReply *initializeCategories() override; + QString parentCategoryId(const QString &categoryId) const override; + QStringList childCategoryIds(const QString &categoryId) const override; + QPlaceCategory category(const QString &categoryId) const override; + QList childCategories(const QString &parentId) const override; + + QList locales() const override; + void setLocales(const QList &locales) override; + + // TODO: icon + //QPlaceIcon icon(const QString &remotePath, + // const QList &categories = QList()) const; + + //QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const override; + +private slots: + void onReplyFinished(); + void onReplyError(QPlaceReply::Error, const QString &errorString); + +private: + enum PlaceSearchType { + CompleteSearch = 0, + SuggestionSearch + }; + + QPlaceReply *doSearch(const QPlaceSearchRequest&, PlaceSearchType); + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_accessToken; + QString m_urlPrefix; + bool m_isEnterprise; + + QList m_locales; + QHash m_categories; +}; + +QT_END_NAMESPACE + +#endif // QPLACEMANAGERENGINEMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp b/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp new file mode 100644 index 0000000..b2f2f04 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.cpp @@ -0,0 +1,231 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchreplymapbox.h" +#include "qplacemanagerenginemapbox.h" +#include "qmapboxcommon.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace { + +// https://www.mapbox.com/api-documentation/#response-object +QPlaceResult parsePlaceResult(const QJsonObject &response, const QString &attribution) +{ + QPlace place; + + place.setAttribution(attribution); + place.setPlaceId(response.value(QStringLiteral("id")).toString()); + place.setVisibility(QLocation::PublicVisibility); + + QString placeName = response.value(QStringLiteral("text")).toString(); + if (placeName.isEmpty()) + placeName = response.value(QStringLiteral("place_name")).toString(); + + place.setName(placeName); + place.setDetailsFetched(true); + + // Unused data: type, place_type, relevance, properties.short_code, + // properties.landmark, properties.wikidata + + // The property object is unstable and only Carmen GeoJSON properties are + // guaranteed. This implementation should check for the presence of these + // values in a response before it attempts to use them. + if (response.value(QStringLiteral("properties")).isObject()) { + const QJsonObject properties = response.value(QStringLiteral("properties")).toObject(); + + const QString makiString = properties.value(QStringLiteral("maki")).toString(); + if (!makiString.isEmpty()) { + QVariantMap iconParameters; + iconParameters.insert(QPlaceIcon::SingleUrl, + QUrl::fromLocalFile(QStringLiteral(":/mapbox/") + makiString + QStringLiteral(".svg"))); + + QPlaceIcon icon; + icon.setParameters(iconParameters); + place.setIcon(icon); + } + + const QString phoneString = properties.value(QStringLiteral("tel")).toString(); + if (!phoneString.isEmpty()) { + QPlaceContactDetail phoneDetail; + phoneDetail.setLabel(QPlaceContactDetail::Phone); + phoneDetail.setValue(phoneString); + place.setContactDetails(QPlaceContactDetail::Phone, QList() << phoneDetail); + } + + const QString categoryString = properties.value(QStringLiteral("category")).toString(); + if (!categoryString.isEmpty()) { + QList categories; + for (const QString &categoryId : categoryString.split(QStringLiteral(", "), QString::SkipEmptyParts)) { + QPlaceCategory category; + category.setName(QMapboxCommon::mapboxNameForCategory(categoryId)); + category.setCategoryId(categoryId); + categories.append(category); + } + place.setCategories(categories); + } + } + + // XXX: matching_text, matching_place_name + // XXX: text_{language}, place_name_{language} + // XXX: language, language_{language} + + place.setLocation(QMapboxCommon::parseGeoLocation(response)); + + // XXX: geometry, geometry.type, geometry.coordinates, geometry.interpolated + + QPlaceResult result; + result.setPlace(place); + result.setTitle(place.name()); + + return result; +} + +} // namespace + +QPlaceSearchReplyMapbox::QPlaceSearchReplyMapbox(const QPlaceSearchRequest &request, QNetworkReply *reply, QPlaceManagerEngineMapbox *parent) +: QPlaceSearchReply(parent) +{ + Q_ASSERT(parent); + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + setRequest(request); + + connect(reply, &QNetworkReply::finished, this, &QPlaceSearchReplyMapbox::onReplyFinished); + connect(reply, QOverload::of(&QNetworkReply::error), + this, &QPlaceSearchReplyMapbox::onNetworkError); + + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QPlaceSearchReplyMapbox::~QPlaceSearchReplyMapbox() +{ +} + +void QPlaceSearchReplyMapbox::setError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply::setError(errorCode, errorString); + emit error(errorCode, errorString); + + setFinished(true); + emit finished(); +} + +void QPlaceSearchReplyMapbox::onReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + const QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + setError(ParseError, tr("Response parse error")); + return; + } + + const QJsonArray features = document.object().value(QStringLiteral("features")).toArray(); + const QString attribution = document.object().value(QStringLiteral("attribution")).toString(); + + const QGeoCoordinate searchCenter = request().searchArea().center(); + const QList categories = request().categories(); + + QList results; + for (const QJsonValue &feature : features) { + QPlaceResult placeResult = parsePlaceResult(feature.toObject(), attribution); + + if (!categories.isEmpty()) { + const QList placeCategories = placeResult.place().categories(); + bool categoryMatch = false; + if (!placeCategories.isEmpty()) { + for (const QPlaceCategory &placeCategory : placeCategories) { + if (categories.contains(placeCategory)) { + categoryMatch = true; + break; + } + } + } + if (!categoryMatch) + continue; + } + placeResult.setDistance(searchCenter.distanceTo(placeResult.place().location().coordinate())); + results.append(placeResult); + } + + if (request().relevanceHint() == QPlaceSearchRequest::DistanceHint) { + std::sort(results.begin(), results.end(), [](const QPlaceResult &a, const QPlaceResult &b) -> bool { + return a.distance() < b.distance(); + }); + } else if (request().relevanceHint() == QPlaceSearchRequest::LexicalPlaceNameHint) { + std::sort(results.begin(), results.end(), [](const QPlaceResult &a, const QPlaceResult &b) -> bool { + return a.place().name() < b.place().name(); + }); + } + + setResults(results); + + setFinished(true); + emit finished(); +} + +void QPlaceSearchReplyMapbox::onNetworkError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.h b/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.h new file mode 100644 index 0000000..05ba8fc --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacesearchreplymapbox.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHREPLYMAPBOX_H +#define QPLACESEARCHREPLYMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QPlaceManagerEngineMapbox; +class QPlaceResult; + +class QPlaceSearchReplyMapbox : public QPlaceSearchReply +{ + Q_OBJECT + +public: + QPlaceSearchReplyMapbox(const QPlaceSearchRequest &request, QNetworkReply *reply, + QPlaceManagerEngineMapbox *parent); + ~QPlaceSearchReplyMapbox(); + +public slots: + void setError(QPlaceReply::Error errorCode, const QString &errorString); + +private slots: + void onReplyFinished(); + void onNetworkError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QPLACESEARCHREPLYMAPBOX_H diff --git a/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp b/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp new file mode 100644 index 0000000..23d7617 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchsuggestionreplymapbox.h" +#include "qplacemanagerenginemapbox.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QPlaceSearchSuggestionReplyMapbox::QPlaceSearchSuggestionReplyMapbox(QNetworkReply *reply, QPlaceManagerEngineMapbox *parent) +: QPlaceSearchSuggestionReply(parent) +{ + Q_ASSERT(parent); + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + + connect(reply, &QNetworkReply::finished, this, &QPlaceSearchSuggestionReplyMapbox::onReplyFinished); + connect(reply, QOverload::of(&QNetworkReply::error), + this, &QPlaceSearchSuggestionReplyMapbox::onNetworkError); + + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QPlaceSearchSuggestionReplyMapbox::~QPlaceSearchSuggestionReplyMapbox() +{ +} + +void QPlaceSearchSuggestionReplyMapbox::setError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply::setError(errorCode, errorString); + emit error(errorCode, errorString); + + setFinished(true); + emit finished(); +} + +void QPlaceSearchSuggestionReplyMapbox::onReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + setError(ParseError, tr("Response parse error")); + return; + } + + const QJsonArray features = document.object().value(QStringLiteral("features")).toArray(); + + QStringList suggestions; + for (const QJsonValue &feature : features) { + if (feature.isObject()) + suggestions.append(feature.toObject().value(QStringLiteral("text")).toString()); + } + + setSuggestions(suggestions); + + setFinished(true); + emit finished(); +} + +void QPlaceSearchSuggestionReplyMapbox::onNetworkError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.h b/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.h new file mode 100644 index 0000000..8516c73 --- /dev/null +++ b/src/plugins/geoservices/mapbox/qplacesearchsuggestionreplymapbox.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHSUGGESTIONREPLYMAPBOX_H +#define QPLACESEARCHSUGGESTIONREPLYMAPBOX_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QPlaceManagerEngineMapbox; +class QPlaceResult; + +class QPlaceSearchSuggestionReplyMapbox : public QPlaceSearchSuggestionReply +{ + Q_OBJECT + +public: + QPlaceSearchSuggestionReplyMapbox(QNetworkReply *reply, QPlaceManagerEngineMapbox *parent); + ~QPlaceSearchSuggestionReplyMapbox(); + +public slots: + void setError(QPlaceReply::Error errorCode, const QString &errorString); + +private slots: + void onReplyFinished(); + void onNetworkError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QPLACESEARCHSUGGESTIONREPLYMAPBOX_H diff --git a/src/plugins/geoservices/mapboxgl/logo.png b/src/plugins/geoservices/mapboxgl/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9d6e90ef57f3bc13c5ad6c75251f5acddc7985e1 GIT binary patch literal 2749 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 + + logo.png + + diff --git a/src/plugins/geoservices/mapboxgl/mapboxgl_plugin.json b/src/plugins/geoservices/mapboxgl/mapboxgl_plugin.json new file mode 100644 index 0000000..0031e08 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/mapboxgl_plugin.json @@ -0,0 +1,11 @@ +{ + "Keys": ["mapboxgl"], + "Provider": "mapboxgl", + "Version": 100, + "Experimental": false, + "Features": [ + "OnlineMappingFeature", + "OfflineMappingFeature", + "LocalizedMappingFeature" + ] +} diff --git a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp new file mode 100644 index 0000000..dfebc20 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.cpp @@ -0,0 +1,469 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapmapboxgl.h" +#include "qgeomapmapboxgl_p.h" +#include "qsgmapboxglnode.h" +#include "qmapboxglstylechange_p.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +// FIXME: Expose from Mapbox GL constants +#define MBGL_TILE_SIZE 512.0 + +namespace { + +// WARNING! The development token is subject to Mapbox Terms of Services +// and must not be used in production. +static char developmentToken[] = + "pk.eyJ1IjoicXRzZGsiLCJhIjoiY2l5azV5MHh5MDAwdTMybzBybjUzZnhxYSJ9.9rfbeqPjX2BusLRDXHCOBA"; + +static const double invLog2 = 1.0 / std::log(2.0); + +static double zoomLevelFrom256(double zoomLevelFor256, double tileSize) +{ + return std::log(std::pow(2.0, zoomLevelFor256) * 256.0 / tileSize) * invLog2; +} + +} // namespace + +QGeoMapMapboxGLPrivate::QGeoMapMapboxGLPrivate(QGeoMappingManagerEngineMapboxGL *engine) + : QGeoMapPrivate(engine, new QGeoProjectionWebMercator) +{ +} + +QGeoMapMapboxGLPrivate::~QGeoMapMapboxGLPrivate() +{ +} + +QSGNode *QGeoMapMapboxGLPrivate::updateSceneGraph(QSGNode *node, QQuickWindow *window) +{ + Q_Q(QGeoMapMapboxGL); + + if (m_viewportSize.isEmpty()) { + delete node; + return 0; + } + + QMapboxGL *map = 0; + if (!node) { + QOpenGLContext *currentCtx = QOpenGLContext::currentContext(); + if (!currentCtx) { + qWarning("QOpenGLContext is NULL!"); + return node; + } + if (m_useFBO) { + QSGMapboxGLTextureNode *mbglNode = new QSGMapboxGLTextureNode(m_settings, m_viewportSize, window->devicePixelRatio(), q); + QObject::connect(mbglNode->map(), &QMapboxGL::mapChanged, q, &QGeoMapMapboxGL::onMapChanged); + m_syncState = MapTypeSync | CameraDataSync | ViewportSync; + node = mbglNode; + } else { + QSGMapboxGLRenderNode *mbglNode = new QSGMapboxGLRenderNode(m_settings, m_viewportSize, window->devicePixelRatio(), q); + QObject::connect(mbglNode->map(), &QMapboxGL::mapChanged, q, &QGeoMapMapboxGL::onMapChanged); + m_syncState = MapTypeSync | CameraDataSync | ViewportSync; + node = mbglNode; + } + } + map = (m_useFBO) ? static_cast(node)->map() + : static_cast(node)->map(); + + if (m_syncState & MapTypeSync) { + m_developmentMode = m_activeMapType.name().startsWith("mapbox://") + && m_settings.accessToken() == developmentToken; + + map->setStyleUrl(m_activeMapType.name()); + } + + if (m_syncState & CameraDataSync) { + map->setZoom(zoomLevelFrom256(m_cameraData.zoomLevel() , MBGL_TILE_SIZE)); + map->setBearing(m_cameraData.bearing()); + map->setPitch(m_cameraData.tilt()); + + QGeoCoordinate coordinate = m_cameraData.center(); + map->setCoordinate(QMapbox::Coordinate(coordinate.latitude(), coordinate.longitude())); + } + + if (m_syncState & ViewportSync) { + if (m_useFBO) { + static_cast(node)->resize(m_viewportSize, window->devicePixelRatio()); + } else { + map->resize(m_viewportSize); + } + } + + if (m_styleLoaded) { + syncStyleChanges(map); + } + + if (m_useFBO) { + static_cast(node)->render(window); + } + + threadedRenderingHack(window, map); + + m_syncState = NoSync; + + return node; +} + +void QGeoMapMapboxGLPrivate::addParameter(QGeoMapParameter *param) +{ + Q_Q(QGeoMapMapboxGL); + + QObject::connect(param, &QGeoMapParameter::propertyUpdated, q, + &QGeoMapMapboxGL::onParameterPropertyUpdated); + + if (m_styleLoaded) { + m_styleChanges << QMapboxGLStyleChange::addMapParameter(param); + emit q->sgNodeChanged(); + } +} + +void QGeoMapMapboxGLPrivate::removeParameter(QGeoMapParameter *param) +{ + Q_Q(QGeoMapMapboxGL); + + q->disconnect(param); +} + +QGeoMap::ItemTypes QGeoMapMapboxGLPrivate::supportedMapItemTypes() const +{ + return QGeoMap::MapRectangle | QGeoMap::MapCircle | QGeoMap::MapPolygon | QGeoMap::MapPolyline; +} + +void QGeoMapMapboxGLPrivate::addMapItem(QDeclarativeGeoMapItemBase *item) +{ + Q_Q(QGeoMapMapboxGL); + + switch (item->itemType()) { + case QGeoMap::NoItem: + case QGeoMap::MapQuickItem: + case QGeoMap::CustomMapItem: + return; + case QGeoMap::MapRectangle: { + QDeclarativeRectangleMapItem *mapItem = static_cast(item); + QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativeRectangleMapItem::bottomRightChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged); + QObject::connect(mapItem, &QDeclarativeRectangleMapItem::topLeftChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged); + QObject::connect(mapItem, &QDeclarativeRectangleMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged); + QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged); + } break; + case QGeoMap::MapCircle: { + QDeclarativeCircleMapItem *mapItem = static_cast(item); + QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativeCircleMapItem::centerChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged); + QObject::connect(mapItem, &QDeclarativeCircleMapItem::radiusChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged); + QObject::connect(mapItem, &QDeclarativeCircleMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged); + QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged); + } break; + case QGeoMap::MapPolygon: { + QDeclarativePolygonMapItem *mapItem = static_cast(item); + QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativePolygonMapItem::pathChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged); + QObject::connect(mapItem, &QDeclarativePolygonMapItem::colorChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged); + QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged); + QObject::connect(mapItem->border(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged); + } break; + case QGeoMap::MapPolyline: { + QDeclarativePolylineMapItem *mapItem = static_cast(item); + QObject::connect(mapItem, &QQuickItem::visibleChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + QObject::connect(mapItem, &QDeclarativePolylineMapItem::pathChanged, q, &QGeoMapMapboxGL::onMapItemGeometryChanged); + QObject::connect(mapItem->line(), &QDeclarativeMapLineProperties::colorChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged); + QObject::connect(mapItem->line(), &QDeclarativeMapLineProperties::widthChanged, q, &QGeoMapMapboxGL::onMapItemSubPropertyChanged); + } break; + } + + QObject::connect(item, &QDeclarativeGeoMapItemBase::mapItemOpacityChanged, q, &QGeoMapMapboxGL::onMapItemPropertyChanged); + + m_styleChanges << QMapboxGLStyleChange::addMapItem(item, m_mapItemsBefore); + + emit q->sgNodeChanged(); +} + +void QGeoMapMapboxGLPrivate::removeMapItem(QDeclarativeGeoMapItemBase *item) +{ + Q_Q(QGeoMapMapboxGL); + + switch (item->itemType()) { + case QGeoMap::NoItem: + case QGeoMap::MapQuickItem: + case QGeoMap::CustomMapItem: + return; + case QGeoMap::MapRectangle: + q->disconnect(static_cast(item)->border()); + break; + case QGeoMap::MapCircle: + q->disconnect(static_cast(item)->border()); + break; + case QGeoMap::MapPolygon: + q->disconnect(static_cast(item)->border()); + break; + case QGeoMap::MapPolyline: + q->disconnect(static_cast(item)->line()); + break; + } + + q->disconnect(item); + + m_styleChanges << QMapboxGLStyleChange::removeMapItem(item); + + emit q->sgNodeChanged(); +} + +void QGeoMapMapboxGLPrivate::changeViewportSize(const QSize &) +{ + Q_Q(QGeoMapMapboxGL); + + m_syncState = m_syncState | ViewportSync; + emit q->sgNodeChanged(); +} + +void QGeoMapMapboxGLPrivate::changeCameraData(const QGeoCameraData &) +{ + Q_Q(QGeoMapMapboxGL); + + m_syncState = m_syncState | CameraDataSync; + emit q->sgNodeChanged(); +} + +void QGeoMapMapboxGLPrivate::changeActiveMapType(const QGeoMapType) +{ + Q_Q(QGeoMapMapboxGL); + + m_syncState = m_syncState | MapTypeSync; + emit q->sgNodeChanged(); +} + +void QGeoMapMapboxGLPrivate::syncStyleChanges(QMapboxGL *map) +{ + for (const auto& change : m_styleChanges) { + change->apply(map); + } + + m_styleChanges.clear(); +} + +void QGeoMapMapboxGLPrivate::threadedRenderingHack(QQuickWindow *window, QMapboxGL *map) +{ + // FIXME: Optimal support for threaded rendering needs core changes + // in Mapbox GL Native. Meanwhile we need to set a timer to update + // the map until all the resources are loaded, which is not exactly + // battery friendly, because might trigger more paints than we need. + if (!m_warned) { + m_threadedRendering = window->openglContext()->thread() != QCoreApplication::instance()->thread(); + + if (m_threadedRendering) { + qWarning() << "Threaded rendering is not optimal in the Mapbox GL plugin."; + } + + m_warned = true; + } + + if (m_threadedRendering) { + if (!map->isFullyLoaded()) { + QMetaObject::invokeMethod(&m_refresh, "start", Qt::QueuedConnection); + } else { + QMetaObject::invokeMethod(&m_refresh, "stop", Qt::QueuedConnection); + } + } +} + +/* + * QGeoMapMapboxGL implementation + */ + +QGeoMapMapboxGL::QGeoMapMapboxGL(QGeoMappingManagerEngineMapboxGL *engine, QObject *parent) + : QGeoMap(*new QGeoMapMapboxGLPrivate(engine), parent), m_engine(engine) +{ + Q_D(QGeoMapMapboxGL); + + connect(&d->m_refresh, &QTimer::timeout, this, &QGeoMap::sgNodeChanged); + d->m_refresh.setInterval(250); +} + +QGeoMapMapboxGL::~QGeoMapMapboxGL() +{ +} + +QString QGeoMapMapboxGL::copyrightsStyleSheet() const +{ + return QStringLiteral("* { vertical-align: middle; font-weight: normal }"); +} + +void QGeoMapMapboxGL::setMapboxGLSettings(const QMapboxGLSettings& settings) +{ + Q_D(QGeoMapMapboxGL); + + d->m_settings = settings; + + // If the access token is not set, use the development access token. + // This will only affect mapbox:// styles. + if (d->m_settings.accessToken().isEmpty()) { + d->m_settings.setAccessToken(developmentToken); + } +} + +void QGeoMapMapboxGL::setUseFBO(bool useFBO) +{ + Q_D(QGeoMapMapboxGL); + d->m_useFBO = useFBO; +} + +void QGeoMapMapboxGL::setMapItemsBefore(const QString &before) +{ + Q_D(QGeoMapMapboxGL); + d->m_mapItemsBefore = before; +} + +QGeoMap::Capabilities QGeoMapMapboxGL::capabilities() const +{ + return Capabilities(SupportsVisibleRegion + | SupportsSetBearing + | SupportsAnchoringCoordinate); +} + +QSGNode *QGeoMapMapboxGL::updateSceneGraph(QSGNode *oldNode, QQuickWindow *window) +{ + Q_D(QGeoMapMapboxGL); + return d->updateSceneGraph(oldNode, window); +} + +void QGeoMapMapboxGL::onMapChanged(QMapboxGL::MapChange change) +{ + Q_D(QGeoMapMapboxGL); + + if (change == QMapboxGL::MapChangeDidFinishLoadingStyle || change == QMapboxGL::MapChangeDidFailLoadingMap) { + d->m_styleLoaded = true; + } else if (change == QMapboxGL::MapChangeWillStartLoadingMap) { + d->m_styleLoaded = false; + d->m_styleChanges.clear(); + + for (QDeclarativeGeoMapItemBase *item : d->m_mapItems) + d->m_styleChanges << QMapboxGLStyleChange::addMapItem(item, d->m_mapItemsBefore); + + for (QGeoMapParameter *param : d->m_mapParameters) + d->m_styleChanges << QMapboxGLStyleChange::addMapParameter(param); + } +} + +void QGeoMapMapboxGL::onMapItemPropertyChanged() +{ + Q_D(QGeoMapMapboxGL); + + QDeclarativeGeoMapItemBase *item = static_cast(sender()); + d->m_styleChanges << QMapboxGLStyleSetPaintProperty::fromMapItem(item); + d->m_styleChanges << QMapboxGLStyleSetLayoutProperty::fromMapItem(item); + + emit sgNodeChanged(); +} + +void QGeoMapMapboxGL::onMapItemSubPropertyChanged() +{ + Q_D(QGeoMapMapboxGL); + + QDeclarativeGeoMapItemBase *item = static_cast(sender()->parent()); + d->m_styleChanges << QMapboxGLStyleSetPaintProperty::fromMapItem(item); + + emit sgNodeChanged(); +} + +void QGeoMapMapboxGL::onMapItemUnsupportedPropertyChanged() +{ + // TODO https://bugreports.qt.io/browse/QTBUG-58872 + qWarning() << "Unsupported property for managed Map item"; +} + +void QGeoMapMapboxGL::onMapItemGeometryChanged() +{ + Q_D(QGeoMapMapboxGL); + + QDeclarativeGeoMapItemBase *item = static_cast(sender()); + d->m_styleChanges << QMapboxGLStyleAddSource::fromMapItem(item); + + emit sgNodeChanged(); +} + +void QGeoMapMapboxGL::onParameterPropertyUpdated(QGeoMapParameter *param, const char *) +{ + Q_D(QGeoMapMapboxGL); + + d->m_styleChanges.append(QMapboxGLStyleChange::addMapParameter(param)); + + emit sgNodeChanged(); +} + +void QGeoMapMapboxGL::copyrightsChanged(const QString ©rightsHtml) +{ + Q_D(QGeoMapMapboxGL); + + QString copyrightsHtmlFinal = copyrightsHtml; + + if (d->m_developmentMode) { + copyrightsHtmlFinal.prepend("" + + tr("Development access token, do not use in production.") + " - "); + } + + if (d->m_activeMapType.name().startsWith("mapbox://")) { + copyrightsHtmlFinal = "
    " + + copyrightsHtmlFinal + "
    "; + } + + QGeoMap::copyrightsChanged(copyrightsHtmlFinal); +} diff --git a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h new file mode 100644 index 0000000..0ffaf4e --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPMAPBOXGL_H +#define QGEOMAPMAPBOXGL_H + +#include "qgeomappingmanagerenginemapboxgl.h" +#include +#include + +class QGeoMapMapboxGLPrivate; + +class QGeoMapMapboxGL : public QGeoMap +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoMapMapboxGL) + +public: + QGeoMapMapboxGL(QGeoMappingManagerEngineMapboxGL *engine, QObject *parent); + virtual ~QGeoMapMapboxGL(); + + QString copyrightsStyleSheet() const override; + void setMapboxGLSettings(const QMapboxGLSettings &); + void setUseFBO(bool); + void setMapItemsBefore(const QString &); + Capabilities capabilities() const override; + +private Q_SLOTS: + // QMapboxGL + void onMapChanged(QMapboxGL::MapChange); + + // QDeclarativeGeoMapItemBase + void onMapItemPropertyChanged(); + void onMapItemSubPropertyChanged(); + void onMapItemUnsupportedPropertyChanged(); + void onMapItemGeometryChanged(); + + // QGeoMapParameter + void onParameterPropertyUpdated(QGeoMapParameter *param, const char *propertyName); + +public Q_SLOTS: + void copyrightsChanged(const QString ©rightsHtml); + +private: + QSGNode *updateSceneGraph(QSGNode *oldNode, QQuickWindow *window) override; + + QGeoMappingManagerEngineMapboxGL *m_engine; +}; + +#endif // QGEOMAPMAPBOXGL_H diff --git a/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h new file mode 100644 index 0000000..ffb0620 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qgeomapmapboxgl_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPMAPBOXGL_P_H +#define QGEOMAPMAPBOXGL_P_H + +#include +#include +#include +#include +#include +#include +#include + +class QMapboxGL; +class QMapboxGLStyleChange; + +class QGeoMapMapboxGLPrivate : public QGeoMapPrivate +{ + Q_DECLARE_PUBLIC(QGeoMapMapboxGL) + +public: + QGeoMapMapboxGLPrivate(QGeoMappingManagerEngineMapboxGL *engine); + + ~QGeoMapMapboxGLPrivate(); + + QSGNode *updateSceneGraph(QSGNode *oldNode, QQuickWindow *window); + + void addParameter(QGeoMapParameter *param) override; + void removeParameter(QGeoMapParameter *param) override; + + QGeoMap::ItemTypes supportedMapItemTypes() const override; + void addMapItem(QDeclarativeGeoMapItemBase *item) override; + void removeMapItem(QDeclarativeGeoMapItemBase *item) override; + + /* Data members */ + enum SyncState : int { + NoSync = 0, + ViewportSync = 1 << 0, + CameraDataSync = 1 << 1, + MapTypeSync = 1 << 2 + }; + Q_DECLARE_FLAGS(SyncStates, SyncState); + + QMapboxGLSettings m_settings; + bool m_useFBO = true; + bool m_developmentMode = false; + QString m_mapItemsBefore; + + QTimer m_refresh; + bool m_shouldRefresh = true; + bool m_warned = false; + bool m_threadedRendering = false; + bool m_styleLoaded = false; + + SyncStates m_syncState = NoSync; + + QList> m_styleChanges; + +protected: + void changeViewportSize(const QSize &size) override; + void changeCameraData(const QGeoCameraData &oldCameraData) override; + void changeActiveMapType(const QGeoMapType mapType) override; + +private: + Q_DISABLE_COPY(QGeoMapMapboxGLPrivate); + + void syncStyleChanges(QMapboxGL *map); + void threadedRenderingHack(QQuickWindow *window, QMapboxGL *map); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoMapMapboxGLPrivate::SyncStates) + +#endif // QGEOMAPMAPBOXGL_P_H diff --git a/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp new file mode 100644 index 0000000..cc48afb --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomappingmanagerenginemapboxgl.h" +#include "qgeomapmapboxgl.h" + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QGeoMappingManagerEngineMapboxGL::QGeoMappingManagerEngineMapboxGL(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) +: QGeoMappingManagerEngine() +{ + *error = QGeoServiceProvider::NoError; + errorString->clear(); + + QGeoCameraCapabilities cameraCaps; + cameraCaps.setMinimumZoomLevel(0.0); + cameraCaps.setMaximumZoomLevel(20.0); + cameraCaps.setTileSize(512); + cameraCaps.setSupportsBearing(true); + cameraCaps.setSupportsTilting(true); + cameraCaps.setMinimumTilt(0); + cameraCaps.setMaximumTilt(60); + cameraCaps.setMinimumFieldOfView(36.87); + cameraCaps.setMaximumFieldOfView(36.87); + setCameraCapabilities(cameraCaps); + + QList mapTypes; + int mapId = 0; + const QByteArray pluginName = "mapboxgl"; + + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/streets-v10"), + tr("Streets"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/basic-v9"), + tr("Basic"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("mapbox://styles/mapbox/bright-v9"), + tr("Bright"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::TerrainMap, QStringLiteral("mapbox://styles/mapbox/outdoors-v10"), + tr("Outdoors"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, QStringLiteral("mapbox://styles/mapbox/satellite-v9"), + tr("Satellite"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::HybridMap, QStringLiteral("mapbox://styles/mapbox/satellite-streets-v10"), + tr("Satellite Streets"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/light-v9"), + tr("Light"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::GrayStreetMap, QStringLiteral("mapbox://styles/mapbox/dark-v9"), + tr("Dark"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::TransitMap, QStringLiteral("mapbox://styles/mapbox/navigation-preview-day-v2"), + tr("Navigation Preview Day"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::TransitMap, QStringLiteral("mapbox://styles/mapbox/navigation-preview-night-v2"), + tr("Navigation Preview Night"), false, true, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::CarNavigationMap, QStringLiteral("mapbox://styles/mapbox/navigation-guidance-day-v2"), + tr("Navigation Guidance Day"), false, false, ++mapId, pluginName, cameraCaps); + mapTypes << QGeoMapType(QGeoMapType::CarNavigationMap, QStringLiteral("mapbox://styles/mapbox/navigation-guidance-night-v2"), + tr("Navigation Guidance Night"), false, true, ++mapId, pluginName, cameraCaps); + + if (parameters.contains(QStringLiteral("mapboxgl.mapping.additional_style_urls"))) { + const QString ids = parameters.value(QStringLiteral("mapboxgl.mapping.additional_style_urls")).toString(); + const QStringList idList = ids.split(',', QString::SkipEmptyParts); + + for (auto it = idList.crbegin(), end = idList.crend(); it != end; ++it) { + if ((*it).isEmpty()) + continue; + + mapTypes.prepend(QGeoMapType(QGeoMapType::CustomMap, *it, + tr("User provided style"), false, false, ++mapId, pluginName, cameraCaps)); + } + } + + setSupportedMapTypes(mapTypes); + + if (parameters.contains(QStringLiteral("mapboxgl.access_token"))) { + m_settings.setAccessToken(parameters.value(QStringLiteral("mapboxgl.access_token")).toString()); + } + + bool memoryCache = false; + if (parameters.contains(QStringLiteral("mapboxgl.mapping.cache.memory"))) { + memoryCache = parameters.value(QStringLiteral("mapboxgl.mapping.cache.memory")).toBool(); + m_settings.setCacheDatabasePath(QStringLiteral(":memory:")); + } + + QString cacheDirectory; + if (parameters.contains(QStringLiteral("mapboxgl.mapping.cache.directory"))) { + cacheDirectory = parameters.value(QStringLiteral("mapboxgl.mapping.cache.directory")).toString(); + } else { + cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QStringLiteral("mapboxgl/"); + } + + if (!memoryCache && QDir::root().mkpath(cacheDirectory)) { + m_settings.setCacheDatabasePath(cacheDirectory + "/mapboxgl.db"); + } + + if (parameters.contains(QStringLiteral("mapboxgl.mapping.cache.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("mapboxgl.mapping.cache.size")).toString().toInt(&ok); + + if (ok) + m_settings.setCacheDatabaseMaximumSize(cacheSize); + } + + if (parameters.contains(QStringLiteral("mapboxgl.mapping.use_fbo"))) { + m_useFBO = parameters.value(QStringLiteral("mapboxgl.mapping.use_fbo")).toBool(); + } + + if (parameters.contains(QStringLiteral("mapboxgl.mapping.items.insert_before"))) { + m_mapItemsBefore = parameters.value(QStringLiteral("mapboxgl.mapping.items.insert_before")).toString(); + } + + engineInitialized(); +} + +QGeoMappingManagerEngineMapboxGL::~QGeoMappingManagerEngineMapboxGL() +{ +} + +QGeoMap *QGeoMappingManagerEngineMapboxGL::createMap() +{ + QGeoMapMapboxGL* map = new QGeoMapMapboxGL(this, 0); + map->setMapboxGLSettings(m_settings); + map->setUseFBO(m_useFBO); + map->setMapItemsBefore(m_mapItemsBefore); + + return map; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h new file mode 100644 index 0000000..b3afe77 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qgeomappingmanagerenginemapboxgl.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPPINGMANAGERENGINEMAPBOXGL_H +#define QGEOTILEDMAPPINGMANAGERENGINEMAPBOXGL_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoMappingManagerEngineMapboxGL : public QGeoMappingManagerEngine +{ + Q_OBJECT + +public: + QGeoMappingManagerEngineMapboxGL(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString); + ~QGeoMappingManagerEngineMapboxGL(); + + QGeoMap *createMap() override; + +private: + QMapboxGLSettings m_settings; + bool m_useFBO = true; + QString m_mapItemsBefore; +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEDMAPPINGMANAGERENGINEMAPBOXGL_H diff --git a/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp b/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp new file mode 100644 index 0000000..dd25c99 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderpluginmapboxgl.h" +#include "qgeomappingmanagerenginemapboxgl.h" + +#include + +static void initResources() +{ + Q_INIT_RESOURCE(mapboxgl); +} + +QT_BEGIN_NAMESPACE + +QGeoServiceProviderFactoryMapboxGL::QGeoServiceProviderFactoryMapboxGL() +{ + initResources(); +} + +QGeoCodingManagerEngine *QGeoServiceProviderFactoryMapboxGL::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +QGeoMappingManagerEngine *QGeoServiceProviderFactoryMapboxGL::createMappingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new QGeoMappingManagerEngineMapboxGL(parameters, error, errorString); +} + +QGeoRoutingManagerEngine *QGeoServiceProviderFactoryMapboxGL::createRoutingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +QPlaceManagerEngine *QGeoServiceProviderFactoryMapboxGL::createPlaceManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(parameters) + Q_UNUSED(error) + Q_UNUSED(errorString) + + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.h b/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.h new file mode 100644 index 0000000..b9c0098 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qgeoserviceproviderpluginmapboxgl.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_MAPBOXGL_H +#define QGEOSERVICEPROVIDER_MAPBOXGL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoServiceProviderFactoryMapboxGL: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "mapboxgl_plugin.json") + +public: + QGeoServiceProviderFactoryMapboxGL(); + + QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp new file mode 100644 index 0000000..6c47d3e --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange.cpp @@ -0,0 +1,678 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmapboxglstylechange_p.h" + +#include +#include +#include +#include +#include +#include + +namespace { + +QByteArray formatPropertyName(const QByteArray &name) +{ + QString nameAsString = QString::fromLatin1(name); + static const QRegularExpression camelCaseRegex(QStringLiteral("([a-z0-9])([A-Z])")); + return nameAsString.replace(camelCaseRegex, QStringLiteral("\\1-\\2")).toLower().toLatin1(); +} + +bool isImmutableProperty(const QByteArray &name) +{ + return name == QStringLiteral("type") || name == QStringLiteral("layer"); +} + +QString getId(QDeclarativeGeoMapItemBase *mapItem) +{ + return QStringLiteral("QtLocation-") + + ((mapItem->objectName().isEmpty()) ? QString::number(quint64(mapItem)) : mapItem->objectName()); +} + +// Mapbox GL supports geometry segments that spans above 180 degrees in +// longitude. To keep visual expectations in parity with Qt, we need to adapt +// the coordinates to always use the shortest path when in ambiguity. +bool geoRectangleCrossesDateLine(const QGeoRectangle &rect) { + return rect.topLeft().longitude() > rect.bottomRight().longitude(); +} + +QMapbox::Feature featureFromMapRectangle(QDeclarativeRectangleMapItem *mapItem) +{ + const QGeoRectangle *rect = static_cast(&mapItem->geoShape()); + QMapbox::Coordinate bottomLeft { rect->bottomLeft().latitude(), rect->bottomLeft().longitude() }; + QMapbox::Coordinate topLeft { rect->topLeft().latitude(), rect->topLeft().longitude() }; + QMapbox::Coordinate bottomRight { rect->bottomRight().latitude(), rect->bottomRight().longitude() }; + QMapbox::Coordinate topRight { rect->topRight().latitude(), rect->topRight().longitude() }; + if (geoRectangleCrossesDateLine(*rect)) { + bottomRight.second += 360.0; + topRight.second += 360.0; + } + QMapbox::CoordinatesCollections geometry { { { bottomLeft, bottomRight, topRight, topLeft, bottomLeft } } }; + + return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem)); +} + +QMapbox::Feature featureFromMapCircle(QDeclarativeCircleMapItem *mapItem) +{ + static const int circleSamples = 128; + const QGeoProjectionWebMercator &p = static_cast(mapItem->map()->geoProjection()); + QList path; + QGeoCoordinate leftBound; + QDeclarativeCircleMapItem::calculatePeripheralPoints(path, mapItem->center(), mapItem->radius(), circleSamples, leftBound); + QList pathProjected; + for (const QGeoCoordinate &c : qAsConst(path)) + pathProjected << p.geoToMapProjection(c); + if (QDeclarativeCircleMapItem::crossEarthPole(mapItem->center(), mapItem->radius())) + mapItem->preserveCircleGeometry(pathProjected, mapItem->center(), mapItem->radius(), p); + path.clear(); + for (const QDoubleVector2D &c : qAsConst(pathProjected)) + path << p.mapProjectionToGeo(c); + + + QMapbox::Coordinates coordinates; + for (const QGeoCoordinate &coordinate : path) { + coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() }; + } + coordinates.append(coordinates.first()); // closing the path + QMapbox::CoordinatesCollections geometry { { coordinates } }; + return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem)); +} + +QMapbox::Feature featureFromMapPolygon(QDeclarativePolygonMapItem *mapItem) +{ + const QGeoPath *path = static_cast(&mapItem->geoShape()); + QMapbox::Coordinates coordinates; + const bool crossesDateline = geoRectangleCrossesDateLine(path->boundingGeoRectangle()); + for (const QGeoCoordinate &coordinate : path->path()) { + if (!coordinates.empty() && crossesDateline && qAbs(coordinate.longitude() - coordinates.last().second) > 180.0) { + coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() + (coordinate.longitude() >= 0 ? -360.0 : 360.0) }; + } else { + coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() }; + } + } + + if (!coordinates.empty()) + coordinates.append(coordinates.first()); // closing the path + + QMapbox::CoordinatesCollections geometry { { coordinates } }; + + return QMapbox::Feature(QMapbox::Feature::PolygonType, geometry, {}, getId(mapItem)); +} + +QMapbox::Feature featureFromMapPolyline(QDeclarativePolylineMapItem *mapItem) +{ + const QGeoPath *path = static_cast(&mapItem->geoShape()); + QMapbox::Coordinates coordinates; + const bool crossesDateline = geoRectangleCrossesDateLine(path->boundingGeoRectangle()); + for (const QGeoCoordinate &coordinate : path->path()) { + if (!coordinates.empty() && crossesDateline && qAbs(coordinate.longitude() - coordinates.last().second) > 180.0) { + coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() + (coordinate.longitude() >= 0 ? -360.0 : 360.0) }; + } else { + coordinates << QMapbox::Coordinate { coordinate.latitude(), coordinate.longitude() }; + } + } + QMapbox::CoordinatesCollections geometry { { coordinates } }; + + return QMapbox::Feature(QMapbox::Feature::LineStringType, geometry, {}, getId(mapItem)); +} + +QMapbox::Feature featureFromMapItem(QDeclarativeGeoMapItemBase *item) +{ + switch (item->itemType()) { + case QGeoMap::MapRectangle: + return featureFromMapRectangle(static_cast(item)); + case QGeoMap::MapCircle: + return featureFromMapCircle(static_cast(item)); + case QGeoMap::MapPolygon: + return featureFromMapPolygon(static_cast(item)); + case QGeoMap::MapPolyline: + return featureFromMapPolyline(static_cast(item)); + default: + qWarning() << "Unsupported QGeoMap item type: " << item->itemType(); + return QMapbox::Feature(); + } +} + +QList getAllPropertyNamesList(QObject *object) +{ + const QMetaObject *metaObject = object->metaObject(); + QList propertyNames(object->dynamicPropertyNames()); + for (int i = metaObject->propertyOffset(); i < metaObject->propertyCount(); ++i) { + propertyNames.append(metaObject->property(i).name()); + } + return propertyNames; +} + +} // namespace + + +// QMapboxGLStyleChange + +QList> QMapboxGLStyleChange::addMapParameter(QGeoMapParameter *param) +{ + static const QStringList acceptedParameterTypes = QStringList() + << QStringLiteral("paint") << QStringLiteral("layout") << QStringLiteral("filter") + << QStringLiteral("layer") << QStringLiteral("source") << QStringLiteral("image") + << QStringLiteral("margins"); + + QList> changes; + + switch (acceptedParameterTypes.indexOf(param->type())) { + case -1: + qWarning() << "Invalid value for property 'type': " + param->type(); + break; + case 0: // paint + changes << QMapboxGLStyleSetPaintProperty::fromMapParameter(param); + break; + case 1: // layout + changes << QMapboxGLStyleSetLayoutProperty::fromMapParameter(param); + break; + case 2: // filter + changes << QMapboxGLStyleSetFilter::fromMapParameter(param); + break; + case 3: // layer + changes << QMapboxGLStyleAddLayer::fromMapParameter(param); + break; + case 4: // source + changes << QMapboxGLStyleAddSource::fromMapParameter(param); + break; + case 5: // image + changes << QMapboxGLStyleAddImage::fromMapParameter(param); + break; + case 6: // margins + changes << QMapboxGLMapMargins::fromMapParameter(param); + break; + } + + return changes; +} + +QList> QMapboxGLStyleChange::addMapItem(QDeclarativeGeoMapItemBase *item, const QString &before) +{ + QList> changes; + + switch (item->itemType()) { + case QGeoMap::MapRectangle: + case QGeoMap::MapCircle: + case QGeoMap::MapPolygon: + case QGeoMap::MapPolyline: + break; + default: + qWarning() << "Unsupported QGeoMap item type: " << item->itemType(); + return changes; + } + + QMapbox::Feature feature = featureFromMapItem(item); + + changes << QMapboxGLStyleAddLayer::fromFeature(feature, before); + changes << QMapboxGLStyleAddSource::fromFeature(feature); + changes << QMapboxGLStyleSetPaintProperty::fromMapItem(item); + changes << QMapboxGLStyleSetLayoutProperty::fromMapItem(item); + + return changes; +} + +QList> QMapboxGLStyleChange::removeMapItem(QDeclarativeGeoMapItemBase *item) +{ + QList> changes; + + const QString id = getId(item); + + changes << QSharedPointer(new QMapboxGLStyleRemoveLayer(id)); + changes << QSharedPointer(new QMapboxGLStyleRemoveSource(id)); + + return changes; +} + +// QMapboxGLStyleSetLayoutProperty + +void QMapboxGLStyleSetLayoutProperty::apply(QMapboxGL *map) +{ + map->setLayoutProperty(m_layer, m_property, m_value); +} + +QList> QMapboxGLStyleSetLayoutProperty::fromMapParameter(QGeoMapParameter *param) +{ + Q_ASSERT(param->type() == "layout"); + + QList> changes; + + QList propertyNames = getAllPropertyNamesList(param); + for (const QByteArray &propertyName : propertyNames) { + if (isImmutableProperty(propertyName)) + continue; + + auto layout = new QMapboxGLStyleSetLayoutProperty(); + + layout->m_value = param->property(propertyName); + if (layout->m_value.canConvert()) { + layout->m_value = layout->m_value.value().toVariant(); + } + + layout->m_layer = param->property("layer").toString(); + layout->m_property = formatPropertyName(propertyName); + + changes << QSharedPointer(layout); + } + + return changes; +} + +QList> QMapboxGLStyleSetLayoutProperty::fromMapItem(QDeclarativeGeoMapItemBase *item) +{ + QList> changes; + + switch (item->itemType()) { + case QGeoMap::MapPolyline: + changes = fromMapItem(static_cast(item)); + default: + break; + } + + changes << QSharedPointer( + new QMapboxGLStyleSetLayoutProperty(getId(item), QStringLiteral("visibility"), + item->isVisible() ? QStringLiteral("visible") : QStringLiteral("none"))); + + return changes; +} + +QList> QMapboxGLStyleSetLayoutProperty::fromMapItem(QDeclarativePolylineMapItem *item) +{ + QList> changes; + changes.reserve(2); + + const QString id = getId(item); + + changes << QSharedPointer( + new QMapboxGLStyleSetLayoutProperty(id, QStringLiteral("line-cap"), QStringLiteral("square"))); + changes << QSharedPointer( + new QMapboxGLStyleSetLayoutProperty(id, QStringLiteral("line-join"), QStringLiteral("bevel"))); + + return changes; +} + +QMapboxGLStyleSetLayoutProperty::QMapboxGLStyleSetLayoutProperty(const QString& layer, const QString& property, const QVariant &value) + : m_layer(layer), m_property(property), m_value(value) +{ +} + +// QMapboxGLStyleSetPaintProperty + +QMapboxGLStyleSetPaintProperty::QMapboxGLStyleSetPaintProperty(const QString& layer, const QString& property, const QVariant &value) + : m_layer(layer), m_property(property), m_value(value) +{ +} + +void QMapboxGLStyleSetPaintProperty::apply(QMapboxGL *map) +{ + map->setPaintProperty(m_layer, m_property, m_value); +} + +QList> QMapboxGLStyleSetPaintProperty::fromMapParameter(QGeoMapParameter *param) +{ + Q_ASSERT(param->type() == "paint"); + + QList> changes; + + QList propertyNames = getAllPropertyNamesList(param); + for (const QByteArray &propertyName : propertyNames) { + if (isImmutableProperty(propertyName)) + continue; + + auto paint = new QMapboxGLStyleSetPaintProperty(); + + paint->m_value = param->property(propertyName); + if (paint->m_value.canConvert()) { + paint->m_value = paint->m_value.value().toVariant(); + } + + paint->m_layer = param->property("layer").toString(); + paint->m_property = formatPropertyName(propertyName); + + changes << QSharedPointer(paint); + } + + return changes; +} + +QList> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativeGeoMapItemBase *item) +{ + switch (item->itemType()) { + case QGeoMap::MapRectangle: + return fromMapItem(static_cast(item)); + case QGeoMap::MapCircle: + return fromMapItem(static_cast(item)); + case QGeoMap::MapPolygon: + return fromMapItem(static_cast(item)); + case QGeoMap::MapPolyline: + return fromMapItem(static_cast(item)); + default: + qWarning() << "Unsupported QGeoMap item type: " << item->itemType(); + return QList>(); + } +} + +QList> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativeRectangleMapItem *item) +{ + QList> changes; + changes.reserve(3); + + const QString id = getId(item); + + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-opacity"), item->color().alphaF() * item->mapItemOpacity())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-color"), item->color())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-outline-color"), item->border()->color())); + + return changes; +} + +QList> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativeCircleMapItem *item) +{ + QList> changes; + changes.reserve(3); + + const QString id = getId(item); + + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-opacity"), item->color().alphaF() * item->mapItemOpacity())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-color"), item->color())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-outline-color"), item->border()->color())); + + return changes; +} + +QList> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativePolygonMapItem *item) +{ + QList> changes; + changes.reserve(3); + + const QString id = getId(item); + + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-opacity"), item->color().alphaF() * item->mapItemOpacity())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-color"), item->color())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("fill-outline-color"), item->border()->color())); + + return changes; +} + +QList> QMapboxGLStyleSetPaintProperty::fromMapItem(QDeclarativePolylineMapItem *item) +{ + QList> changes; + changes.reserve(3); + + const QString id = getId(item); + + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("line-opacity"), item->line()->color().alphaF() * item->mapItemOpacity())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("line-color"), item->line()->color())); + changes << QSharedPointer( + new QMapboxGLStyleSetPaintProperty(id, QStringLiteral("line-width"), item->line()->width())); + + return changes; +} + +// QMapboxGLStyleAddLayer + +void QMapboxGLStyleAddLayer::apply(QMapboxGL *map) +{ + map->addLayer(m_params, m_before); +} + +QSharedPointer QMapboxGLStyleAddLayer::fromMapParameter(QGeoMapParameter *param) +{ + Q_ASSERT(param->type() == "layer"); + + auto layer = new QMapboxGLStyleAddLayer(); + + static const QStringList layerProperties = QStringList() + << QStringLiteral("name") << QStringLiteral("layerType") << QStringLiteral("before"); + + QList propertyNames = getAllPropertyNamesList(param); + for (const QByteArray &propertyName : propertyNames) { + if (isImmutableProperty(propertyName)) + continue; + + const QVariant value = param->property(propertyName); + + switch (layerProperties.indexOf(propertyName)) { + case -1: + layer->m_params[formatPropertyName(propertyName)] = value; + break; + case 0: // name + layer->m_params[QStringLiteral("id")] = value; + break; + case 1: // layerType + layer->m_params[QStringLiteral("type")] = value; + break; + case 2: // before + layer->m_before = value.toString(); + break; + } + } + + return QSharedPointer(layer); +} + +QSharedPointer QMapboxGLStyleAddLayer::fromFeature(const QMapbox::Feature &feature, const QString &before) +{ + auto layer = new QMapboxGLStyleAddLayer(); + layer->m_params[QStringLiteral("id")] = feature.id; + layer->m_params[QStringLiteral("source")] = feature.id; + + switch (feature.type) { + case QMapbox::Feature::PointType: + layer->m_params[QStringLiteral("type")] = QStringLiteral("circle"); + break; + case QMapbox::Feature::LineStringType: + layer->m_params[QStringLiteral("type")] = QStringLiteral("line"); + break; + case QMapbox::Feature::PolygonType: + layer->m_params[QStringLiteral("type")] = QStringLiteral("fill"); + break; + } + + layer->m_before = before; + + return QSharedPointer(layer); +} + + +// QMapboxGLStyleRemoveLayer + +void QMapboxGLStyleRemoveLayer::apply(QMapboxGL *map) +{ + map->removeLayer(m_id); +} + +QMapboxGLStyleRemoveLayer::QMapboxGLStyleRemoveLayer(const QString &id) : m_id(id) +{ +} + + +// QMapboxGLStyleAddSource + +void QMapboxGLStyleAddSource::apply(QMapboxGL *map) +{ + map->updateSource(m_id, m_params); +} + +QSharedPointer QMapboxGLStyleAddSource::fromMapParameter(QGeoMapParameter *param) +{ + Q_ASSERT(param->type() == "source"); + + static const QStringList acceptedSourceTypes = QStringList() + << QStringLiteral("vector") << QStringLiteral("raster") << QStringLiteral("raster-dem") << QStringLiteral("geojson"); + + QString sourceType = param->property("sourceType").toString(); + + auto source = new QMapboxGLStyleAddSource(); + source->m_id = param->property("name").toString(); + source->m_params[QStringLiteral("type")] = sourceType; + + switch (acceptedSourceTypes.indexOf(sourceType)) { + case -1: + qWarning() << "Invalid value for property 'sourceType': " + sourceType; + break; + case 0: // vector + case 1: // raster + case 2: // raster-dem + source->m_params[QStringLiteral("url")] = param->property("url"); + break; + case 3: { // geojson + auto data = param->property("data").toString(); + if (data.startsWith(':')) { + QFile geojson(data); + geojson.open(QIODevice::ReadOnly); + source->m_params[QStringLiteral("data")] = geojson.readAll(); + } else { + source->m_params[QStringLiteral("data")] = data.toUtf8(); + } + } break; + } + + return QSharedPointer(source); +} + +QSharedPointer QMapboxGLStyleAddSource::fromFeature(const QMapbox::Feature &feature) +{ + auto source = new QMapboxGLStyleAddSource(); + + source->m_id = feature.id.toString(); + source->m_params[QStringLiteral("type")] = QStringLiteral("geojson"); + source->m_params[QStringLiteral("data")] = QVariant::fromValue(feature); + + return QSharedPointer(source); +} + +QSharedPointer QMapboxGLStyleAddSource::fromMapItem(QDeclarativeGeoMapItemBase *item) +{ + return fromFeature(featureFromMapItem(item)); +} + + +// QMapboxGLStyleRemoveSource + +void QMapboxGLStyleRemoveSource::apply(QMapboxGL *map) +{ + map->removeSource(m_id); +} + +QMapboxGLStyleRemoveSource::QMapboxGLStyleRemoveSource(const QString &id) : m_id(id) +{ +} + + +// QMapboxGLStyleSetFilter + +void QMapboxGLStyleSetFilter::apply(QMapboxGL *map) +{ + map->setFilter(m_layer, m_filter); +} + +QSharedPointer QMapboxGLStyleSetFilter::fromMapParameter(QGeoMapParameter *param) +{ + Q_ASSERT(param->type() == "filter"); + + auto filter = new QMapboxGLStyleSetFilter(); + filter->m_layer = param->property("layer").toString(); + filter->m_filter = param->property("filter"); + + return QSharedPointer(filter); +} + + +// QMapboxGLStyleAddImage + +void QMapboxGLStyleAddImage::apply(QMapboxGL *map) +{ + map->addImage(m_name, m_sprite); +} + +QSharedPointer QMapboxGLStyleAddImage::fromMapParameter(QGeoMapParameter *param) +{ + Q_ASSERT(param->type() == "image"); + + auto image = new QMapboxGLStyleAddImage(); + image->m_name = param->property("name").toString(); + image->m_sprite = QImage(param->property("sprite").toString()); + + return QSharedPointer(image); +} + +// QMapboxGLMapMargins + +void QMapboxGLMapMargins::apply(QMapboxGL *map) +{ + // FIXME: Qt projection handlers are not yet aware of these margins, + // thus map items placement, {to,from}Coordinate, mouse area, etc. + // will require manual fixups. + map->setMargins(m_margins); +} + +QSharedPointer QMapboxGLMapMargins::fromMapParameter(QGeoMapParameter *param) +{ + Q_ASSERT(param->type() == "margins"); + + auto mapMargins = new QMapboxGLMapMargins(); + + QVariant leftMargin = param->property("left"); + if (leftMargin.isValid()) + mapMargins->m_margins.setLeft(leftMargin.toInt()); + + QVariant topMargin = param->property("top"); + if (topMargin.isValid()) + mapMargins->m_margins.setTop(topMargin.toInt()); + + QVariant rightMargin = param->property("right"); + if (rightMargin.isValid()) + mapMargins->m_margins.setRight(rightMargin.toInt()); + + QVariant bottomMargin = param->property("bottom"); + if (bottomMargin.isValid()) + mapMargins->m_margins.setBottom(bottomMargin.toInt()); + + return QSharedPointer(mapMargins); +} diff --git a/src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h new file mode 100644 index 0000000..38aa87f --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qmapboxglstylechange_p.h @@ -0,0 +1,206 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMAPBOXGLSTYLECHANGE_P_H +#define QQMAPBOXGLSTYLECHANGE_P_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class QMapboxGLStyleChange +{ +public: + virtual ~QMapboxGLStyleChange() = default; + + static QList> addMapParameter(QGeoMapParameter *); + static QList> addMapItem(QDeclarativeGeoMapItemBase *, const QString &before); + static QList> removeMapItem(QDeclarativeGeoMapItemBase *); + + virtual void apply(QMapboxGL *map) = 0; +}; + +class QMapboxGLStyleSetLayoutProperty : public QMapboxGLStyleChange +{ +public: + static QList> fromMapParameter(QGeoMapParameter *); + static QList> fromMapItem(QDeclarativeGeoMapItemBase *); + + void apply(QMapboxGL *map) override; + +private: + static QList> fromMapItem(QDeclarativePolylineMapItem *); + + QMapboxGLStyleSetLayoutProperty() = default; + QMapboxGLStyleSetLayoutProperty(const QString &layer, const QString &property, const QVariant &value); + + QString m_layer; + QString m_property; + QVariant m_value; +}; + +class QMapboxGLStyleSetPaintProperty : public QMapboxGLStyleChange +{ +public: + static QList> fromMapParameter(QGeoMapParameter *); + static QList> fromMapItem(QDeclarativeGeoMapItemBase *); + + void apply(QMapboxGL *map) override; + +private: + static QList> fromMapItem(QDeclarativeRectangleMapItem *); + static QList> fromMapItem(QDeclarativeCircleMapItem *); + static QList> fromMapItem(QDeclarativePolygonMapItem *); + static QList> fromMapItem(QDeclarativePolylineMapItem *); + + QMapboxGLStyleSetPaintProperty() = default; + QMapboxGLStyleSetPaintProperty(const QString &layer, const QString &property, const QVariant &value); + + QString m_layer; + QString m_property; + QVariant m_value; +}; + +class QMapboxGLStyleAddLayer : public QMapboxGLStyleChange +{ +public: + static QSharedPointer fromMapParameter(QGeoMapParameter *); + static QSharedPointer fromFeature(const QMapbox::Feature &feature, const QString &before); + + void apply(QMapboxGL *map) override; + +private: + QMapboxGLStyleAddLayer() = default; + + QVariantMap m_params; + QString m_before; +}; + +class QMapboxGLStyleRemoveLayer : public QMapboxGLStyleChange +{ +public: + explicit QMapboxGLStyleRemoveLayer(const QString &id); + + void apply(QMapboxGL *map) override; + +private: + QMapboxGLStyleRemoveLayer() = default; + + QString m_id; +}; + +class QMapboxGLStyleAddSource : public QMapboxGLStyleChange +{ +public: + static QSharedPointer fromMapParameter(QGeoMapParameter *); + static QSharedPointer fromFeature(const QMapbox::Feature &feature); + static QSharedPointer fromMapItem(QDeclarativeGeoMapItemBase *); + + void apply(QMapboxGL *map) override; + +private: + QMapboxGLStyleAddSource() = default; + + QString m_id; + QVariantMap m_params; +}; + +class QMapboxGLStyleRemoveSource : public QMapboxGLStyleChange +{ +public: + explicit QMapboxGLStyleRemoveSource(const QString &id); + + void apply(QMapboxGL *map) override; + +private: + QMapboxGLStyleRemoveSource() = default; + + QString m_id; +}; + +class QMapboxGLStyleSetFilter : public QMapboxGLStyleChange +{ +public: + static QSharedPointer fromMapParameter(QGeoMapParameter *); + + void apply(QMapboxGL *map) override; + +private: + QMapboxGLStyleSetFilter() = default; + + QString m_layer; + QVariant m_filter; +}; + +class QMapboxGLStyleAddImage : public QMapboxGLStyleChange +{ +public: + static QSharedPointer fromMapParameter(QGeoMapParameter *); + + void apply(QMapboxGL *map) override; + +private: + QMapboxGLStyleAddImage() = default; + + QString m_name; + QImage m_sprite; +}; + +class QMapboxGLMapMargins : public QMapboxGLStyleChange +{ +public: + static QSharedPointer fromMapParameter(QGeoMapParameter *); + + void apply(QMapboxGL *map) override; + +private: + QMapboxGLMapMargins() = default; + + QMargins m_margins; +}; + +#endif // QQMAPBOXGLSTYLECHANGE_P_H diff --git a/src/plugins/geoservices/mapboxgl/qsgmapboxglnode.cpp b/src/plugins/geoservices/mapboxgl/qsgmapboxglnode.cpp new file mode 100644 index 0000000..ed594b5 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qsgmapboxglnode.cpp @@ -0,0 +1,160 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsgmapboxglnode.h" +#include "qgeomapmapboxgl.h" + +#include +#include + +// QSGMapboxGLTextureNode + +static const QSize minTextureSize = QSize(64, 64); + +QSGMapboxGLTextureNode::QSGMapboxGLTextureNode(const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio, QGeoMapMapboxGL *geoMap) + : QSGSimpleTextureNode() +{ + setTextureCoordinatesTransform(QSGSimpleTextureNode::MirrorVertically); + setFiltering(QSGTexture::Linear); + + m_map.reset(new QMapboxGL(nullptr, settings, size.expandedTo(minTextureSize), pixelRatio)); + + QObject::connect(m_map.data(), &QMapboxGL::needsRendering, geoMap, &QGeoMap::sgNodeChanged); + QObject::connect(m_map.data(), &QMapboxGL::copyrightsChanged, geoMap, + static_cast(&QGeoMapMapboxGL::copyrightsChanged)); +} + +void QSGMapboxGLTextureNode::resize(const QSize &size, qreal pixelRatio) +{ + const QSize& minSize = size.expandedTo(minTextureSize); + const QSize fbSize = minSize * pixelRatio; + m_map->resize(minSize); + + m_fbo.reset(new QOpenGLFramebufferObject(fbSize, QOpenGLFramebufferObject::CombinedDepthStencil)); + m_map->setFramebufferObject(m_fbo->handle(), fbSize); + + QSGPlainTexture *fboTexture = static_cast(texture()); + if (!fboTexture) { + fboTexture = new QSGPlainTexture; + fboTexture->setHasAlphaChannel(true); + } + + fboTexture->setTextureId(m_fbo->texture()); + fboTexture->setTextureSize(fbSize); + + if (!texture()) { + setTexture(fboTexture); + setOwnsTexture(true); + } + + setRect(QRectF(QPointF(), minSize)); + markDirty(QSGNode::DirtyGeometry); +} + +void QSGMapboxGLTextureNode::render(QQuickWindow *window) +{ + QOpenGLFunctions *f = window->openglContext()->functions(); + f->glViewport(0, 0, m_fbo->width(), m_fbo->height()); + + GLint alignment; + f->glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment); + + m_fbo->bind(); + + f->glClearColor(0.f, 0.f, 0.f, 0.f); + f->glColorMask(true, true, true, true); + f->glClear(GL_COLOR_BUFFER_BIT); + + m_map->render(); + m_fbo->release(); + + // QTBUG-62861 + f->glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); + + window->resetOpenGLState(); + markDirty(QSGNode::DirtyMaterial); +} + +QMapboxGL* QSGMapboxGLTextureNode::map() const +{ + return m_map.data(); +} + +// QSGMapboxGLRenderNode + +QSGMapboxGLRenderNode::QSGMapboxGLRenderNode(const QMapboxGLSettings &settings, const QSize &size, qreal pixelRatio, QGeoMapMapboxGL *geoMap) + : QSGRenderNode() +{ + m_map.reset(new QMapboxGL(nullptr, settings, size, pixelRatio)); + QObject::connect(m_map.data(), &QMapboxGL::needsRendering, geoMap, &QGeoMap::sgNodeChanged); + QObject::connect(m_map.data(), &QMapboxGL::copyrightsChanged, geoMap, + static_cast(&QGeoMapMapboxGL::copyrightsChanged)); +} + +QMapboxGL* QSGMapboxGLRenderNode::map() const +{ + return m_map.data(); +} + +void QSGMapboxGLRenderNode::render(const RenderState *state) +{ + // QMapboxGL assumes we've prepared the viewport prior to render(). + QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions(); + f->glViewport(state->scissorRect().x(), state->scissorRect().y(), state->scissorRect().width(), state->scissorRect().height()); + f->glScissor(state->scissorRect().x(), state->scissorRect().y(), state->scissorRect().width(), state->scissorRect().height()); + f->glEnable(GL_SCISSOR_TEST); + + GLint alignment; + f->glGetIntegerv(GL_UNPACK_ALIGNMENT, &alignment); + + m_map->render(); + + // QTBUG-62861 + f->glPixelStorei(GL_UNPACK_ALIGNMENT, alignment); +} + +QSGRenderNode::StateFlags QSGMapboxGLRenderNode::changedStates() const +{ + return QSGRenderNode::DepthState + | QSGRenderNode::StencilState + | QSGRenderNode::ScissorState + | QSGRenderNode::ColorState + | QSGRenderNode::BlendState + | QSGRenderNode::ViewportState + | QSGRenderNode::RenderTargetState; +} + diff --git a/src/plugins/geoservices/mapboxgl/qsgmapboxglnode.h b/src/plugins/geoservices/mapboxgl/qsgmapboxglnode.h new file mode 100644 index 0000000..f89ee14 --- /dev/null +++ b/src/plugins/geoservices/mapboxgl/qsgmapboxglnode.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Copyright (C) 2017 Mapbox, Inc. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSGMAPBOXGLNODE_H +#define QSGMAPBOXGLNODE_H + +#include +#include +#include +#include +#include + +#include + +class QGeoMapMapboxGL; + +class QSGMapboxGLTextureNode : public QSGSimpleTextureNode +{ +public: + QSGMapboxGLTextureNode(const QMapboxGLSettings &, const QSize &, qreal pixelRatio, QGeoMapMapboxGL *geoMap); + + QMapboxGL* map() const; + + void resize(const QSize &size, qreal pixelRatio); + void render(QQuickWindow *); + +private: + QScopedPointer m_map; + QScopedPointer m_fbo; +}; + +class QSGMapboxGLRenderNode : public QSGRenderNode +{ +public: + QSGMapboxGLRenderNode(const QMapboxGLSettings &, const QSize &, qreal pixelRatio, QGeoMapMapboxGL *geoMap); + + QMapboxGL* map() const; + + // QSGRenderNode + void render(const RenderState *state) override; + StateFlags changedStates() const override; + +private: + QScopedPointer m_map; +}; + +#endif // QSGMAPBOXGLNODE_H diff --git a/src/plugins/geoservices/nokia/logo.png b/src/plugins/geoservices/nokia/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ba4219178808c7b6866765c54e1f1b48b044fec6 GIT binary patch literal 1217 zcmV;y1U~zTP)ks2w#4tjcQ8J7 z&(m!7?!k}T&dhG+&Ac|_^~CS@gqTEvmY_L!1D=9s;Je)mr9)`)GM5oxA&55GbHQ=2 z8a%LiF)TzB2nSoiE~S7-MKBti2V+86i6vkXm9djLU>nDUoDyxpOw}ena2kA8?d8ET z&@j}L7zb(?&zC?~&=;&zWAnjs@FG-{s73Q>s@mr>Ds@vk^bJe{NuWKb7HlPEfy*Ek z0CewnFXMo(SI96f!ch|qXMaHJEO+; zbURPTDi1X*poPU4bApoSCW0#rwTY_#rTB-N=F|$*QEF@sI2;u!FIRGj##c@O-`oB=#xnE4jf?YUje*E ze+?CWeR2xSaVt7?0!OKUPrRz6@JRw!zQE7-eu5O1Avta-@tV$QC;bdFksaCyVp#CW zBnvrl`FkCV1XI9Q&mXH&G9fmcVNzDT64*rNw31a|u|>PQU!Mytj{h9|a-$TNN%vmH+z3PRSN9*^ha7 z0#L+jC={?b<=C?=6!;Oq4rp1)QrmSUs#|2zG|-VH^?lVRXM_phg3b4)+vJ84RYJ<* z)~cSF3T}Z2(8%b^W-XM=8tR(*mL$I6;G^40NEj?@Jl_DHzz}fINNBPHccXFs5hv zc}MPq`hX5pKr(9&BL_$Z?#}bDNEUr2gR8%X6|E$}UOaM6)kn_%39(VM(>P{UIXyj5 zW6LqK&M{V4F0>BQR2taqQam^d8iVz8cJs`9zW5n7et#sZ%ow9xPF%7@kBr02I)+j! z`9;(8s&+XSA7kwk$0}Bb)TK@+afUU$mdD(Yhcu6K4wooWevf5=S1bwR{Z5`mUvoNQ zGdiG?l1fMa>BPsxCpNQ&-$c7NC7L18tow`T3*mmE+Xwnh8P)!g_eQ&EYK|3c9)7ft z*Mwy(6fs5aOBXVxuT{rQ(_vGnY&9x=hYOPFbltDd6SBn{)XIC*F4QtI>M$xEVKMeO zL-FI1KL2WSNaDp-zMu0#ALIED3%@Lju@X`*vMg~`2@jLebU!wm$YkY{qujxqAl)X~ zI%W!Q-K-`i=5#_8eQ8wWt2$rz=exy~noWjQwv}iU-x4Oxee4l5C5<2HX+zYBIyE9r)K$JeAJ=g-6bOgGMZ^ fql4wxzXcco_%!)2?dJ;100000NkvXXu0mjfxv@EH literal 0 HcmV?d00001 diff --git a/src/plugins/geoservices/nokia/marclanguagecodes.h b/src/plugins/geoservices/nokia/marclanguagecodes.h new file mode 100644 index 0000000..69e309f --- /dev/null +++ b/src/plugins/geoservices/nokia/marclanguagecodes.h @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MARCLANGUAGECODES_H_ +#define MARCLANGUAGECODES_H_ + +// MARC language codes for GeoCoding service language/locale support +// http://www.loc.gov/marc/languages/language_code.html +// Order matches QLocale::Language + +QT_BEGIN_NAMESPACE + +static const unsigned char marc_language_code_list[] = + "\0\0\0" // Unused + "\0\0\0" // C + "abk" // Abkhazian + "\0\0\0" // Oromo + "aar" // Afar + "afr" // Afrikaans + "alb" // Albanian + "amh" // Amharic + "ara" // Arabic + "arm" // Armenian + "asm" // Assamese + "aym" // Aymara + "aze" // Azerbaijani + "bak" // Bashkir + "baq" // Basque + "ben" // Bengali + "\0\0\0" // Dzongkha + "bih" // Bihari + "bis" // Bislama + "bre" // Breton + "bul" // Bulgarian + "bur" // Burmese + "bel" // Belarusian + "khm" // Khmer + "cat" // Catalan + "chi" // Chinese + "cos" // Corsican + "hrv" // Croatian + "cze" // Czech + "dan" // Danish + "dut" // Dutch + "eng" // English + "epo" // Esperanto + "est" // Estonian + "fao" // Faroese + "fij" // Fijian + "fin" // Finnish + "fre" // French + "fry" // WesternFrisian + "gla" // Gaelic + "glg" // Galician + "geo" // Georgian + "ger" // German + "gre" // Greek + "\0\0\0" // Greenlandic + "grn" // Guarani + "guj" // Gujarati + "hau" // Hausa + "heb" // Hebrew + "hin" // Hindi + "hun" // Hungarian + "ice" // Icelandic + "ind" // Indonesian + "ina" // Interlingua + "ile" // Interlingue + "iku" // Inuktitut + "ipk" // Inupiak + "gle" // Irish + "ita" // Italian + "jpn" // Japanese + "jav" // Javanese + "kan" // Kannada + "kas" // Kashmiri + "kaz" // Kazakh + "kin" // Kinyarwanda + "kir" // Kirghiz + "kor" // Korean + "kur" // Kurdish + "\0\0\0" // Rundi + "lao" // Lao + "lat" // Latin + "lav" // Latvian + "lin" // Lingala + "lit" // Lithuanian + "mac" // Macedonian + "mlg" // Malagasy + "may" // Malay + "mal" // Malayalam + "mlt" // Maltese + "mao" // Maori + "mar" // Marathi + "mah" // Marshallese + "mon" // Mongolian + "nau" // NauruLanguage + "nep" // Nepali + "nor" // NorwegianBokmal + "oci" // Occitan + "ori" // Oriya + "\0\0\0" // Pashto + "per" // Persian + "pol" // Polish + "por" // Portuguese + "pan" // Punjabi + "que" // Quechua + "roh" // Romansh + "rum" // Romanian + "rus" // Russian + "smo" // Samoan + "sag" // Sango + "san" // Sanskrit + "srp" // Serbian + "oss" // Ossetic + "\0\0\0" // SouthernSotho + "\0\0\0" // Tswana + "sna" // Shona + "snd" // Sindhi + "\0\0\0" // Sinhala + "\0\0\0" // Swati + "slo" // Slovak + "slv" // Slovenian + "som" // Somali + "spa" // Spanish + "sun" // Sundanese + "swa" // Swahili + "swe" // Swedish + "srd" // Sardinian + "tgk" // Tajik + "tam" // Tamil + "tat" // Tatar + "tel" // Telugu + "tha" // Thai + "tib" // Tibetan + "tir" // Tigrinya + "tog" // Tongan + "tso" // Tsonga + "tur" // Turkish + "tuk" // Turkmen + "tah" // Tahitian + "uig" // Uigur + "ukr" // Ukrainian + "urd" // Urdu + "uzb" // Uzbek + "vie" // Vietnamese + "vol" // Volapuk + "wel" // Welsh + "wol" // Wolof + "xho" // Xhosa + "yid" // Yiddish + "yor" // Yoruba + "zha" // Zhuang + "zul" // Zulu + "nno" // NorwegianNynorsk + "bos" // Bosnian + "div" // Divehi + "glv" // Manx + "cor" // Cornish + "aka" // Akan + "kok" // Konkani + "gaa" // Ga + "ibo" // Igbo + "kam" // Kamba + "syc" // Syriac + "\0\0\0" // Blin + "\0\0\0" // Geez + "\0\0\0" // Koro + "sid" // Sidamo + "\0\0\0" // Atsam + "tig" // Tigre + "\0\0\0" // Jju + "fur" // Friulian + "ven" // Venda + "ewe" // Ewe + "\0\0\0" // Walamo + "haw" // Hawaiian + "\0\0\0" // Tyap + "\0\0\0" // Nyanja + "fil" // Filipino + "gsw" // SwissGerman + "iii" // SichuanYi + "kpe" // Kpelle + "nds" // LowGerman + "nbl" // SouthNdebele + "nso" // NorthernSotho + "sme" // NorthernSami + "\0\0\0" // Taroko + "\0\0\0" // Gusii + "\0\0\0" // Taita + "ful" // Fulah + "kik" // Kikuyu + "\0\0\0" // Samburu + "\0\0\0" // Sena + "nde" // NorthNdebele + "\0\0\0" // Rombo + "\0\0\0" // Tachelhit + "kab" // Kabyle + "nyn" // Nyankole + "\0\0\0" // Bena + "\0\0\0" // Vunjo + "bam" // Bambara + "\0\0\0" // Embu + "chr" // Cherokee + "\0\0\0" // Morisyen + "\0\0\0" // Makonde + "\0\0\0" // Langi + "lug" // Ganda + "bem" // Bemba + "\0\0\0" // Kabuverdianu + "\0\0\0" // Meru + "\0\0\0" // Kalenjin + "\0\0\0" // Nama + "\0\0\0" // Machame + "\0\0\0" // Colognian + "mas" // Masai + "\0\0\0" // Soga + "\0\0\0" // Luyia + "\0\0\0" // Asu + "\0\0\0" // Teso + "\0\0\0" // Saho + "\0\0\0" // KoyraChiini + "\0\0\0" // Rwa + "luo" // Luo + "\0\0\0" // Chiga + "\0\0\0" // CentralMoroccoTamazight + "\0\0\0" // KoyraboroSenni + "\0\0\0" // Shambala + "\0\0\0" // Bodo + "ava" // Avaric + "cha" // Chamorro + "che" // Chechen + "chu" // Church + "chv" // Chuvash + "cre" // Cree + "hat" // Haitian + "her" // Herero + "hmo" // HiriMotu + "kau" // Kanuri + "kom" // Komi + "kon" // Kongo + "\0\0\0" // Kwanyama + "lim" // Limburgish + "lub" // LubaKatanga + "ltz" // Luxembourgish + "\0\0\0" // Navaho + "ndo" // Ndonga + "oji" // Ojibwa + "pli" // Pali + "wln" // Walloon + "\0\0\0" // Aghem + "bas" // Basaa + "\0\0\0" // Zarma + "dua" // Duala + "\0\0\0" // JolaFonyi + "ewo" // Ewondo + "\0\0\0" // Bafia + "\0\0\0" // MakhuwaMeetto + "\0\0\0" // Mundang + "\0\0\0" // Kwasio + "\0\0\0" // Nuer + "\0\0\0" // Sakha + "\0\0\0" // Sangu + "\0\0\0" // CongoSwahili + "\0\0\0" // Tasawaq + "vai" // Vai + "\0\0\0" // Walser + "\0\0\0" // Yangben + "ave" // Avestan + "\0\0\0" // Asturian + "\0\0\0" // Ngomba + "\0\0\0" // Kako + "\0\0\0" // Meta + "\0\0\0" // Ngiemboon + ; + +QT_END_NAMESPACE + +#endif /* MARCLANGUAGECODES_H_ */ diff --git a/src/plugins/geoservices/nokia/nokia.pro b/src/plugins/geoservices/nokia/nokia.pro new file mode 100644 index 0000000..86a0665 --- /dev/null +++ b/src/plugins/geoservices/nokia/nokia.pro @@ -0,0 +1,60 @@ +TARGET = qtgeoservices_nokia + +QT += location-private positioning-private network + +QT_FOR_CONFIG += location-private +qtConfig(location-labs-plugin): DEFINES += LOCATIONLABS + +HEADERS += \ + qgeocodereply_nokia.h \ + qgeocodejsonparser.h \ + qgeocodingmanagerengine_nokia.h \ + qgeotiledmappingmanagerengine_nokia.h \ + qgeotilefetcher_nokia.h \ + qgeomapreply_nokia.h \ + qgeoroutereply_nokia.h \ + qgeoroutexmlparser.h \ + qgeoroutingmanagerengine_nokia.h \ + qgeoserviceproviderplugin_nokia.h \ + marclanguagecodes.h \ + qgeonetworkaccessmanager.h \ + qgeointrinsicnetworkaccessmanager.h \ + qgeouriprovider.h \ + uri_constants.h \ + qgeoerror_messages.h \ + qgeomapversion.h \ + qgeotiledmap_nokia.h \ + qgeofiletilecachenokia.h + + +SOURCES += \ + qgeocodereply_nokia.cpp \ + qgeocodejsonparser.cpp \ + qgeocodingmanagerengine_nokia.cpp \ + qgeotiledmappingmanagerengine_nokia.cpp \ + qgeotilefetcher_nokia.cpp \ + qgeomapreply_nokia.cpp \ + qgeoroutereply_nokia.cpp \ + qgeoroutexmlparser.cpp \ + qgeoroutingmanagerengine_nokia.cpp \ + qgeoserviceproviderplugin_nokia.cpp \ + qgeointrinsicnetworkaccessmanager.cpp \ + qgeouriprovider.cpp \ + uri_constants.cpp \ + qgeoerror_messages.cpp \ + qgeomapversion.cpp \ + qgeotiledmap_nokia.cpp \ + qgeofiletilecachenokia.cpp + +include(placesv2/placesv2.pri) + +RESOURCES += nokia.qrc + +INCLUDEPATH += ../../../location/maps + +OTHER_FILES += \ + nokia_plugin.json + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = QGeoServiceProviderFactoryNokia +load(qt_plugin) diff --git a/src/plugins/geoservices/nokia/nokia.qrc b/src/plugins/geoservices/nokia/nokia.qrc new file mode 100644 index 0000000..41d973c --- /dev/null +++ b/src/plugins/geoservices/nokia/nokia.qrc @@ -0,0 +1,5 @@ + + + logo.png + + diff --git a/src/plugins/geoservices/nokia/nokia_plugin.json b/src/plugins/geoservices/nokia/nokia_plugin.json new file mode 100644 index 0000000..1fc2827 --- /dev/null +++ b/src/plugins/geoservices/nokia/nokia_plugin.json @@ -0,0 +1,19 @@ +{ + "Keys": ["here"], + "Provider": "here", + "Version": 101, + "Experimental": false, + "Features": [ + "OnlineRoutingFeature", + "RouteUpdatesFeature", + "AlternativeRoutesFeature", + "ExcludeAreasRoutingFeature", + "OnlineGeocodingFeature", + "OnlineMappingFeature", + "OnlinePlacesFeature", + "ReverseGeocodingFeature", + "PlaceRecommendationsFeature", + "SearchSuggestionsFeature", + "LocalizedPlacesFeature" + ] +} diff --git a/src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.cpp b/src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.cpp new file mode 100644 index 0000000..b09331b --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jsonparserhelpers.h" +#include "../qplacemanagerengine_nokiav2.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoCoordinate parseCoordinate(const QJsonArray &coordinateArray) +{ + return QGeoCoordinate(coordinateArray.at(0).toDouble(), coordinateArray.at(1).toDouble()); +} + +QPlaceSupplier parseSupplier(const QJsonObject &supplierObject, + const QPlaceManagerEngineNokiaV2 *engine) +{ + Q_ASSERT(engine); + + QPlaceSupplier supplier; + supplier.setName(supplierObject.value(QStringLiteral("title")).toString()); + supplier.setUrl(supplierObject.value(QStringLiteral("href")).toString()); + + supplier.setIcon(engine->icon(supplierObject.value(QStringLiteral("icon")).toString())); + + return supplier; +} + +QPlaceCategory parseCategory(const QJsonObject &categoryObject, + const QPlaceManagerEngineNokiaV2 *engine) +{ + Q_ASSERT(engine); + + QPlaceCategory category; + + category.setName(categoryObject.value(QStringLiteral("title")).toString()); + + const QUrl href(categoryObject.value(QStringLiteral("href")).toString()); + const QString hrefPath(href.path()); + category.setCategoryId(hrefPath.mid(hrefPath.lastIndexOf(QLatin1Char('/')) + 1)); + + + category.setIcon(engine->icon(categoryObject.value(QStringLiteral("icon")).toString())); + return category; +} + +QList parseCategories(const QJsonArray &categoryArray, + const QPlaceManagerEngineNokiaV2 *engine) +{ + Q_ASSERT(engine); + + QList categoryList; + for (int i = 0; i < categoryArray.count(); ++i) + categoryList.append(parseCategory(categoryArray.at(i).toObject(), + engine)); + + return categoryList; +} + +QList parseContactDetails(const QJsonArray &contacts) +{ + QList contactDetails; + + for (int i = 0; i < contacts.count(); ++i) { + QJsonObject contact = contacts.at(i).toObject(); + + QPlaceContactDetail detail; + detail.setLabel(contact.value(QStringLiteral("label")).toString()); + detail.setValue(contact.value(QStringLiteral("value")).toString()); + + contactDetails.append(detail); + } + + return contactDetails; +} + +QPlaceImage parseImage(const QJsonObject &imageObject, + const QPlaceManagerEngineNokiaV2 *engine) +{ + Q_ASSERT(engine); + + QPlaceImage image; + + image.setAttribution(imageObject.value(QStringLiteral("attribution")).toString()); + image.setUrl(imageObject.value(QStringLiteral("src")).toString()); + image.setSupplier(parseSupplier(imageObject.value(QStringLiteral("supplier")).toObject(), + engine)); + + return image; +} + +QPlaceReview parseReview(const QJsonObject &reviewObject, + const QPlaceManagerEngineNokiaV2 *engine) +{ + Q_ASSERT(engine); + + QPlaceReview review; + + review.setDateTime(QDateTime::fromString(reviewObject.value(QStringLiteral("date")).toString())); + + if (reviewObject.contains(QStringLiteral("title"))) + review.setTitle(reviewObject.value(QStringLiteral("title")).toString()); + + if (reviewObject.contains(QStringLiteral("rating"))) + review.setRating(reviewObject.value(QStringLiteral("rating")).toDouble()); + + review.setText(reviewObject.value(QStringLiteral("description")).toString()); + + QJsonObject userObject = reviewObject.value(QStringLiteral("user")).toObject(); + + QPlaceUser user; + user.setUserId(userObject.value(QStringLiteral("id")).toString()); + user.setName(userObject.value(QStringLiteral("title")).toString()); + review.setUser(user); + + review.setAttribution(reviewObject.value(QStringLiteral("attribution")).toString()); + + review.setLanguage(reviewObject.value(QStringLiteral("language")).toString()); + + review.setSupplier(parseSupplier(reviewObject.value(QStringLiteral("supplier")).toObject(), + engine)); + + //if (reviewObject.contains(QStringLiteral("via"))) { + // QJsonObject viaObject = reviewObject.value(QStringLiteral("via")).toObject(); + //} + + return review; +} + +QPlaceEditorial parseEditorial(const QJsonObject &editorialObject, + const QPlaceManagerEngineNokiaV2 *engine) +{ + Q_ASSERT(engine); + + QPlaceEditorial editorial; + + editorial.setAttribution(editorialObject.value(QStringLiteral("attribution")).toString()); + + //if (editorialObject.contains(QStringLiteral("via"))) { + // QJsonObject viaObject = editorialObject.value(QStringLiteral("via")).toObject(); + //} + + editorial.setSupplier(parseSupplier(editorialObject.value(QStringLiteral("supplier")).toObject(), + engine)); + editorial.setLanguage(editorialObject.value(QStringLiteral("language")).toString()); + editorial.setText(editorialObject.value(QStringLiteral("description")).toString()); + + return editorial; +} + +void parseCollection(QPlaceContent::Type type, const QJsonObject &object, + QPlaceContent::Collection *collection, int *totalCount, + QPlaceContentRequest *previous, QPlaceContentRequest *next, + const QPlaceManagerEngineNokiaV2 *engine) +{ + Q_ASSERT(engine); + + if (totalCount) + *totalCount = object.value(QStringLiteral("available")).toDouble(); + + int offset = 0; + if (object.contains(QStringLiteral("offset"))) + offset = object.value(QStringLiteral("offset")).toDouble(); + + if (previous && object.contains(QStringLiteral("previous"))) { + previous->setContentType(type); + previous->setContentContext(QUrl(object.value(QStringLiteral("previous")).toString())); + } + + if (next && object.contains(QStringLiteral("next"))) { + next->setContentType(type); + next->setContentContext(QUrl(object.value(QStringLiteral("next")).toString())); + } + + if (collection) { + QJsonArray items = object.value(QStringLiteral("items")).toArray(); + for (int i = 0; i < items.count(); ++i) { + QJsonObject itemObject = items.at(i).toObject(); + + switch (type) { + case QPlaceContent::ImageType: + collection->insert(offset + i, parseImage(itemObject, engine)); + break; + case QPlaceContent::ReviewType: + collection->insert(offset + i, parseReview(itemObject, engine)); + break; + case QPlaceContent::EditorialType: + collection->insert(offset + i, parseEditorial(itemObject, engine)); + break; + case QPlaceContent::NoType: + default: + break; + } + } + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.h b/src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.h new file mode 100644 index 0000000..4c7fffb --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/jsonparserhelpers.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef JSONPARSERHELPERS_H +#define JSONPARSERHELPERS_H + +#include + +QT_BEGIN_NAMESPACE + +class QJsonArray; +class QJsonObject; +class QGeoCoordinate; +class QPlaceContactDetail; +class QPlaceImage; +class QPlaceReview; +class QPlaceEditorial; +class QPlaceCategory; +class QPlaceContentRequest; +class QPlaceManagerEngineNokiaV2; + +QGeoCoordinate parseCoordinate(const QJsonArray &coordinateArray); +QPlaceSupplier parseSupplier(const QJsonObject &supplierObject, + const QPlaceManagerEngineNokiaV2 *engine); +QPlaceCategory parseCategory(const QJsonObject &categoryObject, + const QPlaceManagerEngineNokiaV2 *engine); +QList parseCategories(const QJsonArray &categoryArray, + const QPlaceManagerEngineNokiaV2 *engine); +QList parseContactDetails(const QJsonArray &contacts); + +QPlaceImage parseImage(const QJsonObject &imageObject, + const QPlaceManagerEngineNokiaV2 *engine); +QPlaceReview parseReview(const QJsonObject &reviewObject, + const QPlaceManagerEngineNokiaV2 *engine); +QPlaceEditorial parseEditorial(const QJsonObject &editorialObject, + const QPlaceManagerEngineNokiaV2 *engine); + +void parseCollection(QPlaceContent::Type type, const QJsonObject &object, + QPlaceContent::Collection *collection, int *totalCount, + QPlaceContentRequest *previous, QPlaceContentRequest *next, + const QPlaceManagerEngineNokiaV2 *engine); + +QT_END_NAMESPACE + +#endif // JSONPARSERHELPERS_H diff --git a/src/plugins/geoservices/nokia/placesv2/placesv2.pri b/src/plugins/geoservices/nokia/placesv2/placesv2.pri new file mode 100644 index 0000000..18c9fe3 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/placesv2.pri @@ -0,0 +1,21 @@ +QT *= location network + +HEADERS += \ + qplacemanagerengine_nokiav2.h \ + placesv2/qplacecategoriesreplyhere.h \ + placesv2/qplacecontentreplyimpl.h \ + placesv2/qplacedetailsreplyimpl.h \ + placesv2/qplaceidreplyimpl.h \ + placesv2/qplacesearchreplyhere.h \ + placesv2/qplacesearchsuggestionreplyimpl.h \ + placesv2/jsonparserhelpers.h + +SOURCES += \ + qplacemanagerengine_nokiav2.cpp \ + placesv2/qplacecategoriesreplyhere.cpp \ + placesv2/qplacecontentreplyimpl.cpp \ + placesv2/qplacedetailsreplyimpl.cpp \ + placesv2/qplaceidreplyimpl.cpp \ + placesv2/qplacesearchreplyhere.cpp \ + placesv2/qplacesearchsuggestionreplyimpl.cpp \ + placesv2/jsonparserhelpers.cpp diff --git a/src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.cpp b/src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.cpp new file mode 100644 index 0000000..5ae0d92 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecategoriesreplyhere.h" + +QT_BEGIN_NAMESPACE + +QPlaceCategoriesReplyHere::QPlaceCategoriesReplyHere(QObject *parent) +: QPlaceReply(parent) +{ +} + +QPlaceCategoriesReplyHere::~QPlaceCategoriesReplyHere() +{ +} + +void QPlaceCategoriesReplyHere::emitFinished() +{ + setFinished(true); + emit finished(); +} + +void QPlaceCategoriesReplyHere::setError(QPlaceReply::Error error_, const QString &errorString) +{ + QPlaceReply::setError(error_, errorString); + emit error(error_, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.h b/src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.h new file mode 100644 index 0000000..4258d39 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacecategoriesreplyhere.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECATEGORIESREPLYHERE_H +#define QPLACECATEGORIESREPLYHERE_H + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceCategoriesReplyHere : public QPlaceReply +{ + Q_OBJECT + +public: + explicit QPlaceCategoriesReplyHere(QObject *parent = 0); + ~QPlaceCategoriesReplyHere(); + + void emitFinished(); + +private slots: + void setError(QPlaceReply::Error error_, const QString &errorString); +}; + +QT_END_NAMESPACE + +#endif // QPLACECATEGORIESREPLYHERE_H diff --git a/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp b/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp new file mode 100644 index 0000000..f67fa5b --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "jsonparserhelpers.h" +#include "qplacecontentreplyimpl.h" +#include "../qplacemanagerengine_nokiav2.h" +#include "../qgeoerror_messages.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QPlaceContentReplyImpl::QPlaceContentReplyImpl(const QPlaceContentRequest &request, + QNetworkReply *reply, + QPlaceManagerEngineNokiaV2 *engine) + : QPlaceContentReply(engine), m_engine(engine) +{ + Q_ASSERT(engine); + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + setRequest(request); + + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(replyError(QNetworkReply::NetworkError))); + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QPlaceContentReplyImpl::~QPlaceContentReplyImpl() +{ +} + +void QPlaceContentReplyImpl::setError(QPlaceReply::Error error_, const QString &errorString) +{ + QPlaceContentReply::setError(error_, errorString); + emit error(error_, errorString); + setFinished(true); + emit finished(); +} + +void QPlaceContentReplyImpl::replyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + setError(ParseError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, PARSE_ERROR)); + return; + } + + QJsonObject object = document.object(); + + QPlaceContent::Collection collection; + int totalCount; + QPlaceContentRequest previous; + QPlaceContentRequest next; + + parseCollection(request().contentType(), object, &collection, &totalCount, + &previous, &next, m_engine); + + setTotalCount(totalCount); + setContent(collection); + setPreviousPageRequest(previous); + setNextPageRequest(next); + + setFinished(true); + emit finished(); +} + +void QPlaceContentReplyImpl::replyError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) + setError(QPlaceReply::CancelError, QStringLiteral("Request cancelled")); + else + setError(QPlaceReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.h b/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.h new file mode 100644 index 0000000..596b9a4 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacecontentreplyimpl.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECONTENTREPLYIMPL_H +#define QPLACECONTENTREPLYIMPL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManager; +class QPlaceManagerEngineNokiaV2; + +class QPlaceContentReplyImpl : public QPlaceContentReply +{ + Q_OBJECT + +public: + QPlaceContentReplyImpl(const QPlaceContentRequest &request, QNetworkReply *reply, + QPlaceManagerEngineNokiaV2 *engine); + ~QPlaceContentReplyImpl(); + +private slots: + void setError(QPlaceReply::Error error_, const QString &errorString); + void replyFinished(); + void replyError(QNetworkReply::NetworkError error); + +private: + QPlaceManagerEngineNokiaV2 *m_engine; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp b/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp new file mode 100644 index 0000000..1e7f2d2 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacedetailsreplyimpl.h" +#include "jsonparserhelpers.h" +#include "../qplacemanagerengine_nokiav2.h" +#include "../qgeoerror_messages.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +// These countries format the street address as: {house number} {street name} +// All other countries format it as: {street name} {house number} +static const char COUNTRY_TABLE_string[] = + "CAN\0" + "NZL\0" + "GBR\0" + "AUS\0" + "LKA\0" + "USA\0" + "SGP\0" + "FRA\0" + "BHS\0" + "CHN\0" + "IND\0" + "IRL\0" + "ARE\0" + "\0"; + +static const int COUNTRY_TABLE_indices[] = { + 0, 4, 8, 12, 16, 20, 24, 28, + 32, 36, 40, 44, 48, -1 +}; + +static bool countryTableContains(const QString &countryCode) +{ + for (int i = 0; COUNTRY_TABLE_indices[i] != -1; ++i) { + if (countryCode == QLatin1String(COUNTRY_TABLE_string + COUNTRY_TABLE_indices[i])) + return true; + } + + return false; +} + +QPlaceDetailsReplyImpl::QPlaceDetailsReplyImpl(QNetworkReply *reply, + QPlaceManagerEngineNokiaV2 *parent) +: QPlaceDetailsReply(parent), m_engine(parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(replyError(QNetworkReply::NetworkError))); + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QPlaceDetailsReplyImpl::~QPlaceDetailsReplyImpl() +{ +} + +void QPlaceDetailsReplyImpl::setError(QPlaceReply::Error error_, const QString &errorString) +{ + QPlaceReply::setError(error_, errorString); + emit error(error_, errorString); + setFinished(true); + emit finished(); +} + +void QPlaceDetailsReplyImpl::replyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + setError(ParseError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, PARSE_ERROR)); + return; + } + + QJsonObject object = document.object(); + + QPlace place; + + place.setPlaceId(object.value(QLatin1String("placeId")).toString()); + + //const QUrl view = object.value(QLatin1String("view")).toString(); + + place.setName(object.value(QLatin1String("name")).toString()); + + //if (object.contains(QLatin1String("distance"))) + // double distance = object.value(QLatin1String("distance")).toDouble(); + + //if (object.contains(QLatin1String("alternativeNames"))) { + // QJsonArray alternativeNames = object.value(QLatin1String("alternativeNames")).toArray(); + //} + + QGeoLocation location; + + QJsonObject locationObject = object.value(QLatin1String("location")).toObject(); + + //if (locationObject.contains(QLatin1String("locationId"))) + // const QString locationId = locationObject.value(QLatin1String("locationId")).toString(); + + QJsonArray position = locationObject.value(QLatin1String("position")).toArray(); + location.setCoordinate(QGeoCoordinate(position.at(0).toDouble(), position.at(1).toDouble())); + + QGeoAddress address; + + QJsonObject addressObject = locationObject.value(QLatin1String("address")).toObject(); + + address.setText(addressObject.value(QLatin1String("text")).toString()); + + address.setCountry(addressObject.value(QLatin1String("country")).toString()); + address.setCountryCode(addressObject.value(QLatin1String("countryCode")).toString()); + + QString house; + QString street; + + if (addressObject.contains(QLatin1String("house"))) + house = addressObject.value(QLatin1String("house")).toString(); + if (addressObject.contains(QLatin1String("street"))) + street = addressObject.value(QLatin1String("street")).toString(); + + if (countryTableContains(address.countryCode())) { + if (!house.isEmpty() && !street.startsWith(house)) + street = house + QLatin1Char(' ') + street; + } else { + if (!house.isEmpty() && !street.endsWith(house)) + street += QLatin1Char(' ') + house; + } + + address.setStreet(street); + + if (addressObject.contains(QLatin1String("city"))) + address.setCity(addressObject.value(QLatin1String("city")).toString()); + if (addressObject.contains(QLatin1String("district"))) + address.setDistrict(addressObject.value(QLatin1String("district")).toString()); + if (addressObject.contains(QLatin1String("state"))) + address.setState(addressObject.value(QLatin1String("state")).toString()); + if (addressObject.contains(QLatin1String("county"))) + address.setCounty(addressObject.value(QLatin1String("county")).toString()); + if (addressObject.contains(QLatin1String("postalCode"))) + address.setPostalCode(addressObject.value(QLatin1String("postalCode")).toString()); + + location.setAddress(address); + + if (locationObject.contains(QLatin1String("bbox"))) { + QJsonArray bbox = locationObject.value(QLatin1String("bbox")).toArray(); + QGeoRectangle box(QGeoCoordinate(bbox.at(3).toDouble(), bbox.at(0).toDouble()), + QGeoCoordinate(bbox.at(1).toDouble(), bbox.at(2).toDouble())); + location.setBoundingBox(box); + } + + place.setLocation(location); + + place.setCategories(parseCategories(object.value(QLatin1String("categories")).toArray(), + m_engine)); + + place.setIcon(m_engine->icon(object.value(QLatin1String("icon")).toString(), + place.categories())); + + if (object.contains(QLatin1String("contacts"))) { + QJsonObject contactsObject = object.value(QLatin1String("contacts")).toObject(); + + if (contactsObject.contains(QLatin1String("phone"))) { + place.setContactDetails(QPlaceContactDetail::Phone, + parseContactDetails(contactsObject.value(QLatin1String("phone")).toArray())); + } + if (contactsObject.contains(QLatin1String("fax"))) { + place.setContactDetails(QPlaceContactDetail::Fax, + parseContactDetails(contactsObject.value(QLatin1String("fax")).toArray())); + } + if (contactsObject.contains(QLatin1String("website"))) { + place.setContactDetails(QPlaceContactDetail::Website, + parseContactDetails(contactsObject.value(QLatin1String("website")).toArray())); + } + if (contactsObject.contains(QLatin1String("email"))) { + place.setContactDetails(QPlaceContactDetail::Email, + parseContactDetails(contactsObject.value(QLatin1String("email")).toArray())); + } + } + + //if (object.contains(QLatin1String("verifiedByOwner"))) + // bool verifiedByOwner = object.value(QLatin1String("verifiedByOwner")).toBool(); + + if (object.contains(QLatin1String("attribution"))) + place.setAttribution(object.value(QLatin1String("attribution")).toString()); + + if (object.contains(QLatin1String("supplier"))) { + place.setSupplier(parseSupplier(object.value(QLatin1String("supplier")).toObject(), + m_engine)); + } + + if (object.contains(QLatin1String("ratings"))) { + QJsonObject ratingsObject = object.value(QLatin1String("ratings")).toObject(); + + QPlaceRatings ratings; + ratings.setAverage(ratingsObject.value(QLatin1String("average")).toDouble()); + ratings.setCount(ratingsObject.value(QLatin1String("count")).toDouble()); + ratings.setMaximum(5.0); + + place.setRatings(ratings); + } + + if (object.contains(QLatin1String("extended"))) { + QJsonObject extendedObject = object.value(QLatin1String("extended")).toObject(); + + for (auto it = extendedObject.constBegin(), end = extendedObject.constEnd(); it != end; ++it) { + QJsonObject attributeObject = it.value().toObject(); + + QPlaceAttribute attribute; + + attribute.setLabel(attributeObject.value(QLatin1String("label")).toString()); + attribute.setText(attributeObject.value(QLatin1String("text")).toString()); + + QString key = it.key(); + if (key == QLatin1String("payment")) + place.setExtendedAttribute(QPlaceAttribute::Payment, attribute); + else if (key == QLatin1String("openingHours")) + place.setExtendedAttribute(QPlaceAttribute::OpeningHours, attribute); + else + place.setExtendedAttribute(key, attribute); + } + } + + if (object.contains(QLatin1String("media"))) { + QJsonObject mediaObject = object.value(QLatin1String("media")).toObject(); + + if (mediaObject.contains(QLatin1String("images"))) { + QPlaceContent::Collection collection; + int totalCount = 0; + + parseCollection(QPlaceContent::ImageType, + mediaObject.value(QLatin1String("images")).toObject(), + &collection, &totalCount, 0, 0, m_engine); + + place.setTotalContentCount(QPlaceContent::ImageType, totalCount); + place.setContent(QPlaceContent::ImageType, collection); + } + if (mediaObject.contains(QLatin1String("editorials"))) { + QPlaceContent::Collection collection; + int totalCount = 0; + + parseCollection(QPlaceContent::EditorialType, + mediaObject.value(QLatin1String("editorials")).toObject(), + &collection, &totalCount, 0, 0, m_engine); + + place.setTotalContentCount(QPlaceContent::EditorialType, totalCount); + place.setContent(QPlaceContent::EditorialType, collection); + } + if (mediaObject.contains(QLatin1String("reviews"))) { + QPlaceContent::Collection collection; + int totalCount = 0; + + parseCollection(QPlaceContent::ReviewType, + mediaObject.value(QLatin1String("reviews")).toObject(), + &collection, &totalCount, 0, 0, m_engine); + + place.setTotalContentCount(QPlaceContent::ReviewType, totalCount); + place.setContent(QPlaceContent::ReviewType, collection); + } + } + + //if (object.contains(QLatin1String("related"))) { + // QJsonObject relatedObject = object.value(QLatin1String("related")).toObject(); + //} + + QPlaceAttribute provider; + provider.setText(QLatin1String("here")); + place.setExtendedAttribute(QPlaceAttribute::Provider, provider); + + place.setVisibility(QLocation::PublicVisibility); + place.setDetailsFetched(true); + setPlace(place); + + setFinished(true); + emit finished(); +} + +void QPlaceDetailsReplyImpl::replyError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) { + setError(QPlaceReply::CancelError, QStringLiteral("Request cancelled")); + } else if (error == QNetworkReply::ContentNotFoundError) { + setError(QPlaceReply::PlaceDoesNotExistError, + QString::fromLatin1("The id, %1, does not reference an existing place") + .arg(m_placeId)); + } else { + setError(QPlaceReply::CommunicationError, reply->errorString()); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.h b/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.h new file mode 100644 index 0000000..dc537c8 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacedetailsreplyimpl.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEDETAILSREPLYIMPL_H +#define QPLACEDETAILSREPLYIMPL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManager; +class QPlaceManagerEngineNokiaV2; + +class QPlaceDetailsReplyImpl : public QPlaceDetailsReply +{ + Q_OBJECT + +public: + QPlaceDetailsReplyImpl(QNetworkReply *reply, QPlaceManagerEngineNokiaV2 *parent); + ~QPlaceDetailsReplyImpl(); + + void setPlaceId(const QString &placeId) { m_placeId = placeId; } + +private slots: + void setError(QPlaceReply::Error error_, const QString &errorString); + void replyFinished(); + void replyError(QNetworkReply::NetworkError error); + +private: + QPlaceManagerEngineNokiaV2 *m_engine; + QString m_placeId; +}; + +QT_END_NAMESPACE + +#endif // QPLACEDETAILSREPLYIMPL_H diff --git a/src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.cpp b/src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.cpp new file mode 100644 index 0000000..2378d0b --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.cpp @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplaceidreplyimpl.h" + +QT_BEGIN_NAMESPACE + +QPlaceIdReplyImpl::QPlaceIdReplyImpl(QPlaceIdReply::OperationType type, QObject *parent) +: QPlaceIdReply(type, parent) +{ +} + +QPlaceIdReplyImpl::~QPlaceIdReplyImpl() +{ +} + +void QPlaceIdReplyImpl::setId(const QString &id) +{ + QPlaceIdReply::setId(id); +} + +void QPlaceIdReplyImpl::setError(QPlaceReply::Error error_, const QString &errorString) +{ + if (error_ != QPlaceReply::NoError) { + QPlaceIdReply::setError(error_, errorString); + emit error(error_, errorString); + } + + setFinished(true); + emit finished(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.h b/src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.h new file mode 100644 index 0000000..091f3d6 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplaceidreplyimpl.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef UNSUPPORTED_REPLIES_H +#define UNSUPPORTED_REPLIES_H + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceIdReplyImpl : public QPlaceIdReply +{ + Q_OBJECT + +public: + QPlaceIdReplyImpl(QPlaceIdReply::OperationType type, QObject *parent = 0); + ~QPlaceIdReplyImpl(); + + void setId(const QString &id); + +private slots: + void setError(QPlaceReply::Error error_, const QString &errorString); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp new file mode 100644 index 0000000..9808b53 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.cpp @@ -0,0 +1,228 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchreplyhere.h" +#include "jsonparserhelpers.h" +#include "../qplacemanagerengine_nokiav2.h" +#include "../qgeoerror_messages.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QPlaceSearchReplyHere::QPlaceSearchReplyHere(const QPlaceSearchRequest &request, + QNetworkReply *reply, + QPlaceManagerEngineNokiaV2 *parent) + : QPlaceSearchReply(parent), m_engine(parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + setRequest(request); + + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(replyError(QNetworkReply::NetworkError))); + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QPlaceSearchReplyHere::~QPlaceSearchReplyHere() +{ +} + +void QPlaceSearchReplyHere::setError(QPlaceReply::Error error_, const QString &errorString) +{ + QPlaceReply::setError(error_, errorString); + emit error(error_, errorString); + setFinished(true); + emit finished(); +} + +void QPlaceSearchReplyHere::replyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + setError(ParseError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, PARSE_ERROR)); + return; + } + + QJsonObject resultsObject = document.object(); + + if (resultsObject.contains(QStringLiteral("results"))) + resultsObject = resultsObject.value(QStringLiteral("results")).toObject(); + + QJsonArray items = resultsObject.value(QStringLiteral("items")).toArray(); + + QList results; + for (int i = 0; i < items.count(); ++i) { + QJsonObject item = items.at(i).toObject(); + + const QString type = item.value(QStringLiteral("type")).toString(); + if (type == QStringLiteral("urn:nlp-types:place")) + results.append(parsePlaceResult(item)); + else if (type == QStringLiteral("urn:nlp-types:search")) + results.append(parseSearchResult(item)); + } + + if (resultsObject.contains(QStringLiteral("next"))) { + QPlaceSearchRequest request; + request.setSearchContext(QUrl(resultsObject.value(QStringLiteral("next")).toString())); + setNextPageRequest(request); + } + + if (resultsObject.contains(QStringLiteral("previous"))) { + QPlaceSearchRequest request; + request.setSearchContext(QUrl(resultsObject.value(QStringLiteral("previous")).toString())); + setPreviousPageRequest(request); + } + + setResults(results); + + setFinished(true); + emit finished(); +} + +QPlaceResult QPlaceSearchReplyHere::parsePlaceResult(const QJsonObject &item) const +{ + QPlaceResult result; + + if (item.contains(QStringLiteral("distance"))) + result.setDistance(item.value(QStringLiteral("distance")).toDouble()); + + QPlace place; + + QGeoLocation location; + + location.setCoordinate(parseCoordinate(item.value(QStringLiteral("position")).toArray())); + + const QString vicinity = item.value(QStringLiteral("vicinity")).toString(); + QGeoAddress address; + address.setText(vicinity); + location.setAddress(address); + + if (item.contains(QStringLiteral("bbox"))) { + QJsonArray bbox = item.value(QStringLiteral("bbox")).toArray(); + QGeoRectangle box(QGeoCoordinate(bbox.at(3).toDouble(), bbox.at(0).toDouble()), + QGeoCoordinate(bbox.at(1).toDouble(), bbox.at(2).toDouble())); + location.setBoundingBox(box); + } + + place.setLocation(location); + + QPlaceRatings ratings; + ratings.setAverage(item.value(QStringLiteral("averageRating")).toDouble()); + ratings.setMaximum(5.0); + place.setRatings(ratings); + + const QString title = item.value(QStringLiteral("title")).toString(); + place.setName(title); + result.setTitle(title); + + QPlaceIcon icon = m_engine->icon(item.value(QStringLiteral("icon")).toString()); + place.setIcon(icon); + result.setIcon(icon); + + place.setCategory(parseCategory(item.value(QStringLiteral("category")).toObject(), + m_engine)); + + //QJsonArray having = item.value(QStringLiteral("having")).toArray(); + + result.setSponsored(item.value(QStringLiteral("sponsored")).toBool()); + + QUrl href = item.value(QStringLiteral("href")).toString(); + //QUrl type = item.value(QStringLiteral("type")).toString(); + + place.setPlaceId(href.path().mid(18, 41)); + + QPlaceAttribute provider; + provider.setText(QStringLiteral("here")); + place.setExtendedAttribute(QPlaceAttribute::Provider, provider); + place.setVisibility(QLocation::PublicVisibility); + + result.setPlace(place); + + return result; +} + +QPlaceProposedSearchResult QPlaceSearchReplyHere::parseSearchResult(const QJsonObject &item) const +{ + QPlaceProposedSearchResult result; + + result.setTitle(item.value(QStringLiteral("title")).toString()); + + QPlaceIcon icon = m_engine->icon(item.value(QStringLiteral("icon")).toString()); + result.setIcon(icon); + + QPlaceSearchRequest request; + request.setSearchContext(QUrl(item.value("href").toString())); + + result.setSearchRequest(request); + + return result; +} + +void QPlaceSearchReplyHere::replyError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) { + setError(QPlaceReply::CancelError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, CANCEL_ERROR)); + } else if (error == QNetworkReply::ContentNotFoundError) { + setError(QPlaceReply::PlaceDoesNotExistError, + QString::fromLatin1("The id, %1, does not reference an existing place") + .arg(request().recommendationId())); + } else { + setError(QPlaceReply::CommunicationError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, NETWORK_ERROR)); + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.h b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.h new file mode 100644 index 0000000..b3d97a3 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacesearchreplyhere.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHREPLYHERE_H +#define QPLACESEARCHREPLYHERE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceManagerEngineNokiaV2; +class QPlaceResult; +class QPlaceProposedSearchResult; + +class QPlaceSearchReplyHere : public QPlaceSearchReply +{ + Q_OBJECT + +public: + explicit QPlaceSearchReplyHere(const QPlaceSearchRequest &request, + QNetworkReply *reply, + QPlaceManagerEngineNokiaV2 *parent); + ~QPlaceSearchReplyHere(); + +private slots: + void setError(QPlaceReply::Error error_, const QString &errorString); + void replyFinished(); + void replyError(QNetworkReply::NetworkError error); + +private: + QPlaceResult parsePlaceResult(const QJsonObject &item) const; + QPlaceProposedSearchResult parseSearchResult(const QJsonObject &item) const; + + QPlaceManagerEngineNokiaV2 *m_engine; +}; + +QT_END_NAMESPACE + +#endif // QPLACESEARCHREPLYHERE_H diff --git a/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp b/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp new file mode 100644 index 0000000..9882545 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchsuggestionreplyimpl.h" +#include "../qgeoerror_messages.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QPlaceSearchSuggestionReplyImpl::QPlaceSearchSuggestionReplyImpl(QNetworkReply *reply, + QObject *parent) +: QPlaceSearchSuggestionReply(parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(replyError(QNetworkReply::NetworkError))); + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QPlaceSearchSuggestionReplyImpl::~QPlaceSearchSuggestionReplyImpl() +{ +} + +void QPlaceSearchSuggestionReplyImpl::setError(QPlaceReply::Error error_, + const QString &errorString) +{ + QPlaceReply::setError(error_, errorString); + emit error(error_, errorString); + setFinished(true); + emit finished(); +} + +void QPlaceSearchSuggestionReplyImpl::replyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + setError(ParseError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, PARSE_ERROR)); + emit error(error(), errorString()); + return; + } + + QJsonObject object = document.object(); + + QJsonArray suggestions = object.value(QStringLiteral("suggestions")).toArray(); + + QStringList s; + for (int i = 0; i < suggestions.count(); ++i) { + QJsonValue v = suggestions.at(i); + if (v.isString()) + s.append(v.toString()); + } + + setSuggestions(s); + + setFinished(true); + emit finished(); +} + +void QPlaceSearchSuggestionReplyImpl::replyError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) + setError(QPlaceReply::CancelError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, CANCEL_ERROR)); + else + setError(QPlaceReply::CommunicationError, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, NETWORK_ERROR)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.h b/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.h new file mode 100644 index 0000000..97ae3e1 --- /dev/null +++ b/src/plugins/geoservices/nokia/placesv2/qplacesearchsuggestionreplyimpl.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHSUGGESTIONREPLYIMPL_H +#define QPLACESEARCHSUGGESTIONREPLYIMPL_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceSearchSuggestionReplyImpl : public QPlaceSearchSuggestionReply +{ + Q_OBJECT + +public: + explicit QPlaceSearchSuggestionReplyImpl(QNetworkReply *reply, QObject *parent = 0); + ~QPlaceSearchSuggestionReplyImpl(); + +private slots: + void setError(QPlaceReply::Error error_, const QString &errorString); + void replyFinished(); + void replyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QPLACESEARCHSUGGESTIONREPLYIMPL_H diff --git a/src/plugins/geoservices/nokia/qgeocodejsonparser.cpp b/src/plugins/geoservices/nokia/qgeocodejsonparser.cpp new file mode 100644 index 0000000..128f7fd --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeocodejsonparser.cpp @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodejsonparser.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace { + +/* + Checks that the given Location object contains the information + we need and is not malformed in any way. We expect a Location + object of the following form: + + "Location": { + "Address": { + "AdditionalData": [ + { + "key": "CountryName", + "value": "Australia" + }, + { + "key": "StateName", + "value": "New South Wales" + } + ], + "City": "Sydney", + "Country": "AUS", + "District": "Casula", + "Label": "Casula, Sydney, NSW, Australia", + "PostalCode": "2170", + "State": "NSW" + }, + "DisplayPosition": { + "Latitude": -33.949509999999997, + "Longitude": 150.90386000000001 + }, + "LocationId": "NT_5UQ89lKoiI4DIYbOrIR0-D", + "LocationType": "area", + "MapReference": { + "CityId": "1469266800", + "CountryId": "1469256839", + "DistrictId": "1469267758", + "MapId": "NXAM16130", + "MapReleaseDate": "2016-10-05", + "MapVersion": "Q1/2016", + "ReferenceId": "868383156", + "SideOfStreet": "neither", + "StateId": "1469256831" + }, + "MapView": { + "BottomRight": { + "Latitude": -33.966839999999998, + "Longitude": 150.91875999999999 + }, + "TopLeft": { + "Latitude": -33.937440000000002, + "Longitude": 150.87457000000001 + } + } + } + +*/ +bool checkLocation(const QJsonObject &loc, QString *errorString) +{ + QJsonObject::const_iterator ait = loc.constFind(QLatin1String("Address")); + if (ait == loc.constEnd()) { + *errorString = QLatin1String("Expected Address element within Location object"); + return false; + } else if (!ait.value().isObject()) { + *errorString = QLatin1String("Expected Address object within Location object"); + return false; + } + + QJsonObject::const_iterator dpit = loc.constFind(QLatin1String("DisplayPosition")); + if (dpit == loc.constEnd()) { + *errorString = QLatin1String("Expected DisplayPosition element within Location object"); + return false; + } else if (!dpit.value().isObject()) { + *errorString = QLatin1String("Expected DisplayPosition object within Location object"); + return false; + } + QJsonObject displayPosition = dpit.value().toObject(); + QJsonObject::const_iterator latit = displayPosition.constFind(QLatin1String("Latitude")); + if (latit == displayPosition.constEnd()) { + *errorString = QLatin1String("Expected Latitude element within Location.DisplayPosition object"); + return false; + } else if (!latit.value().isDouble()) { + *errorString = QLatin1String("Expected Latitude double within Location.DisplayPosition object"); + return false; + } + QJsonObject::const_iterator lonit = displayPosition.constFind(QLatin1String("Longitude")); + if (lonit == displayPosition.constEnd()) { + *errorString = QLatin1String("Expected Longitude element within Location.DisplayPosition object"); + return false; + } else if (!lonit.value().isDouble()) { + *errorString = QLatin1String("Expected Longitude double within Location.DisplayPosition object"); + return false; + } + + QJsonObject::const_iterator mvit = loc.constFind(QLatin1String("MapView")); + if (mvit == loc.constEnd()) { + *errorString = QLatin1String("Expected MapView element within Location object"); + return false; + } else if (!mvit.value().isObject()) { + *errorString = QLatin1String("Expected MapView object within Location object"); + return false; + } + QJsonObject mapView = mvit.value().toObject(); + QJsonObject::const_iterator brit = mapView.constFind(QLatin1String("BottomRight")); + if (brit == mapView.constEnd()) { + *errorString = QLatin1String("Expected BottomRight element within Location.MapView object"); + return false; + } else if (!brit.value().isObject()) { + *errorString = QLatin1String("Expected BottomRight object within Location.MapView object"); + return false; + } + QJsonObject bottomRight = brit.value().toObject(); + QJsonObject::const_iterator brlatit = bottomRight.constFind(QLatin1String("Latitude")); + if (brlatit == bottomRight.constEnd()) { + *errorString = QLatin1String("Expected Latitude element within Location.MapView.BottomRight object"); + return false; + } else if (!brlatit.value().isDouble()) { + *errorString = QLatin1String("Expected Latitude double within Location.MapView.BottomRight object"); + return false; + } + QJsonObject::const_iterator brlonit = bottomRight.constFind(QLatin1String("Longitude")); + if (brlonit == bottomRight.constEnd()) { + *errorString = QLatin1String("Expected Longitude element within Location.MapView.BottomRight object"); + return false; + } else if (!brlonit.value().isDouble()) { + *errorString = QLatin1String("Expected Longitude double within Location.MapView.BottomRight object"); + return false; + } + QJsonObject::const_iterator tlit = mapView.constFind(QLatin1String("TopLeft")); + if (tlit == mapView.constEnd()) { + *errorString = QLatin1String("Expected TopLeft element within Location.MapView object"); + return false; + } else if (!tlit.value().isObject()) { + *errorString = QLatin1String("Expected TopLeft object within Location.MapView object"); + return false; + } + QJsonObject topLeft = tlit.value().toObject(); + QJsonObject::const_iterator tllatit = topLeft.constFind(QLatin1String("Latitude")); + if (tllatit == topLeft.constEnd()) { + *errorString = QLatin1String("Expected Latitude element within Location.MapView.TopLeft object"); + return false; + } else if (!tllatit.value().isDouble()) { + *errorString = QLatin1String("Expected Latitude double within Location.MapView.TopLeft object"); + return false; + } + QJsonObject::const_iterator tllonit = topLeft.constFind(QLatin1String("Longitude")); + if (tllonit == bottomRight.constEnd()) { + *errorString = QLatin1String("Expected Longitude element within Location.MapView.TopLeft object"); + return false; + } else if (!tllonit.value().isDouble()) { + *errorString = QLatin1String("Expected Longitude double within Location.MapView.TopLeft object"); + return false; + } + + return true; +} + +/* + Checks that the given document contains the required information + and is not malformed in any way. We expect a document like the + following: + + { + "Response": { + "MetaInfo": { + "Timestamp": "2016-10-18T08:42:04.369+0000" + }, + "View": [ + { + "ViewId": 0, + "_type": "SearchResultsViewType", + "Result": [ + { + "Direction": 72.099999999999994, + "Distance": -1885.2, + "Location": { + // OMITTED FOR BREVITY + }, + "MatchLevel": "district", + "MatchQuality": { + "City": 1, + "Country": 1, + "District": 1, + "PostalCode": 1, + "State": 1 + }, + "Relevance": 1 + } + ] + } + ] + } + } +*/ +bool checkDocument(const QJsonDocument &doc, QString *errorString) +{ + if (!doc.isObject()) { + *errorString = QLatin1String("Expected JSON document containing object"); + return false; + } + + QJsonObject rootObject = doc.object(); + QJsonObject::const_iterator it = rootObject.constFind(QLatin1String("Response")); + if (it == rootObject.constEnd()) { + *errorString = QLatin1String("Expected Response element within root object"); + return false; + } else if (!it.value().isObject()) { + *errorString = QLatin1String("Expected Response object within root object"); + return false; + } + + QJsonObject response = it.value().toObject(); + QJsonObject::const_iterator rit = response.constFind(QLatin1String("View")); + if (rit == response.constEnd()) { + *errorString = QLatin1String("Expected View element within Response object"); + return false; + } else if (!rit.value().isArray()) { + *errorString = QLatin1String("Expected View array within Response object"); + return false; + } + + QJsonArray view = rit.value().toArray(); + Q_FOREACH (const QJsonValue &viewElement, view) { + if (!viewElement.isObject()) { + *errorString = QLatin1String("Expected View array element to be object"); + return false; + } + + QJsonObject viewObject = viewElement.toObject(); + QJsonObject::const_iterator voit = viewObject.constFind(QLatin1String("Result")); + if (voit == viewObject.constEnd()) { + *errorString = QLatin1String("Expected Result element within View array object element"); + return false; + } else if (!voit.value().isArray()) { + *errorString = QLatin1String("Expected Result array within View array object element"); + return false; + } + + QJsonArray result = voit.value().toArray(); + Q_FOREACH (const QJsonValue &resultElement, result) { + if (!resultElement.isObject()) { + *errorString = QLatin1String("Expected Result array element to be object"); + return false; + } + + QJsonObject resultObject = resultElement.toObject(); + QJsonObject::const_iterator roit = resultObject.constFind("Location"); + if (roit == resultObject.constEnd()) { + *errorString = QLatin1String("Expected Location element in Result array element object"); + return false; + } else if (!roit.value().isObject()) { + *errorString = QLatin1String("Expected Location object in Result array element object"); + return false; + } + + QJsonObject location = roit.value().toObject(); + if (!checkLocation(location, errorString)) { + return false; + } + } + } + + return true; +} + +bool parseLocation(const QJsonObject &obj, const QGeoShape &bounds, QGeoLocation *loc) +{ + QJsonObject displayPosition = obj.value("DisplayPosition").toObject(); + QGeoCoordinate coordinate = QGeoCoordinate(displayPosition.value("Latitude").toDouble(), displayPosition.value("Longitude").toDouble()); + if (bounds.isValid() && !bounds.contains(coordinate)) { + // manual bounds check failed, location can be omitted from results. + return false; + } + + QGeoAddress address; + QJsonObject addr = obj.value("Address").toObject(); + address.setCountryCode(addr.value("Country").toString()); + address.setState(addr.value("State").toString()); + address.setCounty(addr.value("County").toString()); + address.setCity(addr.value("City").toString()); + address.setDistrict(addr.value("District").toString()); + QString houseNumber = addr.value("HouseNumber").toString(); + QString street = addr.value("Street").toString(); + address.setStreet(houseNumber.isEmpty() ? street : QString("%1 %2").arg(houseNumber, street)); + address.setPostalCode(addr.value("PostalCode").toString()); + QString label = addr.value("Label").toString().trimmed(); + if (!label.isEmpty()) { + address.setText(label); + } + QJsonArray additionalData = addr.value("AdditionalData").toArray(); + Q_FOREACH (const QJsonValue &adv, additionalData) { + if (adv.isObject()) { + const QJsonObject &ado(adv.toObject()); + if (ado.value("key").toString() == QLatin1String("CountryName")) { + address.setCountry(ado.value("value").toString()); + } + } + } + + QGeoRectangle boundingBox; + QJsonObject mapView = obj.value("MapView").toObject(); + QJsonObject bottomRight = mapView.value("BottomRight").toObject(); + QJsonObject topLeft = mapView.value("TopLeft").toObject(); + boundingBox.setBottomRight(QGeoCoordinate(bottomRight.value("Latitude").toDouble(), bottomRight.value("Longitude").toDouble())); + boundingBox.setTopLeft(QGeoCoordinate(topLeft.value("Latitude").toDouble(), topLeft.value("Longitude").toDouble())); + + loc->setAddress(address); + loc->setCoordinate(coordinate); + loc->setBoundingBox(boundingBox); + + return true; +} + +void parseDocument(const QJsonDocument &doc, const QGeoShape &bounds, QList *locs) +{ + QJsonArray view = doc.object().value("Response").toObject().value("View").toArray(); + Q_FOREACH (const QJsonValue &viewElement, view) { + QJsonArray result = viewElement.toObject().value("Result").toArray(); + Q_FOREACH (const QJsonValue &resultElement, result) { + QGeoLocation location; + if (parseLocation(resultElement.toObject().value("Location").toObject(), bounds, &location)) { + locs->append(location); + } + } + } +} + +} // namespace + +void QGeoCodeJsonParser::setBounds(const QGeoShape &bounds) +{ + m_bounds = bounds; +} + +void QGeoCodeJsonParser::parse(const QByteArray &data) +{ + m_data = data; + QThreadPool::globalInstance()->start(this); +} + +void QGeoCodeJsonParser::run() +{ + // parse the document. + QJsonParseError perror; + m_document = QJsonDocument::fromJson(m_data, &perror); + if (perror.error != QJsonParseError::NoError) { + m_errorString = perror.errorString(); + } else { + // ensure that the response is valid and contains the information we need. + if (checkDocument(m_document, &m_errorString)) { + // extract the location results from the response. + parseDocument(m_document, m_bounds, &m_results); + emit results(m_results); + return; + } + } + + emit error(m_errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeocodejsonparser.h b/src/plugins/geoservices/nokia/qgeocodejsonparser.h new file mode 100644 index 0000000..0325177 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeocodejsonparser.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODEJSONPARSER_H +#define QGEOCODEJSONPARSER_H + +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCodeJsonParser : public QObject, public QRunnable +{ + Q_OBJECT + +public: + void setBounds(const QGeoShape &bounds); + void parse(const QByteArray &data); + void run(); + +signals: + void results(const QList &locations); + void error(const QString &errorString); + +private: + QJsonDocument m_document; + QByteArray m_data; + QGeoShape m_bounds; + QList m_results; + QString m_errorString; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp b/src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp new file mode 100644 index 0000000..0fb6eb2 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeocodereply_nokia.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodereply_nokia.h" +#include "qgeocodejsonparser.h" +#include "qgeoerror_messages.h" + +#include +#include + +Q_DECLARE_METATYPE(QList) + +QT_BEGIN_NAMESPACE + +// manualBoundsRequired will be true if the parser has to manually +// check if a given result lies within the viewport bounds, +// and false if the bounds information was able to be supplied +// to the server in the request (so it should not return any +// out-of-bounds results). +QGeoCodeReplyNokia::QGeoCodeReplyNokia(QNetworkReply *reply, int limit, int offset, + const QGeoShape &viewport, bool manualBoundsRequired, + QObject *parent) +: QGeoCodeReply(parent), m_parsing(false), m_manualBoundsRequired(manualBoundsRequired) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + qRegisterMetaType >(); + + connect(reply, SIGNAL(finished()), this, SLOT(networkFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkError(QNetworkReply::NetworkError))); + connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QGeoCodeReply::aborted, [this](){ m_parsing = false; }); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + + + setLimit(limit); + setOffset(offset); + setViewport(viewport); +} + +QGeoCodeReplyNokia::~QGeoCodeReplyNokia() +{ +} + +void QGeoCodeReplyNokia::networkFinished() +{ + QNetworkReply *reply = qobject_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QGeoCodeJsonParser *parser = new QGeoCodeJsonParser; // QRunnable, autoDelete = true. + if (m_manualBoundsRequired) + parser->setBounds(viewport()); + connect(parser, SIGNAL(results(QList)), + this, SLOT(appendResults(QList))); + connect(parser, SIGNAL(error(QString)), this, SLOT(parseError(QString))); + + m_parsing = true; + parser->parse(reply->readAll()); +} + +void QGeoCodeReplyNokia::networkError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + + QNetworkReply *reply = qobject_cast(sender()); + reply->deleteLater(); + setError(QGeoCodeReply::CommunicationError, reply->errorString()); +} + +void QGeoCodeReplyNokia::appendResults(const QList &locations) +{ + if (!m_parsing) + return; + + m_parsing = false; + setLocations(locations); + setFinished(true); +} + +void QGeoCodeReplyNokia::parseError(const QString &errorString) +{ + Q_UNUSED(errorString) + + setError(QGeoCodeReply::ParseError, + QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, RESPONSE_NOT_RECOGNIZABLE)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeocodereply_nokia.h b/src/plugins/geoservices/nokia/qgeocodereply_nokia.h new file mode 100644 index 0000000..90443eb --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeocodereply_nokia.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODEREPLY_NOKIA_H +#define QGEOCODEREPLY_NOKIA_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCodeReplyNokia : public QGeoCodeReply +{ + Q_OBJECT +public: + QGeoCodeReplyNokia(QNetworkReply *reply, int limit, int offset, const QGeoShape &viewport, bool manualBoundsRequired, QObject *parent = 0); + ~QGeoCodeReplyNokia(); + +private Q_SLOTS: + void networkFinished(); + void networkError(QNetworkReply::NetworkError error); + void appendResults(const QList &locations); + void parseError(const QString &errorString); + +private: + bool m_parsing; + bool m_manualBoundsRequired; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp b/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp new file mode 100644 index 0000000..68b2429 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodingmanagerengine_nokia.h" +#include "qgeocodereply_nokia.h" +#include "marclanguagecodes.h" +#include "qgeonetworkaccessmanager.h" +#include "qgeouriprovider.h" +#include "uri_constants.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoCodingManagerEngineNokia::QGeoCodingManagerEngineNokia( + QGeoNetworkAccessManager *networkManager, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) + : QGeoCodingManagerEngine(parameters) + , m_networkManager(networkManager) + , m_uriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.geocoding.host"), GEOCODING_HOST)) + , m_reverseGeocodingUriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.reversegeocoding.host"), REVERSE_GEOCODING_HOST)) +{ + Q_ASSERT(networkManager); + m_networkManager->setParent(this); + + if (parameters.contains(QStringLiteral("here.token"))) + m_token = parameters.value(QStringLiteral("here.token")).toString(); + + if (parameters.contains(QStringLiteral("here.app_id"))) + m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString(); + + if (error) + *error = QGeoServiceProvider::NoError; + + if (errorString) + *errorString = ""; +} + +QGeoCodingManagerEngineNokia::~QGeoCodingManagerEngineNokia() {} + +QString QGeoCodingManagerEngineNokia::getAuthenticationString() const +{ + QString authenticationString; + + if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { + authenticationString += "?app_code="; + authenticationString += m_token; + + authenticationString += "&app_id="; + authenticationString += m_applicationId; + } + + return authenticationString; +} + + +QGeoCodeReply *QGeoCodingManagerEngineNokia::geocode(const QGeoAddress &address, + const QGeoShape &bounds) +{ + QString requestString = "https://"; + requestString += m_uriProvider->getCurrentHost(); + requestString += "/6.2/geocode.json"; + + requestString += getAuthenticationString(); + requestString += "&gen=9"; + + requestString += "&language="; + requestString += languageToMarc(locale().language()); + + bool manualBoundsRequired = false; + if (bounds.type() == QGeoShape::UnknownType) { + manualBoundsRequired = true; + } else if (bounds.type() == QGeoShape::CircleType) { + QGeoCircle circ(bounds); + if (circ.isValid()) { + requestString += "?prox="; + requestString += trimDouble(circ.center().latitude()); + requestString += ","; + requestString += trimDouble(circ.center().longitude()); + requestString += ","; + requestString += trimDouble(circ.radius()); + } + } else { + QGeoRectangle rect = bounds.boundingGeoRectangle(); + if (rect.isValid()) { + requestString += "&bbox="; + requestString += trimDouble(rect.topLeft().latitude()); + requestString += ","; + requestString += trimDouble(rect.topLeft().longitude()); + requestString += ";"; + requestString += trimDouble(rect.bottomRight().latitude()); + requestString += ","; + requestString += trimDouble(rect.bottomRight().longitude()); + } + } + + if (address.country().isEmpty()) { + QStringList parts; + + if (!address.state().isEmpty()) + parts << address.state(); + + if (!address.city().isEmpty()) + parts << address.city(); + + if (!address.postalCode().isEmpty()) + parts << address.postalCode(); + + if (!address.street().isEmpty()) + parts << address.street(); + + requestString += "&searchtext="; + requestString += parts.join("+").replace(' ', '+'); + } else { + requestString += "&country="; + requestString += address.country(); + + if (!address.state().isEmpty()) { + requestString += "&state="; + requestString += address.state(); + } + + if (!address.city().isEmpty()) { + requestString += "&city="; + requestString += address.city(); + } + + if (!address.postalCode().isEmpty()) { + requestString += "&postalcode="; + requestString += address.postalCode(); + } + + if (!address.street().isEmpty()) { + requestString += "&street="; + requestString += address.street(); + } + } + + return geocode(requestString, bounds, manualBoundsRequired); +} + +QGeoCodeReply *QGeoCodingManagerEngineNokia::geocode(const QString &address, + int limit, + int offset, + const QGeoShape &bounds) +{ + QString requestString = "https://"; + requestString += m_uriProvider->getCurrentHost(); + requestString += "/6.2/geocode.json"; + + requestString += getAuthenticationString(); + requestString += "&gen=9"; + + requestString += "&language="; + requestString += languageToMarc(locale().language()); + + requestString += "&searchtext="; + requestString += QString(address).replace(' ', '+'); + + if (limit > 0) { + requestString += "&maxresults="; + requestString += QString::number(limit); + } + if (offset > 0) { + // We cannot do this precisely, since HERE doesn't allow + // precise result-set offset to be supplied; instead, it + // returns "pages" of results at a time. + // So, we tell HERE which page of results we want, and the + // client has to filter out duplicates if they changed + // the limit param since the last call. + requestString += "&pageinformation="; + requestString += QString::number(offset/limit); + } + + bool manualBoundsRequired = false; + if (bounds.type() == QGeoShape::RectangleType) { + QGeoRectangle rect(bounds); + if (rect.isValid()) { + requestString += "&bbox="; + requestString += trimDouble(rect.topLeft().latitude()); + requestString += ","; + requestString += trimDouble(rect.topLeft().longitude()); + requestString += ";"; + requestString += trimDouble(rect.bottomRight().latitude()); + requestString += ","; + requestString += trimDouble(rect.bottomRight().longitude()); + } + } else if (bounds.type() == QGeoShape::CircleType) { + QGeoCircle circ(bounds); + if (circ.isValid()) { + requestString += "?prox="; + requestString += trimDouble(circ.center().latitude()); + requestString += ","; + requestString += trimDouble(circ.center().longitude()); + requestString += ","; + requestString += trimDouble(circ.radius()); + } + } else { + manualBoundsRequired = true; + } + + return geocode(requestString, bounds, manualBoundsRequired, limit, offset); +} + +QGeoCodeReply *QGeoCodingManagerEngineNokia::geocode(QString requestString, + const QGeoShape &bounds, + bool manualBoundsRequired, + int limit, + int offset) +{ + QGeoCodeReplyNokia *reply = new QGeoCodeReplyNokia( + m_networkManager->get(QNetworkRequest(QUrl(requestString))), + limit, offset, bounds, manualBoundsRequired, this); + + connect(reply, &QGeoCodeReplyNokia::finished, + this, &QGeoCodingManagerEngineNokia::placesFinished); + + connect(reply, static_cast(&QGeoCodeReplyNokia::error), + this, &QGeoCodingManagerEngineNokia::placesError); + + return reply; +} + +QGeoCodeReply *QGeoCodingManagerEngineNokia::reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) +{ + QString requestString = "https://"; + requestString += m_reverseGeocodingUriProvider->getCurrentHost(); + requestString += "/6.2/reversegeocode.json"; + + requestString += getAuthenticationString(); + requestString += "&gen=9"; + + requestString += "&mode=retrieveAddresses"; + + requestString += "&prox="; + requestString += trimDouble(coordinate.latitude()); + requestString += ","; + requestString += trimDouble(coordinate.longitude()); + + bool manualBoundsRequired = false; + if (bounds.type() == QGeoShape::CircleType) { + QGeoCircle circ(bounds); + if (circ.isValid() && circ.center() == coordinate) { + requestString += ","; + requestString += trimDouble(circ.radius()); + } else { + manualBoundsRequired = true; + } + } else { + manualBoundsRequired = true; + } + + requestString += "&language="; + requestString += languageToMarc(locale().language()); + + return geocode(requestString, bounds, manualBoundsRequired); +} + +QString QGeoCodingManagerEngineNokia::trimDouble(double degree, int decimalDigits) +{ + QString sDegree = QString::number(degree, 'g', decimalDigits); + + int index = sDegree.indexOf('.'); + + if (index == -1) + return sDegree; + else + return QString::number(degree, 'g', decimalDigits + index); +} + +void QGeoCodingManagerEngineNokia::placesFinished() +{ + QGeoCodeReply *reply = qobject_cast(sender()); + + if (!reply) + return; + + if (receivers(SIGNAL(finished(QGeoCodeReply*))) == 0) { + reply->deleteLater(); + return; + } + + emit finished(reply); +} + +void QGeoCodingManagerEngineNokia::placesError(QGeoCodeReply::Error error, const QString &errorString) +{ + QGeoCodeReply *reply = qobject_cast(sender()); + + if (!reply) + return; + + if (receivers(SIGNAL(error(QGeoCodeReply*,QGeoCodeReply::Error,QString))) == 0) { + reply->deleteLater(); + return; + } + + emit this->error(reply, error, errorString); +} + +QString QGeoCodingManagerEngineNokia::languageToMarc(QLocale::Language language) +{ + uint offset = 3 * (uint(language)); + if (language == QLocale::C || offset + 3 > sizeof(marc_language_code_list)) + return QLatin1String("eng"); + + const unsigned char *c = marc_language_code_list + offset; + if (c[0] == 0) + return QLatin1String("eng"); + + QString code(3, Qt::Uninitialized); + code[0] = ushort(c[0]); + code[1] = ushort(c[1]); + code[2] = ushort(c[2]); + + return code; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h b/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h new file mode 100644 index 0000000..9e1564a --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeocodingmanagerengine_nokia.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGER_NOKIA_H +#define QGEOCODINGMANAGER_NOKIA_H + +#include "qgeoserviceproviderplugin_nokia.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoNetworkAccessManager; +class QGeoUriProvider; + +class QGeoCodingManagerEngineNokia : public QGeoCodingManagerEngine +{ + Q_OBJECT +public: + QGeoCodingManagerEngineNokia(QGeoNetworkAccessManager *networkManager, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoCodingManagerEngineNokia(); + + QGeoCodeReply *geocode(const QGeoAddress &address, + const QGeoShape &bounds); + QGeoCodeReply *reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds); + + QGeoCodeReply *geocode(const QString &searchString, + int limit, + int offset, + const QGeoShape &bounds); + +private Q_SLOTS: + void placesFinished(); + void placesError(QGeoCodeReply::Error error, const QString &errorString); + +private: + static QString trimDouble(double degree, int decimalDigits = 10); + QGeoCodeReply *geocode(QString requestString, const QGeoShape &bounds, bool manualBoundsRequired = true, int limit = -1, int offset = 0); + QString languageToMarc(QLocale::Language language); + QString getAuthenticationString() const; + + QGeoNetworkAccessManager *m_networkManager; + QGeoUriProvider *m_uriProvider; + QGeoUriProvider *m_reverseGeocodingUriProvider; + QString m_token; + QString m_applicationId; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeoerror_messages.cpp b/src/plugins/geoservices/nokia/qgeoerror_messages.cpp new file mode 100644 index 0000000..576ecd4 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoerror_messages.cpp @@ -0,0 +1,52 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoerror_messages.h" + +QT_BEGIN_NAMESPACE + +const char NOKIA_PLUGIN_CONTEXT_NAME[] = "QtLocationQML"; +const char MISSED_CREDENTIALS[] = QT_TRANSLATE_NOOP("QtLocationQML", "Qt Location requires app_id and token parameters.\nPlease register at https://developer.here.com/ to get your personal application credentials."); +const char SAVING_PLACE_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Saving places is not supported."); +const char REMOVING_PLACE_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Removing places is not supported."); +const char SAVING_CATEGORY_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Saving categories is not supported."); +const char REMOVING_CATEGORY_NOT_SUPPORTED[] = QT_TRANSLATE_NOOP("QtLocationQML", "Removing categories is not supported."); +const char PARSE_ERROR[] = QT_TRANSLATE_NOOP("QtLocationQML", "Error parsing response."); +const char NETWORK_ERROR[] = QT_TRANSLATE_NOOP("QtLocationQML", "Network error."); +const char CANCEL_ERROR[] = QT_TRANSLATE_NOOP("QtLocationQML", "Request was canceled."); +const char RESPONSE_NOT_RECOGNIZABLE[] = QT_TRANSLATE_NOOP("QtLocationQML", "The response from the service was not in a recognizable format."); + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeoerror_messages.h b/src/plugins/geoservices/nokia/qgeoerror_messages.h new file mode 100644 index 0000000..8bae1f2 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoerror_messages.h @@ -0,0 +1,58 @@ + +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOERROR_MESSAGES_H +#define QGEOERROR_MESSAGES_H + +#include + +QT_BEGIN_NAMESPACE + +extern const char NOKIA_PLUGIN_CONTEXT_NAME[]; +extern const char MISSED_CREDENTIALS[]; +extern const char SAVING_PLACE_NOT_SUPPORTED[]; +extern const char REMOVING_PLACE_NOT_SUPPORTED[]; +extern const char SAVING_CATEGORY_NOT_SUPPORTED[]; +extern const char REMOVING_CATEGORY_NOT_SUPPORTED[]; +extern const char PARSE_ERROR[]; +extern const char NETWORK_ERROR[]; +extern const char CANCEL_ERROR[]; +extern const char RESPONSE_NOT_RECOGNIZABLE[]; + +QT_END_NAMESPACE + +#endif // QGEOERROR_MESSAGES_H diff --git a/src/plugins/geoservices/nokia/qgeofiletilecachenokia.cpp b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.cpp new file mode 100644 index 0000000..8b79532 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeofiletilecachenokia.h" +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoFileTileCacheNokia::QGeoFileTileCacheNokia(int ppi, const QString &directory, QObject *parent) + :QGeoFileTileCache(directory, parent) +{ + m_ppi = QString::number(ppi) + QLatin1String("p"); +} + +QGeoFileTileCacheNokia::~QGeoFileTileCacheNokia() +{ + +} + +QString QGeoFileTileCacheNokia::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const +{ + QString filename = spec.plugin(); + filename += QLatin1String("-"); + filename += QString::number(spec.mapId()); + filename += QLatin1String("-"); + filename += QString::number(spec.zoom()); + filename += QLatin1String("-"); + filename += QString::number(spec.x()); + filename += QLatin1String("-"); + filename += QString::number(spec.y()); + + //Append version if real version number to ensure backwards compatibility and eviction of old tiles + if (spec.version() != -1) { + filename += QLatin1String("-"); + filename += QString::number(spec.version()); + } + + filename += QLatin1String("-"); + filename += m_ppi; + + filename += QLatin1String("."); + filename += format; + + QDir dir = QDir(directory); + + return dir.filePath(filename); +} + +QGeoTileSpec QGeoFileTileCacheNokia::filenameToTileSpec(const QString &filename) const +{ + QGeoTileSpec emptySpec; + + QStringList parts = filename.split('.'); + + if (parts.length() != 2) + return emptySpec; + + QString name = parts.at(0); + QStringList fields = name.split('-'); + + int length = fields.length(); + if (length != 6 && length != 7) + return emptySpec; + else if (fields.last() != m_ppi) + return QGeoTileSpec(); + + QList numbers; + + bool ok = false; + for (int i = 1; i < length-1; ++i) { // skipping - + ok = false; + int value = fields.at(i).toInt(&ok); + if (!ok) + return emptySpec; + numbers.append(value); + } + + //File name without version, append default + if (numbers.length() < 5) + numbers.append(-1); + + return QGeoTileSpec(fields.at(0), + numbers.at(0), + numbers.at(1), + numbers.at(2), + numbers.at(3), + numbers.at(4)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeofiletilecachenokia.h b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.h new file mode 100644 index 0000000..4dcd445 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeofiletilecachenokia.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOFILETILECACHENOKIA_H +#define QGEOFILETILECACHENOKIA_H + +#include + +QT_BEGIN_NAMESPACE + +class QGeoFileTileCacheNokia : public QGeoFileTileCache +{ + Q_OBJECT +public: + QGeoFileTileCacheNokia(int ppi, const QString &directory = QString(), QObject *parent = 0); + ~QGeoFileTileCacheNokia(); + +protected: + virtual QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const override; + virtual QGeoTileSpec filenameToTileSpec(const QString &filename) const override; + + QString m_ppi; +}; + +QT_END_NAMESPACE + +#endif // QGEOFILETILECACHENOKIA_H diff --git a/src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.cpp b/src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.cpp new file mode 100644 index 0000000..267c4ca --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeointrinsicnetworkaccessmanager.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoIntrinsicNetworkAccessManager::QGeoIntrinsicNetworkAccessManager(QObject *parent) +: QGeoNetworkAccessManager(parent) +, m_networkManager(new QNetworkAccessManager(this)) +{ +} + +QGeoIntrinsicNetworkAccessManager::QGeoIntrinsicNetworkAccessManager(const QVariantMap ¶meters, const QString &token, QObject *parent) +: QGeoNetworkAccessManager(parent) +, m_customProxyToken(token) +, m_networkManager(new QNetworkAccessManager(this)) +{ + configure(parameters); +} + +void QGeoIntrinsicNetworkAccessManager::configure(const QVariantMap ¶meters) +{ + QString proxy = parameters.value(QStringLiteral("here.proxy")).toString(); + if (proxy.isEmpty() && !m_customProxyToken.isEmpty()) + proxy = parameters.value(m_customProxyToken).toString(); + + if (!proxy.isEmpty()) { +#ifndef QT_NO_NETWORKPROXY + if (proxy.toLower() != QStringLiteral("system")) { + QUrl proxyUrl(proxy); + if (proxyUrl.isValid()) { + qDebug() << "Setting proxy to " << proxyUrl.toString(); + m_networkManager->setProxy( + QNetworkProxy(QNetworkProxy::HttpProxy, + proxyUrl.host(), + proxyUrl.port(8080), + proxyUrl.userName(), + proxyUrl.password())); + } + } else if (QNetworkProxy::applicationProxy().type() == QNetworkProxy::NoProxy) { + QNetworkProxyFactory::setUseSystemConfiguration(true); + qDebug() << "Setting system proxy."; + } +#else + qDebug() << "No proxy support"; +#endif + } else { + qDebug() << "No proxy parameter specified."; + } +} + +QNetworkReply *QGeoIntrinsicNetworkAccessManager::get(const QNetworkRequest &request) +{ + return m_networkManager->get(request); +} + +QNetworkReply *QGeoIntrinsicNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data) +{ + return m_networkManager->post(request, data); +} +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.h b/src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.h new file mode 100644 index 0000000..fb5ab48 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeointrinsicnetworkaccessmanager.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOINTRINSICNETWORKACCESSMANAGER_H +#define QGEOINTRINSICNETWORKACCESSMANAGER_H + +#include "qgeonetworkaccessmanager.h" + +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class QGeoIntrinsicNetworkAccessManager : public QGeoNetworkAccessManager +{ +public: + explicit QGeoIntrinsicNetworkAccessManager(QObject *parent = 0); + QGeoIntrinsicNetworkAccessManager(const QVariantMap ¶meters, const QString &token = QString(), QObject *parent = 0); + + virtual QNetworkReply *get(const QNetworkRequest &request); + virtual QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data); + +private: + void configure(const QVariantMap ¶meters); + + const QString m_customProxyToken; + QNetworkAccessManager *m_networkManager; +}; + +QT_END_NAMESPACE + +#endif // QGEOINTRINSICNETWORKACCESSMANAGER_H diff --git a/src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp b/src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp new file mode 100644 index 0000000..6e1a1e8 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeomapreply_nokia.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapreply_nokia.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoMapReplyNokia::QGeoMapReplyNokia(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent) + : QGeoTiledMapReply(spec, parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, + SIGNAL(finished()), + this, + SLOT(networkFinished())); + + connect(reply, + SIGNAL(error(QNetworkReply::NetworkError)), + this, + SLOT(networkError(QNetworkReply::NetworkError))); + connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QGeoMapReplyNokia::~QGeoMapReplyNokia() +{ +} + +void QGeoMapReplyNokia::networkFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + setMapImageData(reply->readAll()); + setMapImageFormat("png"); + setFinished(true); +} + +void QGeoMapReplyNokia::networkError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) + setFinished(true); + else + setError(QGeoTiledMapReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeomapreply_nokia.h b/src/plugins/geoservices/nokia/qgeomapreply_nokia.h new file mode 100644 index 0000000..d835757 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeomapreply_nokia.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPREPLY_NOKIA_H +#define QGEOMAPREPLY_NOKIA_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMapReplyNokia : public QGeoTiledMapReply +{ + Q_OBJECT + +public: + QGeoMapReplyNokia(QNetworkReply *reply, const QGeoTileSpec &spec, QObject *parent = 0); + ~QGeoMapReplyNokia(); + +private Q_SLOTS: + void networkFinished(); + void networkError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeomapversion.cpp b/src/plugins/geoservices/nokia/qgeomapversion.cpp new file mode 100644 index 0000000..4432762 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeomapversion.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Appello Systems AB. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapversion.h" + +#include + +QT_BEGIN_NAMESPACE + +QGeoMapVersion::QGeoMapVersion() + : m_version(-1) {} + +bool QGeoMapVersion::isNewVersion(const QJsonObject &newVersionData) +{ + return m_versionData != newVersionData; +} + +int QGeoMapVersion::version() const +{ + return m_version; +} + +void QGeoMapVersion::setVersion(int version) +{ + m_version = version; +} + +void QGeoMapVersion::setVersionData(const QJsonObject &versionData) +{ + m_versionData = versionData; +} + + +QByteArray QGeoMapVersion::toJson() const +{ + + QJsonObject object; + object[QLatin1String("version")] = m_version; + object[QLatin1String("data")] = m_versionData; + + QJsonDocument document(object); + + return document.toJson(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeomapversion.h b/src/plugins/geoservices/nokia/qgeomapversion.h new file mode 100644 index 0000000..0e602e6 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeomapversion.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Appello Systems AB. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPVERSION_H +#define QGEOMAPVERSION_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMapVersion +{ + +public: + QGeoMapVersion(); + bool isNewVersion(const QJsonObject &newVersionData); + int version() const; + void setVersion(const int); + void setVersionData(const QJsonObject &versionData); + QByteArray toJson() const; + +private: + int m_version; + QJsonObject m_versionData; +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPVERSION_H diff --git a/src/plugins/geoservices/nokia/qgeonetworkaccessmanager.h b/src/plugins/geoservices/nokia/qgeonetworkaccessmanager.h new file mode 100644 index 0000000..541f00c --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeonetworkaccessmanager.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEONETWORKACCESSMANAGER_H +#define QGEONETWORKACCESSMANAGER_H + +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QNetworkRequest; +class QByteArray; + +class QGeoNetworkAccessManager : public QObject +{ + Q_OBJECT +public: + virtual ~QGeoNetworkAccessManager() {} + virtual QNetworkReply *get(const QNetworkRequest &request) = 0; + virtual QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data) = 0; + +protected: + QGeoNetworkAccessManager(QObject *parent) : QObject(parent) {} +}; + +QT_END_NAMESPACE + +#endif // QGEONETWORKACCESSMANAGER_H diff --git a/src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp b/src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp new file mode 100644 index 0000000..b5fdfee --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoroutereply_nokia.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutereply_nokia.h" +#include "qgeoroutexmlparser.h" +#include "qgeoerror_messages.h" + +#include + +#include + +Q_DECLARE_METATYPE(QList) + +QT_BEGIN_NAMESPACE + +QGeoRouteReplyNokia::QGeoRouteReplyNokia(const QGeoRouteRequest &request, + const QList &replies, + QObject *parent) +: QGeoRouteReply(request, parent), m_parsers(0) +{ + qRegisterMetaType >(); + + bool failure = false; + foreach (QNetworkReply *reply, replies) { + if (!reply) { + failure = true; + continue; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkError(QNetworkReply::NetworkError))); + connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + } + if (failure) + setError(UnknownError, QStringLiteral("Null reply")); + else + connect(this, &QGeoRouteReply::aborted, [this](){ m_parsers = 0; }); +} + +QGeoRouteReplyNokia::~QGeoRouteReplyNokia() +{ +} + +void QGeoRouteReplyNokia::networkFinished() +{ + QNetworkReply *reply = qobject_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError + && reply->error() != QNetworkReply::UnknownContentError) { + return; + } + + QGeoRouteXmlParser *parser = new QGeoRouteXmlParser(request()); + connect(parser, SIGNAL(results(QList)), + this, SLOT(appendResults(QList))); + connect(parser, SIGNAL(error(QString)), this, SLOT(parserError(QString))); + + ++m_parsers; + parser->parse(reply->readAll()); +} + +void QGeoRouteReplyNokia::networkError(QNetworkReply::NetworkError error) +{ + if (error == QNetworkReply::UnknownContentError) + return; + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QGeoRouteReply::CommunicationError, reply->errorString()); + if (error != QNetworkReply::OperationCanceledError) // Any error not caused by abort() + emit aborted(); // aborts all unfinished replies and sets m_parsers to 0 +} + +void QGeoRouteReplyNokia::appendResults(const QList &routes) +{ + if (!m_parsers) + return; + + --m_parsers; + addRoutes(routes); + + if (!m_parsers) + setFinished(true); +} + +void QGeoRouteReplyNokia::parserError(const QString &errorString) +{ + Q_UNUSED(errorString) + emit aborted(); + setError(QGeoRouteReply::ParseError, + QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, RESPONSE_NOT_RECOGNIZABLE)); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeoroutereply_nokia.h b/src/plugins/geoservices/nokia/qgeoroutereply_nokia.h new file mode 100644 index 0000000..d38262a --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoroutereply_nokia.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREPLY_NOKIA_H +#define QGEOROUTEREPLY_NOKIA_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteXmlParser; + +class QGeoRouteReplyNokia : public QGeoRouteReply +{ + Q_OBJECT +public: + QGeoRouteReplyNokia(const QGeoRouteRequest &request, const QList &replies, QObject *parent = 0); + ~QGeoRouteReplyNokia(); + +private Q_SLOTS: + void networkFinished(); + void networkError(QNetworkReply::NetworkError error); + void appendResults(const QList &routes); + void parserError(const QString &errorString); + +private: + int m_parsers; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp b/src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp new file mode 100644 index 0000000..8e436a9 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoroutexmlparser.cpp @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutexmlparser.h" + +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoDynamicSpeedInfoContainer::QGeoDynamicSpeedInfoContainer() +: trafficSpeed(0) +, baseSpeed(0) +, trafficTime(0) +, baseTime(0) +{} + +QGeoRouteXmlParser::QGeoRouteXmlParser(const QGeoRouteRequest &request) + : m_request(request) +{ +} + +QGeoRouteXmlParser::~QGeoRouteXmlParser() +{ +} + +void QGeoRouteXmlParser::parse(const QByteArray &data) +{ + m_data = data; + QThreadPool::globalInstance()->start(this); +} + +void QGeoRouteXmlParser::run() +{ + m_reader = new QXmlStreamReader(m_data); + + if (!parseRootElement()) + emit error(m_reader->errorString()); + else + emit results(m_results); + + delete m_reader; + m_reader = 0; +} + +bool QGeoRouteXmlParser::parseRootElement() +{ + if (!m_reader->readNextStartElement()) { + m_reader->raiseError("Expected a root element named \"CalculateRoute\" (no root element found)."); + return false; + } + + if (m_reader->name() == QLatin1String("Error")) { + QXmlStreamAttributes attributes = m_reader->attributes(); + if (attributes.value(QStringLiteral("type")) == QLatin1String("ApplicationError") + && attributes.value("subtype") == QLatin1String("NoRouteFound")) + return true; + } + + bool updateroute = false; + if (m_reader->name() != "CalculateRoute" && m_reader->name() != "GetRoute") { + m_reader->raiseError(QString("The root element is expected to have the name \"CalculateRoute\" or \"GetRoute\" (root element was named \"%1\").").arg(m_reader->name().toString())); + return false; + } else if (m_reader->name() == "GetRoute") { + updateroute = true; + } + + if (m_reader->readNextStartElement()) { + if (m_reader->name() != "Response") { + m_reader->raiseError(QString("Expected a element named \"Response\" (element was named \"%1\").").arg(m_reader->name().toString())); + return false; + } + } + + while (m_reader->readNextStartElement() && !m_reader->hasError()) { + if (m_reader->name() == "Route") { + QGeoRoute route; + route.setRequest(m_request); + if (updateroute) + route.setTravelMode(QGeoRouteRequest::TravelMode(int(m_request.travelModes()))); + if (!parseRoute(&route)) + continue; //route parsing failed move on to the next + m_results.append(route); + } else if (m_reader->name() == "Progress") { + //TODO: updated route progress + m_reader->skipCurrentElement(); + } else { + m_reader->skipCurrentElement(); + } + } + + return !m_reader->hasError(); +} + +bool QGeoRouteXmlParser::parseRoute(QGeoRoute *route) +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Route"); + m_maneuvers.clear(); + m_segments.clear(); + + m_reader->readNext(); + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Route") && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == "RouteId") { + route->setRouteId(m_reader->readElementText()); + } + //else if (m_reader->name() == "Waypoint") { + // succeeded = parseWaypoint(route); + //} + else if (m_reader->name() == "Mode") { + if (!parseMode(route)) + return false; + } else if (m_reader->name() == "Shape") { + QString elementName = m_reader->name().toString(); + QList path; + if (!parseGeoPoints(m_reader->readElementText(), &path, elementName)) + return false; + route->setPath(path); + } else if (m_reader->name() == "BoundingBox") { + QGeoRectangle bounds; + if (!parseBoundingBox(bounds)) + return false; + route->setBounds(bounds); + } else if (m_reader->name() == "Leg") { + if (!parseLeg()) + return false; + } else if (m_reader->name() == "Summary") { + if (!parseSummary(route)) + return false; + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + + if (m_reader->hasError()) + return false; + + return postProcessRoute(route); +} + +bool QGeoRouteXmlParser::parseLeg() +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Leg"); + + m_reader->readNext(); + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Leg") && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == "Maneuver") { + if (!parseManeuver()) + return false; + } else if (m_reader->name() == "Link") { + if (!parseLink()) + return false; + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + + return !m_reader->hasError(); +} + +bool QGeoRouteXmlParser::postProcessRoute(QGeoRoute *route) +{ + QList routeSegments; + + int maneuverIndex = 0; + for (int i = 0; i < m_segments.count(); ++i) { + // In case there is a maneuver in the middle of the list with no + // link ID attached, attach it to the next available segment + while ((maneuverIndex < m_maneuvers.size() - 1) && m_maneuvers.at(maneuverIndex).toId.isEmpty()) { + QGeoRouteSegment segment; + segment.setManeuver(m_maneuvers.at(maneuverIndex).maneuver); + QList path; // use instruction position as one point segment path + path.append(m_maneuvers.at(maneuverIndex).maneuver.position()); + segment.setPath(path); + routeSegments.append(segment); + ++maneuverIndex; + } + + QGeoRouteSegment segment = m_segments.at(i).segment; + if ((maneuverIndex < m_maneuvers.size()) && m_segments.at(i).id == m_maneuvers.at(maneuverIndex).toId) { + segment.setManeuver(m_maneuvers.at(maneuverIndex).maneuver); + ++maneuverIndex; + } + routeSegments.append(segment); + } + + // For the final maneuver in the list, make sure to attach it to the very + // last segment on the path, this is why we don't process the last + // maneuver in the loop above + while (maneuverIndex < m_maneuvers.size()) { + QGeoRouteSegment segment; + segment.setManeuver(m_maneuvers.at(maneuverIndex).maneuver); + QList path; // use instruction position as one point segment path + path.append(m_maneuvers.at(maneuverIndex).maneuver.position()); + segment.setPath(path); + + routeSegments.append(segment); + ++maneuverIndex; + } + + QList compactedRouteSegments; + compactedRouteSegments.append(routeSegments.first()); + routeSegments.removeFirst(); + + while (routeSegments.size() > 0) { + QGeoRouteSegment segment = routeSegments.first(); + routeSegments.removeFirst(); + + QGeoRouteSegment lastSegment = compactedRouteSegments.last(); + + if (lastSegment.maneuver().isValid()) { + compactedRouteSegments.append(segment); + } else { + compactedRouteSegments.removeLast(); + lastSegment.setDistance(lastSegment.distance() + segment.distance()); + lastSegment.setTravelTime(lastSegment.travelTime() + segment.travelTime()); + QList path = lastSegment.path(); + path.append(segment.path()); + lastSegment.setPath(path); + lastSegment.setManeuver(segment.maneuver()); + compactedRouteSegments.append(lastSegment); + } + } + + if (compactedRouteSegments.size() > 0) { + route->setFirstRouteSegment(compactedRouteSegments.at(0)); + for (int i = 0; i < compactedRouteSegments.size() - 1; ++i) + compactedRouteSegments[i].setNextRouteSegment(compactedRouteSegments.at(i + 1)); + } + + m_maneuvers.clear(); + m_segments.clear(); + return true; +} + +/* +bool QGeoRouteXmlParser::parseWaypoint(QGeoRoute *route) +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Waypoint"); + m_reader->readNext(); + QList path(route->pathSummary()); + + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Waypoint")) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == "MappedPosition") { + QGeoCoordinate coordinates; + if(!parseCoordinates(coordinates)) + return false; + path.append(coordinates); + } + else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + route->setPathSummary(path); + return true; +} +*/ + +bool QGeoRouteXmlParser::parseMode(QGeoRoute *route) +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Mode"); + m_reader->readNext(); + + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Mode") && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == "TransportModes") { + QString value = m_reader->readElementText(); + if (value == "car") + route->setTravelMode(QGeoRouteRequest::CarTravel); + else if (value == "pedestrian") + route->setTravelMode(QGeoRouteRequest::PedestrianTravel); + else if (value == "publicTransport") + route->setTravelMode(QGeoRouteRequest::PublicTransitTravel); + else if (value == "bicycle") + route->setTravelMode(QGeoRouteRequest::BicycleTravel); + else if (value == "truck") + route->setTravelMode(QGeoRouteRequest::TruckTravel); + else { + // unsupported mode + m_reader->raiseError(QString("Unsupported travel mode '\"%1\"'").arg(value)); + return false; + } + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + return !m_reader->hasError(); +} + +bool QGeoRouteXmlParser::parseSummary(QGeoRoute *route) +{ + Q_ASSERT(route); + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Summary"); + m_reader->readNext(); + + double baseTime = -1, trafficTime = -1; + + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Summary") && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == "Distance") { + route->setDistance(m_reader->readElementText().toDouble()); + } else if (m_reader->name() == "TrafficTime") { + trafficTime = m_reader->readElementText().toDouble(); + } else if (m_reader->name() == "BaseTime") { + baseTime = m_reader->readElementText().toDouble(); + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + + if (m_reader->hasError()) + return false; + + if (trafficTime >= 0) + route->setTravelTime(trafficTime); + else if (baseTime >= 0) + route->setTravelTime(baseTime); + + return true; +} + +bool QGeoRouteXmlParser::parseCoordinates(QGeoCoordinate &coord) +{ + QString currentElement = m_reader->name().toString(); + m_reader->readNext(); + + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == currentElement) && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + QString name = m_reader->name().toString(); + QString value = m_reader->readElementText(); + if (name == "Latitude") + coord.setLatitude(value.toDouble()); + else if (name == "Longitude") + coord.setLongitude(value.toDouble()); + } + m_reader->readNext(); + } + + return !m_reader->hasError(); +} + +bool QGeoRouteXmlParser::parseManeuver() +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "Maneuver"); + + if (!m_reader->attributes().hasAttribute("id")) { + m_reader->raiseError("The element \"Maneuver\" did not have the required attribute \"id\"."); + return false; + } + QGeoManeuverContainer maneuverContainter; + maneuverContainter.id = m_reader->attributes().value("id").toString(); + + m_reader->readNext(); + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "Maneuver") && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == "Position") { + QGeoCoordinate coordinates; + if (parseCoordinates(coordinates)) + maneuverContainter.maneuver.setPosition(coordinates); + } else if (m_reader->name() == "Instruction") { + maneuverContainter.maneuver.setInstructionText(m_reader->readElementText()); + } else if (m_reader->name() == "ToLink") { + maneuverContainter.toId = m_reader->readElementText(); + } else if (m_reader->name() == "TravelTime") { + maneuverContainter.maneuver.setTimeToNextInstruction(qRound(m_reader->readElementText().toDouble())); + } else if (m_reader->name() == "Length") { + maneuverContainter.maneuver.setDistanceToNextInstruction(m_reader->readElementText().toDouble()); + } else if (m_reader->name() == "Direction") { + QString value = m_reader->readElementText(); + if (value == "forward") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionForward); + else if (value == "bearRight") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionBearRight); + else if (value == "lightRight") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLightRight); + else if (value == "right") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionRight); + else if (value == "hardRight") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionHardRight); + else if (value == "uTurnRight") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionUTurnRight); + else if (value == "uTurnLeft") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionUTurnLeft); + else if (value == "hardLeft") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionHardLeft); + else if (value == "left") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLeft); + else if (value == "lightLeft") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionLightLeft); + else if (value == "bearLeft") + maneuverContainter.maneuver.setDirection(QGeoManeuver::DirectionBearLeft); + else + maneuverContainter.maneuver.setDirection(QGeoManeuver::NoDirection); + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + + if (m_reader->hasError()) + return false; + + m_maneuvers.append(maneuverContainter); + return true; +} + +bool QGeoRouteXmlParser::parseLink() +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("Link")); + m_reader->readNext(); + + QGeoRouteSegmentContainer segmentContainer; + + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == QStringLiteral("Link")) && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == QStringLiteral("LinkId")) { + segmentContainer.id = m_reader->readElementText(); + } else if (m_reader->name() == QStringLiteral("Shape")) { + QString elementName = m_reader->name().toString(); + QList path; + parseGeoPoints(m_reader->readElementText(), &path, elementName); + segmentContainer.segment.setPath(path); + } else if (m_reader->name() == QStringLiteral("Length")) { + segmentContainer.segment.setDistance(m_reader->readElementText().toDouble()); + } else if (m_reader->name() == QStringLiteral("Maneuver")) { + segmentContainer.maneuverId = m_reader->readElementText(); + } else if (m_reader->name() == QStringLiteral("DynamicSpeedInfo")) { + QGeoDynamicSpeedInfoContainer speedInfo; + if (!parseDynamicSpeedInfo(speedInfo)) + return false; + const double time = speedInfo.trafficTime >= 0 ? speedInfo.trafficTime : speedInfo.baseTime; + if (time >= 0) + segmentContainer.segment.setTravelTime(time); + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + + if (m_reader->hasError()) + return false; + + m_segments.append(segmentContainer); + return true; +} + +bool QGeoRouteXmlParser::parseGeoPoints(const QString &strPoints, QList *geoPoints, const QString &elementName) +{ + QStringList rawPoints = strPoints.split(' '); + + for (int i = 0; i < rawPoints.length(); ++i) { + QStringList coords = rawPoints[i].split(','); + + if (coords.length() != 2) { + m_reader->raiseError(QString("Each of the space separated values of \"%1\" is expected to be a comma separated pair of coordinates (value was \"%2\")").arg(elementName).arg(rawPoints[i])); + return false; + } + + bool ok = false; + QString latString = coords[0]; + double lat = latString.toDouble(&ok); + + if (!ok) { + m_reader->raiseError(QString("The latitude portions of \"%1\" are expected to have a value convertable to a double (value was \"%2\")").arg(elementName).arg(latString)); + return false; + } + + QString lngString = coords[1]; + double lng = lngString.toDouble(&ok); + + if (!ok) { + m_reader->raiseError(QString("The longitude portions of \"%1\" are expected to have a value convertable to a double (value was \"%2\")").arg(elementName).arg(lngString)); + return false; + } + + QGeoCoordinate geoPoint(lat, lng); + geoPoints->append(geoPoint); + } + + return true; +} + +bool QGeoRouteXmlParser::parseBoundingBox(QGeoRectangle &bounds) +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == "BoundingBox"); + + QGeoCoordinate tl; + QGeoCoordinate br; + + m_reader->readNext(); + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == "BoundingBox") && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == "TopLeft") { + QGeoCoordinate coordinates; + if (parseCoordinates(coordinates)) + tl = coordinates; + } else if (m_reader->name() == "BottomRight") { + QGeoCoordinate coordinates; + if (parseCoordinates(coordinates)) + br = coordinates; + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + + if (m_reader->hasError()) + return false; + + if (tl.isValid() && br.isValid()) { + bounds = QGeoRectangle(tl, br); + return true; + } + + return false; +} + +bool QGeoRouteXmlParser::parseDynamicSpeedInfo(QGeoDynamicSpeedInfoContainer &speedInfo) +{ + Q_ASSERT(m_reader->isStartElement() && m_reader->name() == QStringLiteral("DynamicSpeedInfo")); + + m_reader->readNext(); + while (!(m_reader->tokenType() == QXmlStreamReader::EndElement && m_reader->name() == QStringLiteral("DynamicSpeedInfo")) && + !m_reader->hasError()) { + if (m_reader->tokenType() == QXmlStreamReader::StartElement) { + if (m_reader->name() == QStringLiteral("TrafficSpeed")) { + speedInfo.trafficSpeed = m_reader->readElementText().toDouble(); + } else if (m_reader->name() == QStringLiteral("TrafficTime")) { + speedInfo.trafficTime = qRound(m_reader->readElementText().toDouble()); + } else if (m_reader->name() == QStringLiteral("BaseSpeed")) { + speedInfo.baseSpeed = m_reader->readElementText().toDouble(); + } else if (m_reader->name() == QStringLiteral("BaseTime")) { + speedInfo.baseTime = qRound(m_reader->readElementText().toDouble()); + } else { + m_reader->skipCurrentElement(); + } + } + m_reader->readNext(); + } + + return !m_reader->hasError(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeoroutexmlparser.h b/src/plugins/geoservices/nokia/qgeoroutexmlparser.h new file mode 100644 index 0000000..e2feb72 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoroutexmlparser.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QROUTEXMLPARSER_H +#define QROUTEXMLPARSER_H + +#include +#include +#include +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QXmlStreamReader; +class QGeoRoute; +class QGeoCoordinate; +class QGeoRectangle; + +class QGeoManeuverContainer +{ +public: + QGeoManeuver maneuver; + QString id; + QString toId; +}; + +class QGeoRouteSegmentContainer +{ +public: + QGeoRouteSegment segment; + QString id; + QString maneuverId; +}; + +class QGeoDynamicSpeedInfoContainer +{ +public: + QGeoDynamicSpeedInfoContainer(); + +public: + double trafficSpeed; + double baseSpeed; + int trafficTime; + int baseTime; +}; + +class QGeoRouteXmlParser : public QObject, public QRunnable +{ + Q_OBJECT + +public: + QGeoRouteXmlParser(const QGeoRouteRequest &request); + ~QGeoRouteXmlParser(); + + void parse(const QByteArray &data); + void run(); + +signals: + void results(const QList &routes); + void error(const QString &errorString); + +private: + bool parseRootElement(); + bool parseRoute(QGeoRoute *route); + //bool parseWaypoint(QGeoRoute *route); + bool parseCoordinates(QGeoCoordinate &coord); + bool parseMode(QGeoRoute *route); + bool parseSummary(QGeoRoute *route); + bool parseGeoPoints(const QString &strPoints, QList *geoPoints, const QString &elementName); + bool parseLeg(); + bool parseManeuver(); + bool parseLink(); + bool postProcessRoute(QGeoRoute *route); + + bool parseBoundingBox(QGeoRectangle &bounds); + bool parseDynamicSpeedInfo(QGeoDynamicSpeedInfoContainer &speedInfo); + + QGeoRouteRequest m_request; + QByteArray m_data; + QXmlStreamReader *m_reader; + + QList m_results; + QList m_maneuvers; + QList m_segments; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp b/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp new file mode 100644 index 0000000..1ae0163 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.cpp @@ -0,0 +1,503 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutingmanagerengine_nokia.h" +#include "qgeoroutereply_nokia.h" +#include "qgeonetworkaccessmanager.h" +#include "qgeouriprovider.h" +#include "uri_constants.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoRoutingManagerEngineNokia::QGeoRoutingManagerEngineNokia( + QGeoNetworkAccessManager *networkManager, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) + : QGeoRoutingManagerEngine(parameters) + , m_networkManager(networkManager) + , m_uriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.routing.host"), ROUTING_HOST)) + +{ + Q_ASSERT(networkManager); + m_networkManager->setParent(this); + + m_appId = parameters.value(QStringLiteral("here.app_id")).toString(); + m_token = parameters.value(QStringLiteral("here.token")).toString(); + + QGeoRouteRequest::FeatureTypes featureTypes; + featureTypes |= QGeoRouteRequest::TollFeature; + featureTypes |= QGeoRouteRequest::HighwayFeature; + featureTypes |= QGeoRouteRequest::FerryFeature; + featureTypes |= QGeoRouteRequest::TunnelFeature; + featureTypes |= QGeoRouteRequest::DirtRoadFeature; + featureTypes |= QGeoRouteRequest::ParksFeature; + setSupportedFeatureTypes(featureTypes); + + QGeoRouteRequest::FeatureWeights featureWeights; + featureWeights |= QGeoRouteRequest::DisallowFeatureWeight; + featureWeights |= QGeoRouteRequest::AvoidFeatureWeight; + featureWeights |= QGeoRouteRequest::PreferFeatureWeight; + setSupportedFeatureWeights(featureWeights); + + QGeoRouteRequest::ManeuverDetails maneuverDetails; + maneuverDetails |= QGeoRouteRequest::BasicManeuvers; + setSupportedManeuverDetails(maneuverDetails); + + QGeoRouteRequest::RouteOptimizations optimizations; + optimizations |= QGeoRouteRequest::ShortestRoute; + optimizations |= QGeoRouteRequest::FastestRoute; + setSupportedRouteOptimizations(optimizations); + + QGeoRouteRequest::TravelModes travelModes; + travelModes |= QGeoRouteRequest::CarTravel; + travelModes |= QGeoRouteRequest::PedestrianTravel; + travelModes |= QGeoRouteRequest::PublicTransitTravel; + travelModes |= QGeoRouteRequest::BicycleTravel; + setSupportedTravelModes(travelModes); + + QGeoRouteRequest::SegmentDetails segmentDetails; + segmentDetails |= QGeoRouteRequest::BasicSegmentData; + setSupportedSegmentDetails(segmentDetails); + + if (error) + *error = QGeoServiceProvider::NoError; + + if (errorString) + *errorString = QString(); +} + +QGeoRoutingManagerEngineNokia::~QGeoRoutingManagerEngineNokia() {} + +QGeoRouteReply *QGeoRoutingManagerEngineNokia::calculateRoute(const QGeoRouteRequest &request) +{ + const QStringList reqStrings = calculateRouteRequestString(request); + + if (reqStrings.isEmpty()) { + QGeoRouteReply *reply = new QGeoRouteReply(QGeoRouteReply::UnsupportedOptionError, "The given route request options are not supported by this service provider.", this); + emit error(reply, reply->error(), reply->errorString()); + return reply; + } + + QList replies; + foreach (const QString &reqString, reqStrings) + replies.append(m_networkManager->get(QNetworkRequest(QUrl(reqString)))); + + QGeoRouteReplyNokia *reply = new QGeoRouteReplyNokia(request, replies, this); + + connect(reply, + SIGNAL(finished()), + this, + SLOT(routeFinished())); + + connect(reply, + SIGNAL(error(QGeoRouteReply::Error,QString)), + this, + SLOT(routeError(QGeoRouteReply::Error,QString))); + + return reply; +} + +QGeoRouteReply *QGeoRoutingManagerEngineNokia::updateRoute(const QGeoRoute &route, const QGeoCoordinate &position) +{ + const QStringList reqStrings = updateRouteRequestString(route, position); + + if (reqStrings.isEmpty()) { + QGeoRouteReply *reply = new QGeoRouteReply(QGeoRouteReply::UnsupportedOptionError, "The given route request options are not supported by this service provider.", this); + emit error(reply, reply->error(), reply->errorString()); + return reply; + } + + QList replies; + foreach (const QString &reqString, reqStrings) + replies.append(m_networkManager->get(QNetworkRequest(QUrl(reqString)))); + + QGeoRouteRequest updateRequest(route.request()); + updateRequest.setTravelModes(route.travelMode()); + QGeoRouteReplyNokia *reply = new QGeoRouteReplyNokia(updateRequest, replies, this); + + connect(reply, + SIGNAL(finished()), + this, + SLOT(routeFinished())); + + connect(reply, + SIGNAL(error(QGeoRouteReply::Error,QString)), + this, + SLOT(routeError(QGeoRouteReply::Error,QString))); + + return reply; +} + +bool QGeoRoutingManagerEngineNokia::checkEngineSupport(const QGeoRouteRequest &request, + QGeoRouteRequest::TravelModes travelModes) const +{ + QList featureTypeList = request.featureTypes(); + QGeoRouteRequest::FeatureTypes featureTypeFlag = QGeoRouteRequest::NoFeature; + QGeoRouteRequest::FeatureWeights featureWeightFlag = QGeoRouteRequest::NeutralFeatureWeight; + + for (int i = 0; i < featureTypeList.size(); ++i) { + featureTypeFlag |= featureTypeList.at(i); + featureWeightFlag |= request.featureWeight(featureTypeList.at(i)); + } + + if ((featureTypeFlag & supportedFeatureTypes()) != featureTypeFlag) + return false; + + if ((featureWeightFlag & supportedFeatureWeights()) != featureWeightFlag) + return false; + + + if ((request.maneuverDetail() & supportedManeuverDetails()) != request.maneuverDetail()) + return false; + + if ((request.segmentDetail() & supportedSegmentDetails()) != request.segmentDetail()) + return false; + + if ((request.routeOptimization() & supportedRouteOptimizations()) != request.routeOptimization()) + return false; + + if ((travelModes & supportedTravelModes()) != travelModes) + return false; + + // Count the number of set bits (= number of travel modes) (popcount) + int count = 0; + + for (unsigned bits = travelModes; bits; bits >>= 1) + count += (bits & 1); + + // We only allow one travel mode at a time + if (count != 1) + return false; + + return true; +} + +QStringList QGeoRoutingManagerEngineNokia::calculateRouteRequestString(const QGeoRouteRequest &request) +{ + bool supported = checkEngineSupport(request, request.travelModes()); + + if (!supported) + return QStringList(); + QStringList requests; + + QString baseRequest = QStringLiteral("http://"); + baseRequest += m_uriProvider->getCurrentHost(); + baseRequest += QStringLiteral("/routing/7.2/calculateroute.xml"); + + baseRequest += QStringLiteral("?alternatives="); + baseRequest += QString::number(request.numberAlternativeRoutes()); + + if (!m_appId.isEmpty() && !m_token.isEmpty()) { + baseRequest += QStringLiteral("&app_id="); + baseRequest += m_appId; + baseRequest += QStringLiteral("&token="); + baseRequest += m_token; + } + + const QList metadata = request.waypointsMetadata(); + const QList waypoints = request.waypoints(); + int numWaypoints = waypoints.size(); + if (numWaypoints < 2) + return QStringList(); + // Details: https://developer.here.com/documentation/routing/topics/resource-param-type-waypoint.html + for (int i = 0;i < numWaypoints;++i) { + const QGeoCoordinate &c = waypoints.at(i); + baseRequest += QStringLiteral("&waypoint"); + baseRequest += QString::number(i); + baseRequest += QStringLiteral("=geo!"); + baseRequest += trimDouble(c.latitude()); + baseRequest += ','; + baseRequest += trimDouble(c.longitude()); + baseRequest += QStringLiteral(";;"); // ;; + if (metadata.size() > i) { + const QVariantMap &meta = metadata.at(i); + if (meta.contains(QStringLiteral("bearing"))) { + qreal bearing = meta.value(QStringLiteral("bearing")).toDouble(); + baseRequest += ';' + QString::number(int(bearing)); + } + } + } + + QGeoRouteRequest::RouteOptimizations optimization = request.routeOptimization(); + + QStringList types; + if (optimization.testFlag(QGeoRouteRequest::ShortestRoute)) + types.append("shortest"); + if (optimization.testFlag(QGeoRouteRequest::FastestRoute)) + types.append("fastest"); + + foreach (const QString &optimization, types) { + QString requestString = baseRequest; + requestString += modesRequestString(request, request.travelModes(), optimization); + requestString += routeRequestString(request); + requests << requestString; + } + + return requests; +} + +QStringList QGeoRoutingManagerEngineNokia::updateRouteRequestString(const QGeoRoute &route, const QGeoCoordinate &position) +{ + if (!checkEngineSupport(route.request(), route.travelMode())) + return QStringList(); + QStringList requests; + + QString baseRequest = "http://"; + baseRequest += m_uriProvider->getCurrentHost(); + baseRequest += "/routing/7.2/getroute.xml"; + + baseRequest += "?routeid="; + baseRequest += route.routeId(); + + baseRequest += "&pos="; + baseRequest += QString::number(position.latitude()); + baseRequest += ','; + baseRequest += QString::number(position.longitude()); + + QGeoRouteRequest::RouteOptimizations optimization = route.request().routeOptimization(); + + QStringList types; + if (optimization.testFlag(QGeoRouteRequest::ShortestRoute)) + types.append("shortest"); + if (optimization.testFlag(QGeoRouteRequest::FastestRoute)) + types.append("fastest"); + + foreach (const QString &optimization, types) { + QString requestString = baseRequest; + requestString += modesRequestString(route.request(), route.travelMode(), optimization); + requestString += routeRequestString(route.request()); + requests << requestString; + } + + return requests; +} + +QString QGeoRoutingManagerEngineNokia::modesRequestString(const QGeoRouteRequest &request, + QGeoRouteRequest::TravelModes travelModes, const QString &optimization) const +{ + QString requestString; + + QStringList modes; + if (travelModes.testFlag(QGeoRouteRequest::CarTravel)) + modes.append("car"); + if (travelModes.testFlag(QGeoRouteRequest::PedestrianTravel)) + modes.append("pedestrian"); + if (travelModes.testFlag(QGeoRouteRequest::PublicTransitTravel)) + modes.append("publicTransport"); + + QStringList featureStrings; + QList featureTypes = request.featureTypes(); + for (int i = 0; i < featureTypes.size(); ++i) { + QGeoRouteRequest::FeatureWeight weight = request.featureWeight(featureTypes.at(i)); + + if (weight == QGeoRouteRequest::NeutralFeatureWeight) + continue; + + QString weightString = ""; + switch (weight) { + case QGeoRouteRequest::PreferFeatureWeight: + weightString = '1'; + break; + case QGeoRouteRequest::AvoidFeatureWeight: + weightString = "-1"; + break; + case QGeoRouteRequest::DisallowFeatureWeight: + weightString = "-3"; + break; + case QGeoRouteRequest::NeutralFeatureWeight: + case QGeoRouteRequest::RequireFeatureWeight: + break; + } + + if (weightString.isEmpty()) + continue; + + switch (featureTypes.at(i)) { + case QGeoRouteRequest::TollFeature: + featureStrings.append("tollroad:" + weightString); + break; + case QGeoRouteRequest::HighwayFeature: + featureStrings.append("motorway:" + weightString); + break; + case QGeoRouteRequest::FerryFeature: + featureStrings.append("boatFerry:" + weightString); + featureStrings.append("railFerry:" + weightString); + break; + case QGeoRouteRequest::TunnelFeature: + featureStrings.append("tunnel:" + weightString); + break; + case QGeoRouteRequest::DirtRoadFeature: + featureStrings.append("dirtRoad:" + weightString); + break; + case QGeoRouteRequest::PublicTransitFeature: + case QGeoRouteRequest::ParksFeature: + case QGeoRouteRequest::MotorPoolLaneFeature: + case QGeoRouteRequest::TrafficFeature: + case QGeoRouteRequest::NoFeature: + break; + } + } + + requestString += "&mode="; + requestString += optimization + ';' + modes.join(','); + if (featureStrings.count()) + requestString += ';' + featureStrings.join(','); + return requestString; +} + +QString QGeoRoutingManagerEngineNokia::routeRequestString(const QGeoRouteRequest &request) const +{ + QString requestString; + + foreach (const QGeoRectangle &area, request.excludeAreas()) { + requestString += QLatin1String("&avoidareas="); + requestString += trimDouble(area.topLeft().latitude()); + requestString += QLatin1String(","); + requestString += trimDouble(area.topLeft().longitude()); + requestString += QLatin1String(";"); + requestString += trimDouble(area.bottomRight().latitude()); + requestString += QLatin1String(","); + requestString += trimDouble(area.bottomRight().longitude()); + } + +// TODO: work out what was going on here +// - segment and instruction/maneuever functions are mixed and matched +// - tried to implement sensible equivalents below +// QStringList legAttributes; +// if (request.instructionDetail() & QGeoRouteRequest::BasicSegmentData) { +// requestString += "&linkattributes=sh,le"; //shape,length +// legAttributes.append("links"); +// } +// +// if (request.instructionDetail() & QGeoRouteRequest::BasicInstructions) { +// legAttributes.append("maneuvers"); +// requestString += "&maneuverattributes=po,tt,le,di"; //position,traveltime,length,direction +// if (!(request.instructionDetail() & QGeoRouteRequest::NoSegmentData)) +// requestString += ",li"; //link +// } + + QStringList legAttributes; + if (request.segmentDetail() & QGeoRouteRequest::BasicSegmentData) { + requestString += "&linkattributes=sh,le"; //shape,length + legAttributes.append("links"); + } + + if (request.maneuverDetail() & QGeoRouteRequest::BasicManeuvers) { + legAttributes.append("maneuvers"); + requestString += "&maneuverattributes=po,tt,le,di"; //position,traveltime,length,direction + if (!(request.segmentDetail() & QGeoRouteRequest::NoSegmentData)) + requestString += ",li"; //link + } + + requestString += "&routeattributes=sm,sh,bb,lg"; //summary,shape,boundingBox,legs + if (legAttributes.count() > 0) { + requestString += "&legattributes="; + requestString += legAttributes.join(","); + } + + requestString += "&departure="; + requestString += QDateTime::currentDateTime().toUTC().toString("yyyy-MM-ddThh:mm:ssZ"); + + requestString += "&instructionformat=text"; + + requestString += "&metricSystem="; + if (QLocale::MetricSystem == measurementSystem()) + requestString += "metric"; + else + requestString += "imperial"; + + const QLocale loc(locale()); + + if (QLocale::C != loc.language() && QLocale::AnyLanguage != loc.language()) { + requestString += "&language="; + requestString += loc.name(); + //If the first language isn't supported, english will be selected automatically + if (QLocale::English != loc.language()) + requestString += ",en_US"; + } + + return requestString; +} + +QString QGeoRoutingManagerEngineNokia::trimDouble(double degree, int decimalDigits) +{ + QString sDegree = QString::number(degree, 'g', decimalDigits); + + int index = sDegree.indexOf('.'); + + if (index == -1) + return sDegree; + else + return QString::number(degree, 'g', decimalDigits + index); +} + +void QGeoRoutingManagerEngineNokia::routeFinished() +{ + QGeoRouteReply *reply = qobject_cast(sender()); + + if (!reply) + return; + + if (receivers(SIGNAL(finished(QGeoRouteReply*))) == 0) { + reply->deleteLater(); + return; + } + + emit finished(reply); +} + +void QGeoRoutingManagerEngineNokia::routeError(QGeoRouteReply::Error error, const QString &errorString) +{ + QGeoRouteReply *reply = qobject_cast(sender()); + + if (!reply) + return; + + if (receivers(SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString))) == 0) { + reply->deleteLater(); + return; + } + + emit this->error(reply, error, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h b/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h new file mode 100644 index 0000000..9335bca --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoroutingmanagerengine_nokia.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGER_NOKIA_H +#define QGEOROUTINGMANAGER_NOKIA_H + +#include "qgeoserviceproviderplugin_nokia.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoNetworkAccessManager; +class QGeoUriProvider; + +class QGeoRoutingManagerEngineNokia : public QGeoRoutingManagerEngine +{ + Q_OBJECT +public: + QGeoRoutingManagerEngineNokia(QGeoNetworkAccessManager *networkInterface, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoRoutingManagerEngineNokia(); + + QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request); + QGeoRouteReply *updateRoute(const QGeoRoute &route, const QGeoCoordinate &position); + +private Q_SLOTS: + void routeFinished(); + void routeError(QGeoRouteReply::Error error, const QString &errorString); + +private: + QStringList calculateRouteRequestString(const QGeoRouteRequest &request); + QStringList updateRouteRequestString(const QGeoRoute &route, const QGeoCoordinate &position); + QString routeRequestString(const QGeoRouteRequest &request) const; + bool checkEngineSupport(const QGeoRouteRequest &request, + QGeoRouteRequest::TravelModes travelModes) const; + QString modesRequestString(const QGeoRouteRequest &request, + QGeoRouteRequest::TravelModes travelModes, + const QString &optimization) const; + static QString trimDouble(double degree, int decimalDigits = 10); + + QGeoNetworkAccessManager *m_networkManager; + QGeoUriProvider *m_uriProvider; + QString m_appId; + QString m_token; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp new file mode 100644 index 0000000..f68a0d9 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.cpp @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderplugin_nokia.h" + +#include "qgeocodingmanagerengine_nokia.h" +#include "qgeoroutingmanagerengine_nokia.h" +#include "qgeotiledmappingmanagerengine_nokia.h" +#include "qplacemanagerengine_nokiav2.h" +#include "qgeointrinsicnetworkaccessmanager.h" +#include "qgeoerror_messages.h" + +#include +#include +#include + +static void initResources() +{ + Q_INIT_RESOURCE(nokia); +} + +QT_BEGIN_NAMESPACE + +namespace +{ + bool isValidParameter(const QString ¶m) + { + if (param.isEmpty()) + return false; + + if (param.length() > 512) + return false; + + foreach (QChar c, param) { + if (!c.isLetterOrNumber() && c.toLatin1() != '%' && c.toLatin1() != '-' && + c.toLatin1() != '+' && c.toLatin1() != '_') { + return false; + } + } + return true; + } + + QGeoNetworkAccessManager *tryGetNetworkAccessManager(const QVariantMap ¶meters) + { + return static_cast(qvariant_cast(parameters.value(QStringLiteral("nam")))); + } + + void checkUsageTerms(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) + { + QString appId, token; + + appId = parameters.value(QStringLiteral("here.app_id")).toString(); + token = parameters.value(QStringLiteral("here.token")).toString(); + + if (isValidParameter(appId) && isValidParameter(token)) + return; + else if (!isValidParameter(appId)) + qWarning() << "Invalid here.app_id"; + else + qWarning() << "Invalid here.token"; + + if (parameters.contains(QStringLiteral("app_id")) || parameters.contains(QStringLiteral("token"))) + qWarning() << QStringLiteral("Please prefix 'app_id' and 'token' with prefix 'here' (e.g.: 'here.app_id')"); + + *error = QGeoServiceProvider::MissingRequiredParameterError; + *errorString = QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, MISSED_CREDENTIALS); + } + + template + TInstance * CreateInstanceOf(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) + { + checkUsageTerms(parameters, error, errorString); + + if (*error != QGeoServiceProvider::NoError) + return 0; + + QGeoNetworkAccessManager *networkManager = tryGetNetworkAccessManager(parameters); + if (!networkManager) + networkManager = new QGeoIntrinsicNetworkAccessManager(parameters); + + return new TInstance(networkManager, parameters, error, errorString); + } +} + +QGeoServiceProviderFactoryNokia::QGeoServiceProviderFactoryNokia() +{ + initResources(); +} + +QGeoCodingManagerEngine *QGeoServiceProviderFactoryNokia::createGeocodingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const +{ + return CreateInstanceOf(parameters, error, errorString); +} + +QGeoMappingManagerEngine *QGeoServiceProviderFactoryNokia::createMappingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const +{ + return CreateInstanceOf(parameters, error, errorString); +} + +QGeoRoutingManagerEngine *QGeoServiceProviderFactoryNokia::createRoutingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const +{ + return CreateInstanceOf(parameters, error, errorString); +} + +QPlaceManagerEngine *QGeoServiceProviderFactoryNokia::createPlaceManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const +{ + return CreateInstanceOf(parameters, error, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.h b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.h new file mode 100644 index 0000000..5ed2645 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeoserviceproviderplugin_nokia.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_NOKIA_H +#define QGEOSERVICEPROVIDER_NOKIA_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoNetworkAccessManager; + +class QGeoServiceProviderFactoryNokia : public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "nokia_plugin.json") + +public: + QGeoServiceProviderFactoryNokia(); + + QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp b/src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp new file mode 100644 index 0000000..722420d --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeotiledmap_nokia.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** + ** + ** Copyright (C) 2015 The Qt Company Ltd. + ** Contact: http://www.qt.io/licensing/ + ** + ** This file is part of the QtLocation module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL3$ + ** Commercial License Usage + ** Licensees holding valid commercial Qt licenses may use this file in + ** accordance with the commercial license agreement provided with the + ** Software or, alternatively, in accordance with the terms contained in + ** a written agreement between you and The Qt Company. For licensing terms + ** and conditions see http://www.qt.io/terms-conditions. For further + ** information use the contact form at http://www.qt.io/contact-us. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 3 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPLv3 included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 3 requirements + ** will be met: https://www.gnu.org/licenses/lgpl.html. + ** + ** GNU General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU + ** General Public License version 2.0 or later as published by the Free + ** Software Foundation and appearing in the file LICENSE.GPL included in + ** the packaging of this file. Please review the following information to + ** ensure the GNU General Public License version 2.0 requirements will be + ** met: http://www.gnu.org/licenses/gpl-2.0.html. + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "qgeotiledmap_nokia.h" +#include "qgeotiledmappingmanagerengine_nokia.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/* + Constructs a new tiled map data object, which stores the map data required by + \a geoMap and makes use of the functionality provided by \a engine. + */ +QGeoTiledMapNokia::QGeoTiledMapNokia(QGeoTiledMappingManagerEngineNokia *engine, QObject *parent /*= 0*/) : + Map(engine, parent), + m_logo(":/nokia/logo.png"), // HERE logo image + m_engine(engine) +{} + +QGeoTiledMapNokia::~QGeoTiledMapNokia() {} + +void QGeoTiledMapNokia::evaluateCopyrights(const QSet &visibleTiles) +{ + const int spaceToLogo = 4; + const int blurRate = 1; + const int fontSize = 10; + + if (m_engine.isNull()) + return; + + const QString copyrightsString = m_engine->evaluateCopyrightsText(activeMapType(), cameraData().zoomLevel(), visibleTiles); + + if (viewportWidth() > 0 && viewportHeight() > 0 && ((copyrightsString.isNull() && m_copyrightsSlab.isNull()) || copyrightsString != m_lastCopyrightsString)) { + QFont font("Sans Serif"); + font.setPixelSize(fontSize); + font.setStyleHint(QFont::SansSerif); + font.setWeight(QFont::Bold); + + QRect textBounds = QFontMetrics(font).boundingRect(0, 0, viewportWidth(), viewportHeight(), Qt::AlignBottom | Qt::AlignLeft | Qt::TextWordWrap, copyrightsString); + + m_copyrightsSlab = QImage(m_logo.width() + textBounds.width() + spaceToLogo + blurRate * 2, + qMax(m_logo.height(), textBounds.height() + blurRate * 2), + QImage::Format_ARGB32_Premultiplied); + m_copyrightsSlab.fill(Qt::transparent); + + QPainter painter(&m_copyrightsSlab); + painter.drawImage(QPoint(0, m_copyrightsSlab.height() - m_logo.height()), m_logo); + painter.setFont(font); + painter.setPen(QColor(0, 0, 0, 64)); + painter.translate(spaceToLogo + m_logo.width(), -blurRate); + for (int x=-blurRate; x<=blurRate; ++x) { + for (int y=-blurRate; y<=blurRate; ++y) { + painter.drawText(x, y, textBounds.width(), m_copyrightsSlab.height(), + Qt::AlignBottom | Qt::AlignLeft | Qt::TextWordWrap, + copyrightsString); + } + } + painter.setPen(Qt::white); + painter.drawText(0, 0, textBounds.width(), m_copyrightsSlab.height(), + Qt::AlignBottom | Qt::AlignLeft | Qt::TextWordWrap, + copyrightsString); + painter.end(); + + m_lastCopyrightsString = copyrightsString; + } + + emit copyrightsChanged(m_copyrightsSlab); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeotiledmap_nokia.h b/src/plugins/geoservices/nokia/qgeotiledmap_nokia.h new file mode 100644 index 0000000..487f6f4 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeotiledmap_nokia.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAP_NOKIA_H +#define QGEOMAP_NOKIA_H + +#include "qgeotiledmap_p.h" +#include +#include +#ifdef LOCATIONLABS +#include +typedef QGeoTiledMapLabs Map; +#else +typedef QGeoTiledMap Map; +#endif + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEngineNokia; + +class QGeoTiledMapNokia: public Map +{ +Q_OBJECT +public: + QGeoTiledMapNokia(QGeoTiledMappingManagerEngineNokia *engine, QObject *parent = 0); + ~QGeoTiledMapNokia(); + + QString getViewCopyright(); + void evaluateCopyrights(const QSet &visibleTiles); + +private: + QImage m_logo; + QImage m_copyrightsSlab; + QString m_lastCopyrightsString; + QPointer m_engine; + + Q_DISABLE_COPY(QGeoTiledMapNokia) +}; + +QT_END_NAMESPACE + +#endif // QGEOMAP_NOKIA_H diff --git a/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp new file mode 100644 index 0000000..e315c44 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.cpp @@ -0,0 +1,454 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qgeocameracapabilities_p.h" +#include "qgeotiledmappingmanagerengine_nokia.h" +#include "qgeotiledmap_nokia.h" +#include "qgeotilefetcher_nokia.h" +#include "qgeotilespec_p.h" +#include "qgeofiletilecachenokia.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoTiledMappingManagerEngineNokia::QGeoTiledMappingManagerEngineNokia( + QGeoNetworkAccessManager *networkManager, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) + : QGeoTiledMappingManagerEngine() +{ + Q_UNUSED(error); + Q_UNUSED(errorString); + + int ppi = 72; + if (parameters.contains(QStringLiteral("here.mapping.highdpi_tiles"))) { + const QString param = parameters.value(QStringLiteral("here.mapping.highdpi_tiles")).toString().toLower(); + if (param == "true") + ppi = 250; + } + + QGeoCameraCapabilities capabilities; + + capabilities.setMinimumZoomLevel(0.0); + capabilities.setMaximumZoomLevel(20.0); + if (ppi > 72) { + // Zoom levels 0 and 20 are not supported for 512x512 tiles. + capabilities.setMinimumZoomLevel(1.0); + capabilities.setMaximumZoomLevel(19.0); + } + capabilities.setSupportsBearing(true); + capabilities.setSupportsTilting(true); + capabilities.setMinimumTilt(0); + capabilities.setMaximumTilt(80); + capabilities.setMinimumFieldOfView(20.0); + capabilities.setMaximumFieldOfView(120.0); + capabilities.setOverzoomEnabled(true); + setCameraCapabilities(capabilities); + + setTileSize(QSize(256, 256)); + + int mapId = 0; + const QByteArray pluginName = "here"; + QList types; + types << QGeoMapType(QGeoMapType::StreetMap, tr("Street Map"), tr("Normal map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::TerrainMap, tr("Terrain Map"), tr("Terrain map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::HybridMap, tr("Hybrid Map"), tr("Satellite map view with streets in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::TransitMap, tr("Transit Map"), tr("Color-reduced map view with public transport scheme in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Gray Street Map"), tr("Color-reduced map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::StreetMap, tr("Mobile Street Map"), tr("Mobile normal map view in daylight mode"), true, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::TerrainMap, tr("Mobile Terrain Map"), tr("Mobile terrain map view in daylight mode"), true, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::HybridMap, tr("Mobile Hybrid Map"), tr("Mobile satellite map view with streets in daylight mode"), true, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::TransitMap, tr("Mobile Transit Map"), tr("Mobile color-reduced map view with public transport scheme in daylight mode"), true, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Mobile Gray Street Map"), tr("Mobile color-reduced map view in daylight mode"), true, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::StreetMap, tr("Custom Street Map"), tr("Normal map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::StreetMap, tr("Night Street Map"), tr("Normal map view in night mode"), false, true, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::StreetMap, tr("Mobile Night Street Map"), tr("Mobile normal map view in night mode"), true, true, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Gray Night Street Map"), tr("Color-reduced map view in night mode (especially used for background maps)"), false, true, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::GrayStreetMap, tr("Mobile Gray Night Street Map"), tr("Mobile color-reduced map view in night mode (especially used for background maps)"), true, true, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Pedestrian Street Map"), tr("Pedestrian map view in daylight mode"), false, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Mobile Pedestrian Street Map"), tr("Mobile pedestrian map view in daylight mode for mobile usage"), true, false, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Pedestrian Night Street Map"), tr("Pedestrian map view in night mode"), false, true, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::PedestrianMap, tr("Mobile Pedestrian Night Street Map"), tr("Mobile pedestrian map view in night mode for mobile usage"), true, true, ++mapId, pluginName, capabilities); + types << QGeoMapType(QGeoMapType::CarNavigationMap, tr("Car Navigation Map"), tr("Normal map view in daylight mode for car navigation"), false, false, ++mapId, pluginName, capabilities); + setSupportedMapTypes(types); + + QGeoTileFetcherNokia *fetcher = new QGeoTileFetcherNokia(parameters, networkManager, this, tileSize(), ppi); + setTileFetcher(fetcher); + + /* TILE CACHE */ + // TODO: do this in a plugin-neutral way so that other tiled map plugins + // don't need this boilerplate or hardcode plugin name + if (parameters.contains(QStringLiteral("here.mapping.cache.directory"))) { + m_cacheDirectory = parameters.value(QStringLiteral("here.mapping.cache.directory")).toString(); + } else { + // managerName() is not yet set, we have to hardcode the plugin name below + m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String(pluginName); + } + + QGeoFileTileCache *tileCache = new QGeoFileTileCacheNokia(ppi, m_cacheDirectory); + + /* + * Disk cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("here.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("here.mapping.cache.disk.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.disk.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxDiskUsage(cacheSize); + } + + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("here.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("here.mapping.cache.memory.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.memory.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxMemoryUsage(cacheSize); + } + + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("here.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("here.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("here.mapping.cache.texture.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("here.mapping.cache.texture.size")).toString().toInt(&ok); + if (ok) + tileCache->setExtraTextureUsage(cacheSize); + } + + /* PREFETCHING */ + if (parameters.contains(QStringLiteral("here.mapping.prefetching_style"))) { + const QString prefetchingMode = parameters.value(QStringLiteral("here.mapping.prefetching_style")).toString(); + if (prefetchingMode == QStringLiteral("TwoNeighbourLayers")) + m_prefetchStyle = QGeoTiledMap::PrefetchTwoNeighbourLayers; + else if (prefetchingMode == QStringLiteral("OneNeighbourLayer")) + m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer; + else if (prefetchingMode == QStringLiteral("NoPrefetching")) + m_prefetchStyle = QGeoTiledMap::NoPrefetching; + } + + setTileCache(tileCache); + populateMapSchemes(); + loadMapVersion(); + QMetaObject::invokeMethod(fetcher, "fetchCopyrightsData", Qt::QueuedConnection); + QMetaObject::invokeMethod(fetcher, "fetchVersionData", Qt::QueuedConnection); +} + +QGeoTiledMappingManagerEngineNokia::~QGeoTiledMappingManagerEngineNokia() +{ +} + +void QGeoTiledMappingManagerEngineNokia::populateMapSchemes() +{ + m_mapSchemes[0] = QStringLiteral("normal.day"); + m_mapSchemes[1] = QStringLiteral("normal.day"); + m_mapSchemes[2] = QStringLiteral("satellite.day"); + m_mapSchemes[3] = QStringLiteral("terrain.day"); + m_mapSchemes[4] = QStringLiteral("hybrid.day"); + m_mapSchemes[5] = QStringLiteral("normal.day.transit"); + m_mapSchemes[6] = QStringLiteral("normal.day.grey"); + m_mapSchemes[7] = QStringLiteral("normal.day.mobile"); + m_mapSchemes[8] = QStringLiteral("terrain.day.mobile"); + m_mapSchemes[9] = QStringLiteral("hybrid.day.mobile"); + m_mapSchemes[10] = QStringLiteral("normal.day.transit.mobile"); + m_mapSchemes[11] = QStringLiteral("normal.day.grey.mobile"); + m_mapSchemes[12] = QStringLiteral("normal.day.custom"); + m_mapSchemes[13] = QStringLiteral("normal.night"); + m_mapSchemes[14] = QStringLiteral("normal.night.mobile"); + m_mapSchemes[15] = QStringLiteral("normal.night.grey"); + m_mapSchemes[16] = QStringLiteral("normal.night.grey.mobile"); + m_mapSchemes[17] = QStringLiteral("pedestrian.day"); + m_mapSchemes[18] = QStringLiteral("pedestrian.day.mobile"); + m_mapSchemes[19] = QStringLiteral("pedestrian.night"); + m_mapSchemes[20] = QStringLiteral("pedestrian.night.mobile"); + m_mapSchemes[21] = QStringLiteral("carnav.day.grey"); +} + +QString QGeoTiledMappingManagerEngineNokia::getScheme(int mapId) +{ + return m_mapSchemes[mapId]; +} + +QString QGeoTiledMappingManagerEngineNokia::getBaseScheme(int mapId) +{ + QString fullScheme(m_mapSchemes[mapId]); + + return fullScheme.section(QLatin1Char('.'), 0, 0); +} + +int QGeoTiledMappingManagerEngineNokia::mapVersion() +{ + return m_mapVersion.version(); +} + +void QGeoTiledMappingManagerEngineNokia::loadCopyrightsDescriptorsFromJson(const QByteArray &jsonData) +{ + QJsonDocument doc = QJsonDocument::fromJson(QByteArray(jsonData)); + if (doc.isNull()) { + qDebug() << "QGeoTiledMappingManagerEngineNokia::loadCopyrightsDescriptorsFromJson() Invalid JSon document"; + return; + } + + QJsonObject jsonObj = doc.object(); + + m_copyrights.clear(); + for (auto it = jsonObj.constBegin(), end = jsonObj.constEnd(); it != end; ++it) { + QList copyrightDescList; + + QJsonArray descs = it.value().toArray(); + for (int descIndex = 0; descIndex < descs.count(); descIndex++) { + CopyrightDesc copyrightDesc; + QJsonObject desc = descs.at(descIndex).toObject(); + + copyrightDesc.minLevel = desc["minLevel"].toDouble(); + copyrightDesc.maxLevel = desc["maxLevel"].toDouble(); + copyrightDesc.label = desc["label"].toString(); + copyrightDesc.alt = desc["alt"].toString(); + + QJsonArray coordBoxes = desc["boxes"].toArray(); + for (int boxIndex = 0; boxIndex < coordBoxes.count(); boxIndex++) { + QJsonArray box = coordBoxes[boxIndex].toArray(); + qreal top = box[0].toDouble(); + qreal left = box[1].toDouble(); + qreal bottom = box[2].toDouble(); + qreal right = box[3].toDouble(); + QGeoRectangle boundingBox(QGeoCoordinate(top > bottom? top : bottom, + left), + QGeoCoordinate(top > bottom? bottom : top, + right)); + copyrightDesc.boxes << boundingBox; + } + copyrightDescList << copyrightDesc; + } + m_copyrights[it.key()] = copyrightDescList; + } +} + +void QGeoTiledMappingManagerEngineNokia::parseNewVersionInfo(const QByteArray &versionData) +{ + const QString versionString = QString::fromUtf8(versionData); + + const QStringList versionLines = versionString.split(QLatin1Char('\n')); + QJsonObject newVersionData; + foreach (const QString &line, versionLines) { + const QStringList versionInfo = line.split(':'); + if (versionInfo.size() > 1) { + const QString versionKey = versionInfo[0].trimmed(); + const QString versionValue = versionInfo[1].trimmed(); + if (!versionKey.isEmpty() && !versionValue.isEmpty()) { + newVersionData[versionKey] = versionValue; + } + } + } + + updateVersion(newVersionData); +} + +void QGeoTiledMappingManagerEngineNokia::updateVersion(const QJsonObject &newVersionData) { + + if (m_mapVersion.isNewVersion(newVersionData)) { + + m_mapVersion.setVersionData(newVersionData); + m_mapVersion.setVersion(m_mapVersion.version() + 1); + + saveMapVersion(); + setTileVersion(m_mapVersion.version()); + } +} + +void QGeoTiledMappingManagerEngineNokia::saveMapVersion() +{ + QDir saveDir(m_cacheDirectory); + QFile saveFile(saveDir.filePath(QStringLiteral("here_version"))); + + if (!saveFile.open(QIODevice::WriteOnly)) { + qWarning("Failed to write here/nokia map version."); + return; + } + + saveFile.write(m_mapVersion.toJson()); + saveFile.close(); +} + +void QGeoTiledMappingManagerEngineNokia::loadMapVersion() +{ + QDir saveDir(m_cacheDirectory); + QFile loadFile(saveDir.filePath(QStringLiteral("here_version"))); + + if (!loadFile.open(QIODevice::ReadOnly)) { + qWarning("Failed to read here/nokia map version."); + return; + } + + QByteArray saveData = loadFile.readAll(); + loadFile.close(); + + QJsonDocument doc(QJsonDocument::fromJson(saveData)); + + QJsonObject object = doc.object(); + + m_mapVersion.setVersion(object[QStringLiteral("version")].toInt()); + m_mapVersion.setVersionData(object[QStringLiteral("data")].toObject()); + setTileVersion(m_mapVersion.version()); +} + +QString QGeoTiledMappingManagerEngineNokia::evaluateCopyrightsText(const QGeoMapType mapType, + const qreal zoomLevel, + const QSet &tiles) +{ + static const QChar copyrightSymbol(0x00a9); + typedef QSet::const_iterator tile_iter; + QGeoRectangle viewport; + double viewX0, viewY0, viewX1, viewY1; + + tile_iter tile = tiles.constBegin(); + tile_iter lastTile = tiles.constEnd(); + + if (tiles.count()) { + double divFactor = qPow(2.0, tile->zoom()); + viewX0 = viewX1 = tile->x(); + viewY0 = viewY1 = tile->y(); + + // this approach establishes a geo-bounding box from passed tiles to test for intersecition + // with copyrights boxes. + int numTiles = 0; + for (; tile != lastTile; ++tile) { + if (tile->x() < viewX0) + viewX0 = tile->x(); + if (tile->x() > viewX1) + viewX1 = tile->x(); + if (tile->y() < viewY0) + viewY0 = tile->y(); + if (tile->y() > viewY1) + viewY1 = tile->y(); + numTiles++; + } + + viewX1++; + viewY1++; + + QDoubleVector2D pt; + + pt.setX(viewX0 / divFactor); + pt.setY(viewY0 / divFactor); + viewport.setTopLeft(QWebMercator::mercatorToCoord(pt)); + pt.setX(viewX1 / divFactor); + pt.setY(viewY1 / divFactor); + viewport.setBottomRight(QWebMercator::mercatorToCoord(pt)); + } + + // TODO: the following invalidation detection algorithm may be improved later. + QList descriptorList = m_copyrights[ getBaseScheme(mapType.mapId()) ]; + CopyrightDesc *descriptor; + int descIndex, boxIndex; + QString copyrightsText; + QSet copyrightStrings; + + for (descIndex = 0; descIndex < descriptorList.count(); descIndex++) { + if (descriptorList[descIndex].minLevel <= zoomLevel && zoomLevel <= descriptorList[descIndex].maxLevel) { + descriptor = &descriptorList[descIndex]; + + for (boxIndex = 0; boxIndex < descriptor->boxes.count(); boxIndex++) { + QGeoRectangle box = descriptor->boxes[boxIndex]; + + if (box.intersects(viewport)) { + copyrightStrings.insert(descriptor->label); + break; + } + } + if (!descriptor->boxes.count()) + copyrightStrings.insert(descriptor->label); + } + } + + foreach (const QString ©rightString, copyrightStrings) { + if (copyrightsText.length()) + copyrightsText += QLatin1Char('\n'); + copyrightsText += copyrightSymbol; + copyrightsText += copyrightString; + } + + return copyrightsText; +} + +QGeoMap *QGeoTiledMappingManagerEngineNokia::createMap() +{ + QGeoTiledMap *map = new QGeoTiledMapNokia(this); + map->setPrefetchStyle(m_prefetchStyle); + return map; +} + +QT_END_NAMESPACE + diff --git a/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.h b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.h new file mode 100644 index 0000000..1648312 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeotiledmappingmanagerengine_nokia.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPPINGMANAGERENGINE_NOKIA_H +#define QGEOTILEDMAPPINGMANAGERENGINE_NOKIA_H + +#include "qgeotiledmappingmanagerengine_p.h" +#include +#include "qgeomaptype_p.h" +#include "qgeomapversion.h" + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QByteArray; +class QGeoTileSpec; +class QGeoNetworkAccessManager; + +class QGeoTiledMappingManagerEngineNokia : public QGeoTiledMappingManagerEngine +{ + Q_OBJECT + +public: + QGeoTiledMappingManagerEngineNokia(QGeoNetworkAccessManager *networkManager, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoTiledMappingManagerEngineNokia(); + + virtual QGeoMap *createMap(); + QString evaluateCopyrightsText(const QGeoMapType mapType, + const qreal zoomLevel, + const QSet &tiles); + QString getScheme(int mapId); + QString getBaseScheme(int mapId); + int mapVersion(); + +public Q_SLOTS: + void loadCopyrightsDescriptorsFromJson(const QByteArray &jsonData); + void parseNewVersionInfo(const QByteArray &versionData); + +private: + class CopyrightDesc + { + public: + CopyrightDesc() + : maxLevel(-1), + minLevel(-1) {} + + qreal maxLevel; + qreal minLevel; + QList boxes; + QString alt; + QString label; + }; + + void initialize(); + void populateMapSchemes(); + void updateVersion(const QJsonObject &newVersionData); + void saveMapVersion(); + void loadMapVersion(); + + QHash > m_copyrights; + QHash m_mapSchemes; + QGeoMapVersion m_mapVersion; + + QString m_cacheDirectory; +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEDMAPPINGMANAGERENGINE_NOKIA_H diff --git a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp new file mode 100644 index 0000000..d07a93b --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.cpp @@ -0,0 +1,336 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotilefetcher_nokia.h" +#include "qgeomapreply_nokia.h" +#include "qgeotiledmap_nokia.h" +#include "qgeotiledmappingmanagerengine_nokia.h" +#include "qgeonetworkaccessmanager.h" +#include "qgeouriprovider.h" +#include "uri_constants.h" + +#include + +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace +{ + QString sizeToStr(int size) + { + if (size > 256) + return QStringLiteral("512"); + else if (size > 128) + return QStringLiteral("256"); + else + return QStringLiteral("128"); // 128 pixel tiles are deprecated. + } + + bool isAerialType(const QString mapScheme) + { + return mapScheme.startsWith("satellite") || mapScheme.startsWith("hybrid") || mapScheme.startsWith("terrain"); + } +} +QGeoTileFetcherNokia::QGeoTileFetcherNokia(const QVariantMap ¶meters, + QGeoNetworkAccessManager *networkManager, + QGeoTiledMappingManagerEngineNokia *engine, + const QSize &tileSize, + int ppi) +: QGeoTileFetcher(engine), m_engineNokia(engine), m_networkManager(networkManager), m_ppi(ppi), m_copyrightsReply(0), + m_baseUriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.mapping.host"), MAP_TILES_HOST)), + m_aerialUriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.mapping.host.aerial"), MAP_TILES_HOST_AERIAL)) +{ + Q_ASSERT(networkManager); + m_tileSize = qMax(tileSize.width(), tileSize.height()); + m_networkManager->setParent(this); + + m_applicationId = parameters.value(QStringLiteral("here.app_id")).toString(); + m_token = parameters.value(QStringLiteral("here.token")).toString(); +} + +QGeoTileFetcherNokia::~QGeoTileFetcherNokia() +{ +} + +QGeoTiledMapReply *QGeoTileFetcherNokia::getTileImage(const QGeoTileSpec &spec) +{ + // TODO add error detection for if request.connectivityMode() != QGraphicsGeoMap::OnlineMode + int ppi = m_ppi; + if ((spec.mapId() == 2) || (spec.mapId() == 12) || (spec.mapId() == 21)) { + ppi = 72; // HiDpi apparently not supported for these maps + } else if ((spec.mapId() >= 7 && spec.mapId() <= 11) + || (spec.mapId() == 14) + || (spec.mapId() == 16) + || (spec.mapId() == 18) + || (spec.mapId() == 20)) { + ppi = 250; // LoDpi apparently not supported for these maps + } + + QString rawRequest = getRequestString(spec, ppi); + if (rawRequest.isEmpty()) { + return new QGeoTiledMapReply(QGeoTiledMapReply::UnknownError, + tr("Mapping manager no longer exists"), this); + } + + QNetworkRequest netRequest((QUrl(rawRequest))); // The extra pair of parens disambiguates this from a function declaration + netRequest.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); + + QNetworkReply *netReply = m_networkManager->get(netRequest); + + QGeoTiledMapReply *mapReply = new QGeoMapReplyNokia(netReply, spec); + + return mapReply; +} + +QString QGeoTileFetcherNokia::getRequestString(const QGeoTileSpec &spec, int ppi) +{ + if (!m_engineNokia) + return QString(); + + static const QString http("http://"); + static const QString path("/maptile/2.1/maptile/newest/"); + static const QChar slash('/'); + + QString requestString = http; + + const QString mapScheme = m_engineNokia->getScheme(spec.mapId()); + if (isAerialType(mapScheme)) + requestString += m_aerialUriProvider->getCurrentHost(); + else + requestString += m_baseUriProvider->getCurrentHost(); + + requestString += path; + requestString += mapScheme; + requestString += slash; + requestString += QString::number(spec.zoom()); + requestString += slash; + requestString += QString::number(spec.x()); + requestString += slash; + requestString += QString::number(spec.y()); + requestString += slash; + requestString += ((ppi > 72)) ? sizeToStr(m_tileSize * 2) : sizeToStr(m_tileSize); + static const QString slashpng("/png8"); + requestString += slashpng; + + if (!m_token.isEmpty() && !m_applicationId.isEmpty()) { // TODO: remove the if + requestString += "?token="; + requestString += m_token; + + requestString += "&app_id="; + requestString += m_applicationId; + } + + requestString += "&ppi=" + QString::number(ppi); + + requestString += "&lg="; + requestString += getLanguageString(); + return requestString; +} + +QString QGeoTileFetcherNokia::getLanguageString() const +{ + if (!m_engineNokia) + return QStringLiteral("ENG"); + + QLocale locale = m_engineNokia.data()->locale(); + + // English is the default, where no ln is specified. We hardcode the languages + // here even though the entire list is updated automagically from the server. + // The current languages are Arabic, Chinese, Simplified Chinese, English + // French, German, Italian, Polish, Russian and Spanish. The default is English. + // These are actually available from the same host under the URL: /maptiler/v2/info + + switch (locale.language()) { + case QLocale::Arabic: + return QStringLiteral("ARA"); + case QLocale::Chinese: + if (locale.script() == QLocale::TraditionalChineseScript) + return QStringLiteral("CHI"); + else + return QStringLiteral("CHT"); + case QLocale::Dutch: + return QStringLiteral("DUT"); + case QLocale::French: + return QStringLiteral("FRE"); + case QLocale::German: + return QStringLiteral("GER"); + case QLocale::Gaelic: + return QStringLiteral("GLE"); + case QLocale::Greek: + return QStringLiteral("GRE"); + case QLocale::Hebrew: + return QStringLiteral("HEB"); + case QLocale::Hindi: + return QStringLiteral("HIN"); + case QLocale::Indonesian: + return QStringLiteral("IND"); + case QLocale::Italian: + return QStringLiteral("ITA"); + case QLocale::Persian: + return QStringLiteral("PER"); + case QLocale::Polish: + return QStringLiteral("POL"); + case QLocale::Portuguese: + return QStringLiteral("POR"); + case QLocale::Russian: + return QStringLiteral("RUS"); + case QLocale::Sinhala: + return QStringLiteral("SIN"); + case QLocale::Spanish: + return QStringLiteral("SPA"); + case QLocale::Thai: + return QStringLiteral("THA"); + case QLocale::Turkish: + return QStringLiteral("TUR"); + case QLocale::Ukrainian: + return QStringLiteral("UKR"); + case QLocale::Urdu: + return QStringLiteral("URD"); + case QLocale::Vietnamese: + return QStringLiteral("VIE"); + + default: + return QStringLiteral("ENG"); + } + // No "lg" param means that we want English. +} + +QString QGeoTileFetcherNokia::token() const +{ + return m_token; +} + +QString QGeoTileFetcherNokia::applicationId() const +{ + return m_applicationId; +} + +void QGeoTileFetcherNokia::copyrightsFetched() +{ + if (m_engineNokia && m_copyrightsReply->error() == QNetworkReply::NoError) { + QMetaObject::invokeMethod(m_engineNokia.data(), + "loadCopyrightsDescriptorsFromJson", + Qt::QueuedConnection, + Q_ARG(QByteArray, m_copyrightsReply->readAll())); + } + + m_copyrightsReply->deleteLater(); +} + +void QGeoTileFetcherNokia::versionFetched() +{ + if (m_engineNokia && m_versionReply->error() == QNetworkReply::NoError) { + QMetaObject::invokeMethod(m_engineNokia.data(), + "parseNewVersionInfo", + Qt::QueuedConnection, + Q_ARG(QByteArray, m_versionReply->readAll())); + } + + m_versionReply->deleteLater(); +} + +void QGeoTileFetcherNokia::fetchCopyrightsData() +{ + QString copyrightUrl = QStringLiteral("http://"); + + copyrightUrl += m_baseUriProvider->getCurrentHost(); + copyrightUrl += QStringLiteral("/maptile/2.1/copyright/newest?output=json"); + + if (!token().isEmpty()) { + copyrightUrl += QStringLiteral("&token="); + copyrightUrl += token(); + } + + if (!applicationId().isEmpty()) { + copyrightUrl += QStringLiteral("&app_id="); + copyrightUrl += applicationId(); + } + + QNetworkRequest netRequest((QUrl(copyrightUrl))); + m_copyrightsReply = m_networkManager->get(netRequest); + if (m_copyrightsReply->error() != QNetworkReply::NoError) { + qWarning() << __FUNCTION__ << m_copyrightsReply->errorString(); + m_copyrightsReply->deleteLater(); + return; + } + + if (m_copyrightsReply->isFinished()) { + copyrightsFetched(); + } else { + connect(m_copyrightsReply, SIGNAL(finished()), this, SLOT(copyrightsFetched())); + } +} + +void QGeoTileFetcherNokia::fetchVersionData() +{ + QString versionUrl = QStringLiteral("http://"); + + versionUrl += m_baseUriProvider->getCurrentHost(); + versionUrl += QStringLiteral("/maptile/2.1/version"); + + if (!token().isEmpty()) { + versionUrl += QStringLiteral("?token="); + versionUrl += token(); + } + + if (!applicationId().isEmpty()) { + versionUrl += QStringLiteral("&app_id="); + versionUrl += applicationId(); + } + + QNetworkRequest netRequest((QUrl(versionUrl))); + m_versionReply = m_networkManager->get(netRequest); + + if (m_versionReply->error() != QNetworkReply::NoError) { + qWarning() << __FUNCTION__ << m_versionReply->errorString(); + m_versionReply->deleteLater(); + return; + } + + if (m_versionReply->isFinished()) + versionFetched(); + else + connect(m_versionReply, SIGNAL(finished()), this, SLOT(versionFetched())); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h new file mode 100644 index 0000000..06d1bba --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeotilefetcher_nokia.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEFETCHER_NOKIA_H +#define QGEOTILEFETCHER_NOKIA_H + +#include "qgeoserviceproviderplugin_nokia.h" + +#include + +QT_BEGIN_NAMESPACE + +class QGeoTiledMapReply; +class QGeoTileSpec; +class QGeoTiledMappingManagerEngine; +class QGeoTiledMappingManagerEngineNokia; +class QNetworkReply; +class QGeoNetworkAccessManager; +class QGeoUriProvider; + +class QGeoTileFetcherNokia : public QGeoTileFetcher +{ + Q_OBJECT + +public: + QGeoTileFetcherNokia(const QVariantMap ¶meters, QGeoNetworkAccessManager *networkManager, + QGeoTiledMappingManagerEngineNokia *engine, const QSize &tileSize, int ppi); + ~QGeoTileFetcherNokia(); + + QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec); + + QString token() const; + QString applicationId() const; + +public Q_SLOTS: + void copyrightsFetched(); + void fetchCopyrightsData(); + void versionFetched(); + void fetchVersionData(); + +private: + Q_DISABLE_COPY(QGeoTileFetcherNokia) + + QString getRequestString(const QGeoTileSpec &spec, int ppi=72); + + QString getLanguageString() const; + + QPointer m_engineNokia; + QGeoNetworkAccessManager *m_networkManager; + int m_tileSize; + int m_ppi; + QString m_token; + QNetworkReply *m_copyrightsReply; + QNetworkReply *m_versionReply; + + QString m_applicationId; + QGeoUriProvider *m_baseUriProvider; + QGeoUriProvider *m_aerialUriProvider; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/nokia/qgeouriprovider.cpp b/src/plugins/geoservices/nokia/qgeouriprovider.cpp new file mode 100644 index 0000000..cef3d48 --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeouriprovider.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeouriprovider.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoUriProvider::QGeoUriProvider( + QObject *parent, + const QVariantMap ¶meters, + const QString &hostParameterName, + const QString &internationalHost, + const QString &localizedHost) + : QObject(parent) + , m_internationalHost(parameters.value(hostParameterName, internationalHost).toString()) + , m_localizedHost(localizedHost) + , m_firstSubdomain(QChar::Null) + , m_maxSubdomains(0) +{ + setCurrentHost(isInternationalNetwork() || m_localizedHost.isEmpty() ? m_internationalHost : m_localizedHost); +} + +QString QGeoUriProvider::getCurrentHost() const +{ + if (m_maxSubdomains) { + QString result(m_firstSubdomain.toLatin1() + QRandomGenerator::global()->bounded(m_maxSubdomains)); + result += '.' + m_currentHost; + return result; + } + return m_currentHost; +} + +void QGeoUriProvider::setCurrentHost(const QString &host) +{ + if (host.length() > 4 && host.at(1) == QChar('-') && host.at(3) == QChar('.')) { + QString realHost = host.right(host.length() - 4); + m_firstSubdomain = host.at(0); + m_maxSubdomains = host.at(2).toLatin1() - host.at(0).toLatin1() + 1; + m_currentHost = realHost; + } else { + m_currentHost = host; + m_firstSubdomain = QChar::Null; + m_maxSubdomains = 0; + } +} + +void QGeoUriProvider::mobileCountryCodeChanged(int interfaceId, const QString& mcc) +{ + Q_UNUSED(interfaceId) + Q_UNUSED(mcc) + + setCurrentHost(isInternationalNetwork() || m_localizedHost.isEmpty() ? m_internationalHost : m_localizedHost); +} + +bool QGeoUriProvider::isInternationalNetwork() const +{ + return true; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qgeouriprovider.h b/src/plugins/geoservices/nokia/qgeouriprovider.h new file mode 100644 index 0000000..d4d178a --- /dev/null +++ b/src/plugins/geoservices/nokia/qgeouriprovider.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEO_MOBILE_COUNTRY_TRACKER_H +#define QGEO_MOBILE_COUNTRY_TRACKER_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkInfo; + +class QGeoUriProvider : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(QGeoUriProvider) + +public: + QGeoUriProvider(QObject *parent, + const QVariantMap ¶meters, + const QString &hostParameterName, + const QString &internationalHost, + const QString &localizedHost = QString()); + + QString getCurrentHost() const; + +private Q_SLOTS: + void mobileCountryCodeChanged(int interfaceId, const QString& mcc); + +private: + bool isInternationalNetwork() const; + void setCurrentHost(const QString &host); + + const QString m_internationalHost; + const QString m_localizedHost; + QString m_currentHost; + QChar m_firstSubdomain; + unsigned char m_maxSubdomains; +}; + +QT_END_NAMESPACE + +#endif // QGEO_MOBILE_COUNTRY_TRACKER_H diff --git a/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp new file mode 100644 index 0000000..ab57546 --- /dev/null +++ b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.cpp @@ -0,0 +1,864 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacemanagerengine_nokiav2.h" + +#include "placesv2/qplacecategoriesreplyhere.h" +#include "placesv2/qplacecontentreplyimpl.h" +#include "placesv2/qplacesearchsuggestionreplyimpl.h" +#include "placesv2/qplacesearchreplyhere.h" +#include "placesv2/qplacedetailsreplyimpl.h" +#include "placesv2/qplaceidreplyimpl.h" +#include "qgeonetworkaccessmanager.h" +#include "qgeouriprovider.h" +#include "uri_constants.h" +#include "qgeoerror_messages.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +static const char FIXED_CATEGORIES_string[] = + "eat-drink\0" + "going-out\0" + "sights-museums\0" + "transport\0" + "accommodation\0" + "shopping\0" + "leisure-outdoor\0" + "administrative-areas-buildings\0" + "natural-geographical\0" + "petrol-station\0" + "atm-bank-exchange\0" + "toilet-rest-area\0" + "hospital-health-care-facility\0" + "eat-drink|restaurant\0" // subcategories always start after relative parent category + "eat-drink|coffee-tea\0" + "eat-drink|snacks-fast-food\0" + "transport|airport" + "\0"; + +static const int FIXED_CATEGORIES_indices[] = { + 0, 10, 20, 35, 45, 59, 68, 84, + 115, 136, 151, 169, 186, 216, 237, 258, + 285, -1 +}; + +static const char * const NokiaIcon = "nokiaIcon"; +static const char * const IconPrefix = "iconPrefix"; +static const char * const NokiaIconGenerated = "nokiaIconGenerated"; + +static const char * const IconThemeKey = "places.icons.theme"; +static const char * const LocalDataPathKey = "places.local_data_path"; + +class CategoryParser +{ +public: + CategoryParser(); + bool parse(const QString &fileName); + + QPlaceCategoryTree tree() const { return m_tree; } + QHash restIdToIconHash() const { return m_restIdToIconHash; } + + QString errorString() const; + +private: + void processCategory(int level, const QString &id, + const QString &parentId = QString()); + + QJsonObject m_exploreObject; + QPlaceCategoryTree m_tree; + QString m_errorString; + + QHash m_restIdToIconHash; +}; + +CategoryParser::CategoryParser() +{ +} + +bool CategoryParser::parse(const QString &fileName) +{ + m_exploreObject = QJsonObject(); + m_tree.clear(); + m_errorString.clear(); + + QFile mappingFile(fileName); + + if (mappingFile.open(QIODevice::ReadOnly)) { + QJsonDocument document = QJsonDocument::fromJson(mappingFile.readAll()); + if (document.isObject()) { + QJsonObject docObject = document.object(); + if (docObject.contains(QStringLiteral("offline_explore"))) { + m_exploreObject = docObject.value(QStringLiteral("offline_explore")) + .toObject(); + if (m_exploreObject.contains(QStringLiteral("ROOT"))) { + processCategory(0, QString()); + return true; + } + } else { + m_errorString = fileName + + QStringLiteral("does not contain the offline_explore property"); + return false; + } + } else { + m_errorString = fileName + QStringLiteral("is not an json object"); + return false; + } + } + m_errorString = QString::fromLatin1("Unable to open ") + fileName; + return false; +} + +void CategoryParser::processCategory(int level, const QString &id, const QString &parentId) +{ + //We are basing the tree on a DAG from the input file, however we are simplyfing + //this into a 2 level tree, and a given category only has one parent + // + // A->B->Z + // A->C->Z + // Z in this case is not in the tree because it is 3 levels deep. + // + // X->Z + // Y->Z + // Only one of these is shown in the tree since Z can only have one parent + // the choice made between X and Y is arbitrary. + const int maxLevel = 2; + PlaceCategoryNode node; + node.category.setCategoryId(id); + node.parentId = parentId; + + m_tree.insert(node.category.categoryId(), node); + //this is simply to mark the node as being visited. + //a proper assignment to the tree happens at the end of function + + QJsonObject categoryJson = m_exploreObject.value(id.isEmpty() + ? QStringLiteral("ROOT") : id).toObject(); + QJsonArray children = categoryJson.value(QStringLiteral("children")).toArray(); + + if (level + 1 <= maxLevel && !categoryJson.contains(QStringLiteral("final"))) { + for (int i = 0; i < children.count(); ++i) { + QString childId = children.at(i).toString(); + if (!m_tree.contains(childId)) { + node.childIds.append(childId); + processCategory(level + 1, childId, id); + } + } + } + + m_tree.insert(node.category.categoryId(), node); +} + +QPlaceManagerEngineNokiaV2::QPlaceManagerEngineNokiaV2( + QGeoNetworkAccessManager *networkManager, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) + : QPlaceManagerEngine(parameters) + , m_manager(networkManager) + , m_uriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.places.host"), PLACES_HOST)) +{ + Q_ASSERT(networkManager); + m_manager->setParent(this); + + m_locales.append(QLocale()); + + m_appId = parameters.value(QStringLiteral("here.app_id")).toString(); + m_appCode = parameters.value(QStringLiteral("here.token")).toString(); + + m_theme = parameters.value(IconThemeKey, QString()).toString(); + + if (m_theme == QStringLiteral("default")) + m_theme.clear(); + + m_localDataPath = parameters.value(LocalDataPathKey, QString()).toString(); + if (m_localDataPath.isEmpty()) { + QStringList dataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + + if (!dataLocations.isEmpty() && !dataLocations.first().isEmpty()) { + m_localDataPath = dataLocations.first() + + QStringLiteral("/here/qtlocation/data"); + } + } + + if (error) + *error = QGeoServiceProvider::NoError; + + if (errorString) + errorString->clear(); +} + +QPlaceManagerEngineNokiaV2::~QPlaceManagerEngineNokiaV2() {} + +QPlaceDetailsReply *QPlaceManagerEngineNokiaV2::getPlaceDetails(const QString &placeId) +{ + QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/places/") + placeId); + + QUrlQuery queryItems; + + queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html")); + //queryItems.append(qMakePair(QStringLiteral("size"), QString::number(5))); + //queryItems.append(qMakePair(QStringLiteral("image_dimensions"), QStringLiteral("w64-h64,w100"))); + + requestUrl.setQuery(queryItems); + + QNetworkReply *networkReply = sendRequest(requestUrl); + + QPlaceDetailsReplyImpl *reply = new QPlaceDetailsReplyImpl(networkReply, this); + reply->setPlaceId(placeId); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + return reply; +} + +QPlaceContentReply *QPlaceManagerEngineNokiaV2::getPlaceContent(const QPlaceContentRequest &request) +{ + QNetworkReply *networkReply = 0; + + if (request.contentContext().userType() == qMetaTypeId()) { + QUrl u = request.contentContext().value(); + + networkReply = sendRequest(u); + } else { + QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/places/") + request.placeId() + + QStringLiteral("/media/")); + + QUrlQuery queryItems; + + switch (request.contentType()) { + case QPlaceContent::ImageType: + requestUrl.setPath(requestUrl.path() + QStringLiteral("images")); + + queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html")); + + if (request.limit() > 0) + queryItems.addQueryItem(QStringLiteral("size"), QString::number(request.limit())); + + //queryItems.append(qMakePair(QStringLiteral("image_dimensions"), QStringLiteral("w64-h64,w100"))); + + requestUrl.setQuery(queryItems); + + networkReply = sendRequest(requestUrl); + break; + case QPlaceContent::ReviewType: + requestUrl.setPath(requestUrl.path() + QStringLiteral("reviews")); + + queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html")); + + if (request.limit() > 0) + queryItems.addQueryItem(QStringLiteral("size"), QString::number(request.limit())); + + requestUrl.setQuery(queryItems); + + networkReply = sendRequest(requestUrl); + break; + case QPlaceContent::EditorialType: + requestUrl.setPath(requestUrl.path() + QStringLiteral("editorials")); + + queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html")); + + if (request.limit() > 0) + queryItems.addQueryItem(QStringLiteral("size"), QString::number(request.limit())); + + requestUrl.setQuery(queryItems); + + networkReply = sendRequest(requestUrl); + break; + case QPlaceContent::NoType: + default: + ; + } + } + + QPlaceContentReply *reply = new QPlaceContentReplyImpl(request, networkReply, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + if (!networkReply) { + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::UnsupportedError), + Q_ARG(QString, QString("Retrieval of given content type not supported."))); + } + + return reply; +} + +static bool addAtForBoundingArea(const QGeoShape &area, + QUrlQuery *queryItems) +{ + QGeoCoordinate center = area.center(); + if (!center.isValid()) + return false; + + queryItems->addQueryItem(QStringLiteral("at"), + QString::number(center.latitude()) + + QLatin1Char(',') + + QString::number(center.longitude())); + return true; +} + +QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest &query) +{ + bool unsupported = false; + + unsupported |= query.visibilityScope() != QLocation::UnspecifiedVisibility && + query.visibilityScope() != QLocation::PublicVisibility; + + // Both a search term and search categories are not supported. + unsupported |= !query.searchTerm().isEmpty() && !query.categories().isEmpty(); + + //only a recommendation id by itself is supported. + unsupported |= !query.recommendationId().isEmpty() + && (!query.searchTerm().isEmpty() || !query.categories().isEmpty() + || query.searchArea().type() != QGeoShape::UnknownType); + + if (unsupported) { + QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, 0, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError), + Q_ARG(QString, "Unsupported search request options specified.")); + return reply; + } + + QUrlQuery queryItems; + + // Check that the search area is valid for all searches except recommendation and proposed + // searches, which do not need search centers. + if (query.recommendationId().isEmpty() && !query.searchContext().isValid()) { + if (!addAtForBoundingArea(query.searchArea(), &queryItems)) { + QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, 0, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError), + Q_ARG(QString, "Invalid search area provided")); + return reply; + } + } + + QNetworkReply *networkReply = 0; + + if (query.searchContext().userType() == qMetaTypeId()) { + // provided search context + QUrl u = query.searchContext().value(); + + typedef QPair QueryItem; + QList queryItemList = queryItems.queryItems(QUrl::FullyEncoded); + queryItems = QUrlQuery(u); + foreach (const QueryItem &item, queryItemList) + queryItems.addQueryItem(item.first, item.second); + + if (query.limit() > 0) + queryItems.addQueryItem(QStringLiteral("size"), QString::number(query.limit())); + + u.setQuery(queryItems); + + networkReply = sendRequest(u); + } else if (!query.searchTerm().isEmpty()) { + // search term query + QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/discover/search")); + + queryItems.addQueryItem(QStringLiteral("q"), query.searchTerm()); + queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html")); + + if (query.limit() > 0) { + queryItems.addQueryItem(QStringLiteral("size"), + QString::number(query.limit())); + } + + requestUrl.setQuery(queryItems); + + QNetworkReply *networkReply = sendRequest(requestUrl); + + QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, networkReply, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + return reply; + } else if (!query.recommendationId().isEmpty()) { + QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/places/") + query.recommendationId() + + QStringLiteral("/related/recommended")); + + queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html")); + + requestUrl.setQuery(queryItems); + + networkReply = sendRequest(requestUrl); + } else { + // category search + QUrl requestUrl(QStringLiteral("http://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/discover/explore")); + + QStringList ids; + foreach (const QPlaceCategory &category, query.categories()) + ids.append(category.categoryId()); + + QUrlQuery queryItems; + + if (!ids.isEmpty()) + queryItems.addQueryItem(QStringLiteral("cat"), ids.join(QStringLiteral(","))); + + addAtForBoundingArea(query.searchArea(), &queryItems); + + queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html")); + + if (query.limit() > 0) { + queryItems.addQueryItem(QStringLiteral("size"), + QString::number(query.limit())); + } + + requestUrl.setQuery(queryItems); + + networkReply = sendRequest(requestUrl); + } + + QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, networkReply, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + return reply; +} + +QPlaceSearchSuggestionReply *QPlaceManagerEngineNokiaV2::searchSuggestions(const QPlaceSearchRequest &query) +{ + bool unsupported = false; + + unsupported |= query.visibilityScope() != QLocation::UnspecifiedVisibility && + query.visibilityScope() != QLocation::PublicVisibility; + + unsupported |= !query.categories().isEmpty(); + unsupported |= !query.recommendationId().isEmpty(); + + if (unsupported) { + QPlaceSearchSuggestionReplyImpl *reply = new QPlaceSearchSuggestionReplyImpl(0, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError), + Q_ARG(QString, "Unsupported search request options specified.")); + return reply; + } + + QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/suggest")); + + QUrlQuery queryItems; + + queryItems.addQueryItem(QStringLiteral("q"), query.searchTerm()); + + if (!addAtForBoundingArea(query.searchArea(), &queryItems)) { + QPlaceSearchSuggestionReplyImpl *reply = new QPlaceSearchSuggestionReplyImpl(0, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError), + Q_ARG(QString, "Invalid search area provided")); + return reply; + } + + requestUrl.setQuery(queryItems); + + QNetworkReply *networkReply = sendRequest(requestUrl); + + QPlaceSearchSuggestionReplyImpl *reply = new QPlaceSearchSuggestionReplyImpl(networkReply, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + return reply; +} + +QPlaceIdReply *QPlaceManagerEngineNokiaV2::savePlace(const QPlace &place) +{ + QPlaceIdReplyImpl *reply = new QPlaceIdReplyImpl(QPlaceIdReply::SavePlace, this); + reply->setId(place.placeId()); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::UnsupportedError), + Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, SAVING_PLACE_NOT_SUPPORTED))); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + return reply; +} + +QPlaceIdReply *QPlaceManagerEngineNokiaV2::removePlace(const QString &placeId) +{ + QPlaceIdReplyImpl *reply = new QPlaceIdReplyImpl(QPlaceIdReply::RemovePlace, this); + reply->setId(placeId); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::UnsupportedError), + Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, REMOVING_PLACE_NOT_SUPPORTED))); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + return reply; +} + +QPlaceIdReply *QPlaceManagerEngineNokiaV2::saveCategory(const QPlaceCategory &category, const QString &parentId) +{ + Q_UNUSED(parentId) + + QPlaceIdReplyImpl *reply = new QPlaceIdReplyImpl(QPlaceIdReply::SaveCategory, this); + reply->setId(category.categoryId()); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::UnsupportedError), + Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, SAVING_CATEGORY_NOT_SUPPORTED))); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + return reply; +} + +QPlaceIdReply *QPlaceManagerEngineNokiaV2::removeCategory(const QString &categoryId) +{ + QPlaceIdReplyImpl *reply = new QPlaceIdReplyImpl(QPlaceIdReply::RemoveCategory, this); + reply->setId(categoryId); + QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::UnsupportedError), + Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, REMOVING_CATEGORY_NOT_SUPPORTED))); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + return reply; +} + +QPlaceReply *QPlaceManagerEngineNokiaV2::initializeCategories() +{ + if (m_categoryReply) + return m_categoryReply.data(); + + m_tempTree.clear(); + CategoryParser parser; + + if (parser.parse(m_localDataPath + QStringLiteral("/offline/offline-mapping.json"))) { + m_tempTree = parser.tree(); + } else { + PlaceCategoryNode rootNode; + + for (int i = 0; FIXED_CATEGORIES_indices[i] != -1; ++i) { + const QString id = QString::fromLatin1(FIXED_CATEGORIES_string + + FIXED_CATEGORIES_indices[i]); + + int subCatDivider = id.indexOf(QChar('|')); + if (subCatDivider >= 0) { + // found a sub category + const QString subCategoryId = id.mid(subCatDivider+1); + const QString parentCategoryId = id.left(subCatDivider); + + if (m_tempTree.contains(parentCategoryId)) { + PlaceCategoryNode node; + node.category.setCategoryId(subCategoryId); + node.parentId = parentCategoryId; + + // find parent + PlaceCategoryNode &parent = m_tempTree[parentCategoryId]; + parent.childIds.append(subCategoryId); + m_tempTree.insert(subCategoryId, node); + } + + } else { + PlaceCategoryNode node; + node.category.setCategoryId(id); + + m_tempTree.insert(id, node); + rootNode.childIds.append(id); + } + } + + m_tempTree.insert(QString(), rootNode); + } + + //request all categories in the tree from the server + //because we don't want the root node, we skip it + for (auto it = m_tempTree.keyBegin(), end = m_tempTree.keyEnd(); it != end; ++it) { + if (*it == QString()) + continue; + QUrl requestUrl(QString::fromLatin1("http://") + m_uriProvider->getCurrentHost() + + QStringLiteral("/places/v1/categories/places/") + *it); + QNetworkReply *networkReply = sendRequest(requestUrl); + connect(networkReply, SIGNAL(finished()), this, SLOT(categoryReplyFinished())); + connect(networkReply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(categoryReplyError())); + + m_categoryRequests.insert(*it, networkReply); + } + + QPlaceCategoriesReplyHere *reply = new QPlaceCategoriesReplyHere(this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + m_categoryReply = reply; + return reply; +} + +QString QPlaceManagerEngineNokiaV2::parentCategoryId(const QString &categoryId) const +{ + return m_categoryTree.value(categoryId).parentId; +} + +QStringList QPlaceManagerEngineNokiaV2::childCategoryIds(const QString &categoryId) const +{ + return m_categoryTree.value(categoryId).childIds; +} + +QPlaceCategory QPlaceManagerEngineNokiaV2::category(const QString &categoryId) const +{ + return m_categoryTree.value(categoryId).category; +} + +QList QPlaceManagerEngineNokiaV2::childCategories(const QString &parentId) const +{ + QList results; + foreach (const QString &childId, m_categoryTree.value(parentId).childIds) + results.append(m_categoryTree.value(childId).category); + return results; +} + +QList QPlaceManagerEngineNokiaV2::locales() const +{ + return m_locales; +} + +void QPlaceManagerEngineNokiaV2::setLocales(const QList &locales) +{ + m_locales = locales; +} + +QPlaceIcon QPlaceManagerEngineNokiaV2::icon(const QString &remotePath, + const QList &categories) const +{ + QPlaceIcon icon; + QVariantMap params; + + QRegExp rx("(.*)(/icons/categories/.*)"); + + QString iconPrefix; + QString nokiaIcon; + if (rx.indexIn(remotePath) != -1 && !rx.cap(1).isEmpty() && !rx.cap(2).isEmpty()) { + iconPrefix = rx.cap(1); + nokiaIcon = rx.cap(2); + + if (QFile::exists(m_localDataPath + nokiaIcon)) + iconPrefix = QString::fromLatin1("file://") + m_localDataPath; + + params.insert(NokiaIcon, nokiaIcon); + params.insert(IconPrefix, iconPrefix); + + foreach (const QPlaceCategory &category, categories) { + if (category.icon().parameters().value(NokiaIcon) == nokiaIcon) { + params.insert(NokiaIconGenerated, true); + break; + } + } + } else { + QString path = remotePath + (!m_theme.isEmpty() + ? QLatin1Char('.') + m_theme : QString()); + params.insert(QPlaceIcon::SingleUrl, QUrl(path)); + + if (!nokiaIcon.isEmpty()) { + params.insert(NokiaIcon, nokiaIcon); + params.insert(IconPrefix, iconPrefix); + params.insert(NokiaIconGenerated, true); + } + } + + icon.setParameters(params); + + if (!icon.isEmpty()) + icon.setManager(manager()); + + return icon; +} + +QUrl QPlaceManagerEngineNokiaV2::constructIconUrl(const QPlaceIcon &icon, + const QSize &size) const +{ + Q_UNUSED(size) + QVariantMap params = icon.parameters(); + QString nokiaIcon = params.value(NokiaIcon).toString(); + + if (!nokiaIcon.isEmpty()) { + nokiaIcon.append(!m_theme.isEmpty() ? + QLatin1Char('.') + m_theme : QString()); + + if (params.contains(IconPrefix)) { + return QUrl(params.value(IconPrefix).toString() + + nokiaIcon); + } else { + return QUrl(QString::fromLatin1("file://") + m_localDataPath + + nokiaIcon); + } + } + + return QUrl(); +} + +void QPlaceManagerEngineNokiaV2::replyFinished() +{ + QPlaceReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void QPlaceManagerEngineNokiaV2::replyError(QPlaceReply::Error error_, const QString &errorString) +{ + QPlaceReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, error_, errorString); +} + +void QPlaceManagerEngineNokiaV2::categoryReplyFinished() +{ + QNetworkReply *reply = qobject_cast(sender()); + if (!reply) + return; + + QString categoryId; + + if (reply->error() == QNetworkReply::NoError) { + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isObject()) { + if (m_categoryReply) { + QMetaObject::invokeMethod(m_categoryReply.data(), "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::ParseError), + Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, PARSE_ERROR))); + } + return; + } + + QJsonObject category = document.object(); + + categoryId = category.value(QStringLiteral("categoryId")).toString(); + if (m_tempTree.contains(categoryId)) { + PlaceCategoryNode node = m_tempTree.value(categoryId); + node.category.setName(category.value(QStringLiteral("name")).toString()); + node.category.setCategoryId(categoryId); + node.category.setIcon(icon(category.value(QStringLiteral("icon")).toString())); + + m_tempTree.insert(categoryId, node); + } + } else { + categoryId = m_categoryRequests.key(reply); + PlaceCategoryNode rootNode = m_tempTree.value(QString()); + rootNode.childIds.removeAll(categoryId); + m_tempTree.insert(QString(), rootNode); + m_tempTree.remove(categoryId); + } + + m_categoryRequests.remove(categoryId); + reply->deleteLater(); + + if (m_categoryRequests.isEmpty()) { + m_categoryTree = m_tempTree; + m_tempTree.clear(); + + if (m_categoryReply) + m_categoryReply.data()->emitFinished(); + } +} + +void QPlaceManagerEngineNokiaV2::categoryReplyError() +{ + if (m_categoryReply) { + QMetaObject::invokeMethod(m_categoryReply.data(), "setError", Qt::QueuedConnection, + Q_ARG(QPlaceReply::Error, QPlaceReply::CommunicationError), + Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, NETWORK_ERROR))); + } +} + +QNetworkReply *QPlaceManagerEngineNokiaV2::sendRequest(const QUrl &url) +{ + QUrlQuery queryItems(url); + queryItems.addQueryItem(QStringLiteral("app_id"), m_appId); + queryItems.addQueryItem(QStringLiteral("app_code"), m_appCode); + + QUrl requestUrl = url; + requestUrl.setQuery(queryItems); + + QNetworkRequest request; + request.setUrl(requestUrl); + + request.setRawHeader("Accept", "application/json"); + request.setRawHeader("Accept-Language", createLanguageString()); + + return m_manager->get(request); +} + +QByteArray QPlaceManagerEngineNokiaV2::createLanguageString() const +{ + QByteArray language; + + QList locales = m_locales; + if (locales.isEmpty()) + locales << QLocale(); + + foreach (const QLocale &loc, locales) { + language.append(loc.name().replace(2, 1, QLatin1Char('-')).toLatin1()); + language.append(", "); + } + language.chop(2); + + return language; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h new file mode 100644 index 0000000..cd63295 --- /dev/null +++ b/src/plugins/geoservices/nokia/qplacemanagerengine_nokiav2.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMANAGERENGINE_NOKIAV2_H +#define QPLACEMANAGERENGINE_NOKIAV2_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QPlaceContentReply; +class QNetworkReply; +class QNetworkAccessManager; +class QPlaceCategoriesReplyHere; +class QGeoNetworkAccessManager; +class QGeoUriProvider; + +struct PlaceCategoryNode +{ + QString parentId; + QStringList childIds; + QPlaceCategory category; +}; + +typedef QMap QPlaceCategoryTree; + +class QPlaceManagerEngineNokiaV2 : public QPlaceManagerEngine +{ + Q_OBJECT + +public: + QPlaceManagerEngineNokiaV2(QGeoNetworkAccessManager *networkManager, + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString); + ~QPlaceManagerEngineNokiaV2(); + + QPlaceDetailsReply *getPlaceDetails(const QString &placeId) override; + + QPlaceContentReply *getPlaceContent(const QPlaceContentRequest &request) override; + + QPlaceSearchReply *search(const QPlaceSearchRequest &query) override; + + QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &query) override; + + QPlaceIdReply *savePlace(const QPlace &place) override; + QPlaceIdReply *removePlace(const QString &placeId) override; + + QPlaceIdReply *saveCategory(const QPlaceCategory &category, const QString &parentId) override; + QPlaceIdReply *removeCategory(const QString &categoryId) override; + + QPlaceReply *initializeCategories() override; + QString parentCategoryId(const QString &categoryId) const override; + QStringList childCategoryIds(const QString &categoryId) const override; + QPlaceCategory category(const QString &categoryId) const override; + QList childCategories(const QString &parentId) const override; + + QList locales() const override; + void setLocales(const QList &locales) override; + + QPlaceIcon icon(const QString &remotePath, + const QList &categories = QList()) const; + + QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const override; + +private: + QNetworkReply *sendRequest(const QUrl &url); + QByteArray createLanguageString() const; + +private Q_SLOTS: + void replyFinished(); + void replyError(QPlaceReply::Error error_, const QString &errorString); + void categoryReplyFinished(); + void categoryReplyError(); + +private: + QGeoNetworkAccessManager *m_manager; + QGeoUriProvider *m_uriProvider; + + QList m_locales; + + QPlaceCategoryTree m_categoryTree; + QPlaceCategoryTree m_tempTree; + QHash m_restIdToIconHash; + + QPointer m_categoryReply; + QHash m_categoryRequests; + + QString m_appId; + QString m_appCode; + + QString m_localDataPath; + QString m_theme; +}; + +QT_END_NAMESPACE + +#endif // QPLACEMANAGERENGINE_NOKIAV2_H diff --git a/src/plugins/geoservices/nokia/uri_constants.cpp b/src/plugins/geoservices/nokia/uri_constants.cpp new file mode 100644 index 0000000..8db47be --- /dev/null +++ b/src/plugins/geoservices/nokia/uri_constants.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "uri_constants.h" + +QT_BEGIN_NAMESPACE + +const QString ROUTING_HOST = QLatin1String("route.api.here.com"); +const QString GEOCODING_HOST = QLatin1String("geocoder.api.here.com"); +const QString REVERSE_GEOCODING_HOST = QLatin1String("reverse.geocoder.api.here.com"); +const QString PLACES_HOST = QLatin1String("places.api.here.com"); +const QString MAP_TILES_HOST = QLatin1String("1-4.base.maps.api.here.com"); +const QString MAP_TILES_HOST_AERIAL = QLatin1String("1-4.aerial.maps.api.here.com"); + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/nokia/uri_constants.h b/src/plugins/geoservices/nokia/uri_constants.h new file mode 100644 index 0000000..b2133fe --- /dev/null +++ b/src/plugins/geoservices/nokia/uri_constants.h @@ -0,0 +1,53 @@ + +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef URI_CONSTANTS_H +#define URI_CONSTANTS_H + +#include + +QT_BEGIN_NAMESPACE + +extern const QString ROUTING_HOST; +extern const QString GEOCODING_HOST; +extern const QString REVERSE_GEOCODING_HOST; +extern const QString PLACES_HOST; +extern const QString MAP_TILES_HOST; +extern const QString MAP_TILES_HOST_AERIAL; + +QT_END_NAMESPACE + +#endif // URI_CONSTANTS_H diff --git a/src/plugins/geoservices/osm/osm.pro b/src/plugins/geoservices/osm/osm.pro new file mode 100644 index 0000000..74e27dc --- /dev/null +++ b/src/plugins/geoservices/osm/osm.pro @@ -0,0 +1,46 @@ +TARGET = qtgeoservices_osm + +QT += location-private positioning-private network concurrent + +QT_FOR_CONFIG += location-private +qtConfig(location-labs-plugin): DEFINES += LOCATIONLABS + +HEADERS += \ + qgeoserviceproviderpluginosm.h \ + qgeotiledmappingmanagerengineosm.h \ + qgeotilefetcherosm.h \ + qgeomapreplyosm.h \ + qgeocodingmanagerengineosm.h \ + qgeocodereplyosm.h \ + qgeoroutingmanagerengineosm.h \ + qgeoroutereplyosm.h \ + qplacemanagerengineosm.h \ + qplacesearchreplyosm.h \ + qplacecategoriesreplyosm.h \ + qgeotiledmaposm.h \ + qgeofiletilecacheosm.h \ + qgeotileproviderosm.h + +SOURCES += \ + qgeoserviceproviderpluginosm.cpp \ + qgeotiledmappingmanagerengineosm.cpp \ + qgeotilefetcherosm.cpp \ + qgeomapreplyosm.cpp \ + qgeocodingmanagerengineosm.cpp \ + qgeocodereplyosm.cpp \ + qgeoroutingmanagerengineosm.cpp \ + qgeoroutereplyosm.cpp \ + qplacemanagerengineosm.cpp \ + qplacesearchreplyosm.cpp \ + qplacecategoriesreplyosm.cpp \ + qgeotiledmaposm.cpp \ + qgeofiletilecacheosm.cpp \ + qgeotileproviderosm.cpp + + +OTHER_FILES += \ + osm_plugin.json + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = QGeoServiceProviderFactoryOsm +load(qt_plugin) diff --git a/src/plugins/geoservices/osm/osm_plugin.json b/src/plugins/geoservices/osm/osm_plugin.json new file mode 100644 index 0000000..1aaf6f7 --- /dev/null +++ b/src/plugins/geoservices/osm/osm_plugin.json @@ -0,0 +1,13 @@ +{ + "Keys": ["osm"], + "Provider": "osm", + "Version": 100, + "Experimental": false, + "Features": [ + "OnlineMappingFeature", + "OnlineGeocodingFeature", + "ReverseGeocodingFeature", + "OnlineRoutingFeature", + "OnlinePlacesFeature" + ] +} diff --git a/src/plugins/geoservices/osm/providers/5.8/cycle b/src/plugins/geoservices/osm/providers/5.8/cycle new file mode 100644 index 0000000..5e37aab --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/cycle @@ -0,0 +1,8 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/cycle/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-cycle", + "MapCopyRight" : "Thunderforest", + "DataCopyRight" : "OpenStreetMap contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/hiking b/src/plugins/geoservices/osm/providers/5.8/hiking new file mode 100644 index 0000000..1bb182e --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/hiking @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://b.tiles.wmflabs.org/hikebike/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "wmf-hike", + "MaximumZoomLevel" : 18, + "MapCopyRight" : "WikiMedia Foundation", + "DataCopyRight" : "OpenStreetMap contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/night-transit b/src/plugins/geoservices/osm/providers/5.8/night-transit new file mode 100644 index 0000000..988a096 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/night-transit @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-nighttransit", + "MaximumZoomLevel" : 19, + "MapCopyRight" : "Thunderforest", + "DataCopyRight" : "OpenStreetMap contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/satellite b/src/plugins/geoservices/osm/providers/5.8/satellite new file mode 100644 index 0000000..5c48a07 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/satellite @@ -0,0 +1,10 @@ +{ + "Enabled" : false, + "UrlTemplate" : "http://basemap.nationalmap.gov/arcgis/rest/services/USGSImageryOnly/MapServer/tile/%z/%y/%x", + "ImageFormat" : "jpg", + "QImageFormat" : "RGB888", + "ID" : "usgs-l7", + "MaximumZoomLevel" : 8, + "MapCopyRight" : "USGS The National Map: Orthoimagery", + "DataCopyRight" : "USGS/NASA Landsat" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/street b/src/plugins/geoservices/osm/providers/5.8/street new file mode 100644 index 0000000..b3bccf1 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/street @@ -0,0 +1,10 @@ +{ + "UrlTemplate" : "http://korona.geog.uni-heidelberg.de/tiles/roads/x=%x&y=%y&z=%z", + "ImageFormat" : "jpg", + "QImageFormat" : "Indexed8", + "ID" : "oms-street", + "MaximumZoomLevel" : 20, + "MapCopyRight" : "GIScience Research Group @ University of Heidelberg", + "StyleCopyRight" : "Maxim Rylov", + "DataCopyRight" : "OpenStreetMap contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/street-hires b/src/plugins/geoservices/osm/providers/5.8/street-hires new file mode 100644 index 0000000..9819f61 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/street-hires @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "https://maps.wikimedia.org/osm-intl/%z/%x/%y@2x.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "MaximumZoomLevel" : 18, + "ID" : "wmf-intl-2x", + "MapCopyRight" : "WikiMedia Foundation", + "DataCopyRight" : "OpenStreetMap contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/terrain b/src/plugins/geoservices/osm/providers/5.8/terrain new file mode 100644 index 0000000..7fc6636 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/terrain @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/landscape/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-landsc", + "MaximumZoomLevel" : 19, + "MapCopyRight" : "Thunderforest", + "DataCopyRight" : "OpenStreetMap contributors" +} diff --git a/src/plugins/geoservices/osm/providers/5.8/transit b/src/plugins/geoservices/osm/providers/5.8/transit new file mode 100644 index 0000000..ebf87b0 --- /dev/null +++ b/src/plugins/geoservices/osm/providers/5.8/transit @@ -0,0 +1,9 @@ +{ + "UrlTemplate" : "http://a.tile.thunderforest.com/transport/%z/%x/%y.png", + "ImageFormat" : "png", + "QImageFormat" : "Indexed8", + "ID" : "thf-transit", + "MaximumZoomLevel" : 19, + "MapCopyRight" : "Thunderforest", + "DataCopyRight" : "OpenStreetMap contributors" +} diff --git a/src/plugins/geoservices/osm/qgeocodereplyosm.cpp b/src/plugins/geoservices/osm/qgeocodereplyosm.cpp new file mode 100644 index 0000000..a30601d --- /dev/null +++ b/src/plugins/geoservices/osm/qgeocodereplyosm.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodereplyosm.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoCodeReplyOsm::QGeoCodeReplyOsm(QNetworkReply *reply, QObject *parent) +: QGeoCodeReply(parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoCodeReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + setLimit(1); + setOffset(0); +} + +QGeoCodeReplyOsm::~QGeoCodeReplyOsm() +{ +} + +static QGeoAddress parseAddressObject(const QJsonObject &object) +{ + QGeoAddress address; + address.setText(object.value(QStringLiteral("display_name")).toString()); + QJsonObject ao = object.value(QStringLiteral("address")).toObject(); + // setCountry + address.setCountry(ao.value(QStringLiteral("country")).toString()); + // setCountryCode + address.setCountryCode(ao.value(QStringLiteral("country_code")).toString()); + // setState + address.setState(ao.value(QStringLiteral("state")).toString()); + // setCity + if (ao.contains(QLatin1String("city"))) + address.setCity(ao.value(QStringLiteral("city")).toString()); + else if (ao.contains(QLatin1String("town"))) + address.setCity(ao.value(QLatin1String("town")).toString()); + else if (ao.contains(QLatin1String("village"))) + address.setCity(ao.value(QLatin1String("village")).toString()); + else + address.setCity(ao.value(QLatin1String("hamlet")).toString()); + // setDistrict + address.setDistrict(ao.value(QStringLiteral("suburb")).toString()); + // setPostalCode + address.setPostalCode(ao.value(QStringLiteral("postcode")).toString()); + // setStreet + address.setStreet(ao.value(QStringLiteral("road")).toString()); + return address; +} + +void QGeoCodeReplyOsm::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QList locations; + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + + if (document.isObject()) { + QJsonObject object = document.object(); + + QGeoCoordinate coordinate; + + coordinate.setLatitude(object.value(QStringLiteral("lat")).toString().toDouble()); + coordinate.setLongitude(object.value(QStringLiteral("lon")).toString().toDouble()); + + QGeoLocation location; + location.setCoordinate(coordinate); + location.setAddress(parseAddressObject(object)); + + locations.append(location); + + setLocations(locations); + } else if (document.isArray()) { + QJsonArray results = document.array(); + + for (int i = 0; i < results.count(); ++i) { + if (!results.at(i).isObject()) + continue; + + QJsonObject object = results.at(i).toObject(); + + QGeoCoordinate coordinate; + + coordinate.setLatitude(object.value(QStringLiteral("lat")).toString().toDouble()); + coordinate.setLongitude(object.value(QStringLiteral("lon")).toString().toDouble()); + + QGeoRectangle rectangle; + + if (object.contains(QStringLiteral("boundingbox"))) { + QJsonArray a = object.value(QStringLiteral("boundingbox")).toArray(); + if (a.count() == 4) { + rectangle.setTopLeft(QGeoCoordinate(a.at(1).toString().toDouble(), + a.at(2).toString().toDouble())); + rectangle.setBottomRight(QGeoCoordinate(a.at(0).toString().toDouble(), + a.at(3).toString().toDouble())); + } + } + + QGeoLocation location; + location.setCoordinate(coordinate); + location.setBoundingBox(rectangle); + location.setAddress(parseAddressObject(object)); + locations.append(location); + } + + } + + setLocations(locations); + setFinished(true); +} + +void QGeoCodeReplyOsm::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QGeoCodeReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeocodereplyosm.h b/src/plugins/geoservices/osm/qgeocodereplyosm.h new file mode 100644 index 0000000..0847f58 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeocodereplyosm.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODEREPLYOSM_H +#define QGEOCODEREPLYOSM_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCodeReplyOsm : public QGeoCodeReply +{ + Q_OBJECT + +public: + explicit QGeoCodeReplyOsm(QNetworkReply *reply, QObject *parent = 0); + ~QGeoCodeReplyOsm(); + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QGEOCODEREPLYOSM_H diff --git a/src/plugins/geoservices/osm/qgeocodingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeocodingmanagerengineosm.cpp new file mode 100644 index 0000000..d775128 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeocodingmanagerengineosm.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocodingmanagerengineosm.h" +#include "qgeocodereplyosm.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static QString addressToQuery(const QGeoAddress &address) +{ + return address.street() + QStringLiteral(", ") + + address.district() + QStringLiteral(", ") + + address.city() + QStringLiteral(", ") + + address.state() + QStringLiteral(", ") + + address.country(); +} + +static QString boundingBoxToLtrb(const QGeoRectangle &rect) +{ + return QString::number(rect.topLeft().longitude()) + QLatin1Char(',') + + QString::number(rect.topLeft().latitude()) + QLatin1Char(',') + + QString::number(rect.bottomRight().longitude()) + QLatin1Char(',') + + QString::number(rect.bottomRight().latitude()); +} + +QGeoCodingManagerEngineOsm::QGeoCodingManagerEngineOsm(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) +: QGeoCodingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(QStringLiteral("osm.useragent"))) + m_userAgent = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1(); + else + m_userAgent = "Qt Location based application"; + + if (parameters.contains(QStringLiteral("osm.geocoding.host"))) + m_urlPrefix = parameters.value(QStringLiteral("osm.geocoding.host")).toString().toLatin1(); + else + m_urlPrefix = QStringLiteral("https://nominatim.openstreetmap.org"); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QGeoCodingManagerEngineOsm::~QGeoCodingManagerEngineOsm() +{ +} + +QGeoCodeReply *QGeoCodingManagerEngineOsm::geocode(const QGeoAddress &address, const QGeoShape &bounds) +{ + return geocode(addressToQuery(address), -1, -1, bounds); +} + +QGeoCodeReply *QGeoCodingManagerEngineOsm::geocode(const QString &address, int limit, int offset, const QGeoShape &bounds) +{ + Q_UNUSED(offset) + + QNetworkRequest request; + request.setRawHeader("User-Agent", m_userAgent); + + QUrl url(QString("%1/search").arg(m_urlPrefix)); + QUrlQuery query; + query.addQueryItem(QStringLiteral("q"), address); + query.addQueryItem(QStringLiteral("format"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("accept-language"), locale().name().left(2)); + //query.addQueryItem(QStringLiteral("countrycodes"), QStringLiteral("au,jp")); + if (bounds.type() != QGeoShape::UnknownType) { + query.addQueryItem(QStringLiteral("viewbox"), boundingBoxToLtrb(bounds.boundingGeoRectangle())); + query.addQueryItem(QStringLiteral("bounded"), QStringLiteral("1")); + } + query.addQueryItem(QStringLiteral("polygon_geojson"), QStringLiteral("1")); + query.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1")); + if (limit != -1) + query.addQueryItem(QStringLiteral("limit"), QString::number(limit)); + + url.setQuery(query); + request.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(request); + + QGeoCodeReplyOsm *geocodeReply = new QGeoCodeReplyOsm(reply, this); + + connect(geocodeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), + this, SLOT(replyError(QGeoCodeReply::Error,QString))); + + return geocodeReply; +} + +QGeoCodeReply *QGeoCodingManagerEngineOsm::reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) +{ + Q_UNUSED(bounds) + + QNetworkRequest request; + request.setRawHeader("User-Agent", m_userAgent); + + QUrl url(QString("%1/reverse").arg(m_urlPrefix)); + QUrlQuery query; + query.addQueryItem(QStringLiteral("format"), QStringLiteral("json")); + query.addQueryItem(QStringLiteral("accept-language"), locale().name().left(2)); + query.addQueryItem(QStringLiteral("lat"), QString::number(coordinate.latitude())); + query.addQueryItem(QStringLiteral("lon"), QString::number(coordinate.longitude())); + query.addQueryItem(QStringLiteral("zoom"), QStringLiteral("18")); + query.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1")); + + url.setQuery(query); + request.setUrl(url); + + QNetworkReply *reply = m_networkManager->get(request); + + QGeoCodeReplyOsm *geocodeReply = new QGeoCodeReplyOsm(reply, this); + + connect(geocodeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(geocodeReply, SIGNAL(error(QGeoCodeReply::Error,QString)), + this, SLOT(replyError(QGeoCodeReply::Error,QString))); + + return geocodeReply; +} + +void QGeoCodingManagerEngineOsm::replyFinished() +{ + QGeoCodeReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void QGeoCodingManagerEngineOsm::replyError(QGeoCodeReply::Error errorCode, const QString &errorString) +{ + QGeoCodeReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeocodingmanagerengineosm.h b/src/plugins/geoservices/osm/qgeocodingmanagerengineosm.h new file mode 100644 index 0000000..0fec49d --- /dev/null +++ b/src/plugins/geoservices/osm/qgeocodingmanagerengineosm.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGERENGINEOSM_H +#define QGEOCODINGMANAGERENGINEOSM_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class QGeoCodingManagerEngineOsm : public QGeoCodingManagerEngine +{ + Q_OBJECT + +public: + QGeoCodingManagerEngineOsm(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoCodingManagerEngineOsm(); + + QGeoCodeReply *geocode(const QGeoAddress &address, const QGeoShape &bounds) override; + QGeoCodeReply *geocode(const QString &address, int limit, int offset, + const QGeoShape &bounds) override; + QGeoCodeReply *reverseGeocode(const QGeoCoordinate &coordinate, + const QGeoShape &bounds) override; + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoCodeReply::Error errorCode, const QString &errorString); + +private: + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_urlPrefix; +}; + +QT_END_NAMESPACE + +#endif // QGEOCODINGMANAGERENGINEOSM_H diff --git a/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp new file mode 100644 index 0000000..d79702c --- /dev/null +++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.cpp @@ -0,0 +1,318 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeofiletilecacheosm.h" +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QGeoFileTileCacheOsm::QGeoFileTileCacheOsm(const QVector &providers, + const QString &offlineDirectory, + const QString &directory, + QObject *parent) +: QGeoFileTileCache(directory, parent), m_offlineDirectory(offlineDirectory), m_offlineData(false), m_providers(providers) +{ + m_highDpi.resize(providers.size()); + if (!offlineDirectory.isEmpty()) { + m_offlineDirectory = QDir(offlineDirectory); + if (m_offlineDirectory.exists()) + m_offlineData = true; + } + for (int i = 0; i < providers.size(); i++) { + providers[i]->setParent(this); + m_highDpi[i] = providers[i]->isHighDpi(); + connect(providers[i], &QGeoTileProviderOsm::resolutionFinished, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished); + connect(providers[i], &QGeoTileProviderOsm::resolutionError, this, &QGeoFileTileCacheOsm::onProviderResolutionFinished); + } +} + +QGeoFileTileCacheOsm::~QGeoFileTileCacheOsm() +{ +} + +QSharedPointer QGeoFileTileCacheOsm::get(const QGeoTileSpec &spec) +{ + QSharedPointer tt = getFromMemory(spec); + if (tt) + return tt; + if ((tt = getFromOfflineStorage(spec))) + return tt; + return getFromDisk(spec); +} + +void QGeoFileTileCacheOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider) +{ + clearObsoleteTiles(provider); + Q_UNUSED(provider) + for (int i = 0; i < m_providers.size(); i++) { + if (m_providers[i]->isHighDpi() != m_highDpi[i]) { // e.g., HiDpi was requested but only LoDpi is available + int mapId = m_providers[i]->mapType().mapId(); + m_highDpi[i] = m_providers[i]->isHighDpi(); + + // reload cache for mapId i + dropTiles(mapId); + loadTiles(mapId); + + // send signal to clear scene in all maps created through this provider that use the reloaded tiles + emit mapDataUpdated(mapId); + } + } +} + +// On resolution error the provider is removed. +// This happens ONLY if there is no enabled hardcoded fallback for the mapId. +// Hardcoded fallbacks also have a timestamp, that can get updated with Qt releases. +void QGeoFileTileCacheOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + clearObsoleteTiles(provider); // this still removes tiles who happen to be older than qgeotileproviderosm.cpp defaultTs +} + +// init() is always called before the provider resolution starts +void QGeoFileTileCacheOsm::init() +{ + if (directory_.isEmpty()) + directory_ = baseLocationCacheDirectory(); + QDir::root().mkpath(directory_); + + // find max mapId + int max = 0; + for (auto p: m_providers) + if (p->mapType().mapId() > max) + max = p->mapType().mapId(); + // Create a mapId to maxTimestamp LUT.. + m_maxMapIdTimestamps.resize(max+1); // initializes to invalid QDateTime + + // .. by finding the newest file in each tileset (tileset = mapId). + QDir dir(directory_); + QStringList formats; + formats << QLatin1String("*.*"); + QStringList files = dir.entryList(formats, QDir::Files); + + for (const QString &tileFileName : files) { + QGeoTileSpec spec = filenameToTileSpec(tileFileName); + if (spec.zoom() == -1) + continue; + QFileInfo fi(dir.filePath(tileFileName)); + if (fi.lastModified() > m_maxMapIdTimestamps[spec.mapId()]) + m_maxMapIdTimestamps[spec.mapId()] = fi.lastModified(); + } + + // Base class ::init() + QGeoFileTileCache::init(); + + for (QGeoTileProviderOsm * p: m_providers) + clearObsoleteTiles(p); +} + +QSharedPointer QGeoFileTileCacheOsm::getFromOfflineStorage(const QGeoTileSpec &spec) +{ + if (!m_offlineData) + return QSharedPointer(); + + int providerId = spec.mapId() - 1; + if (providerId < 0 || providerId >= m_providers.size()) + return QSharedPointer(); + + const QString fileName = tileSpecToFilename(spec, QStringLiteral("*"), providerId); + QStringList validTiles = m_offlineDirectory.entryList({fileName}); + if (!validTiles.size()) + return QSharedPointer(); + + QFile file(m_offlineDirectory.absoluteFilePath(validTiles.first())); + if (!file.open(QIODevice::ReadOnly)) + return QSharedPointer(); + QByteArray bytes = file.readAll(); + file.close(); + + QImage image; + if (!image.loadFromData(bytes)) { + handleError(spec, QLatin1String("Problem with tile image")); + return QSharedPointer(0); + } + + addToMemoryCache(spec, bytes, QString()); + return addToTextureCache(spec, image); +} + +void QGeoFileTileCacheOsm::dropTiles(int mapId) +{ + QList keys; + keys = textureCache_.keys(); + for (const QGeoTileSpec &k : keys) + if (k.mapId() == mapId) + textureCache_.remove(k); + + keys = memoryCache_.keys(); + for (const QGeoTileSpec &k : keys) + if (k.mapId() == mapId) + memoryCache_.remove(k); + + keys = diskCache_.keys(); + for (const QGeoTileSpec &k : keys) + if (k.mapId() == mapId) + diskCache_.remove(k); +} + +void QGeoFileTileCacheOsm::loadTiles(int mapId) +{ + QStringList formats; + formats << QLatin1String("*.*"); + + QDir dir(directory_); + QStringList files = dir.entryList(formats, QDir::Files); + + for (int i = 0; i < files.size(); ++i) { + QGeoTileSpec spec = filenameToTileSpec(files.at(i)); + if (spec.zoom() == -1 || spec.mapId() != mapId) + continue; + QString filename = dir.filePath(files.at(i)); + addToDiskCache(spec, filename); + } +} + +QString QGeoFileTileCacheOsm::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const +{ + int providerId = spec.mapId() - 1; + if (providerId < 0 || providerId >= m_providers.size()) + return QString(); + + QDir dir = QDir(directory); + return dir.filePath(tileSpecToFilename(spec, format, providerId)); +} + +QString QGeoFileTileCacheOsm::tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, int providerId) const +{ + QString filename = spec.plugin(); + filename += QLatin1String("-"); + filename += (m_providers[providerId]->isHighDpi()) ? QLatin1Char('h') : QLatin1Char('l'); + filename += QLatin1String("-"); + filename += QString::number(spec.mapId()); + filename += QLatin1String("-"); + filename += QString::number(spec.zoom()); + filename += QLatin1String("-"); + filename += QString::number(spec.x()); + filename += QLatin1String("-"); + filename += QString::number(spec.y()); + + //Append version if real version number to ensure backwards compatibility and eviction of old tiles + if (spec.version() != -1) { + filename += QLatin1String("-"); + filename += QString::number(spec.version()); + } + + filename += QLatin1String("."); + filename += format; + return filename; +} + +QGeoTileSpec QGeoFileTileCacheOsm::filenameToTileSpec(const QString &filename) const +{ + QGeoTileSpec emptySpec; + + QStringList parts = filename.split('.'); + + if (parts.length() != 2) + return emptySpec; + + QString name = parts.at(0); + QStringList fields = name.split('-'); + + int length = fields.length(); + if (length != 6 && length != 7) + return emptySpec; + + QList numbers; + + bool ok = false; + for (int i = 2; i < length; ++i) { + ok = false; + int value = fields.at(i).toInt(&ok); + if (!ok) + return emptySpec; + numbers.append(value); + } + + if (numbers.at(0) > m_providers.size()) + return emptySpec; + + bool highDpi = m_providers[numbers.at(0) - 1]->isHighDpi(); + if (highDpi && fields.at(1) != QLatin1Char('h')) + return emptySpec; + else if (!highDpi && fields.at(1) != QLatin1Char('l')) + return emptySpec; + + //File name without version, append default + if (numbers.length() < 5) + numbers.append(-1); + + return QGeoTileSpec(fields.at(0), + numbers.at(0), + numbers.at(1), + numbers.at(2), + numbers.at(3), + numbers.at(4)); +} + +void QGeoFileTileCacheOsm::clearObsoleteTiles(const QGeoTileProviderOsm *p) +{ + // process initialized providers, and connect the others + + if (p->isResolved()) { + if (m_maxMapIdTimestamps[p->mapType().mapId()].isValid() && // there are tiles in the cache + p->timestamp() > m_maxMapIdTimestamps[p->mapType().mapId()]) { // and they are older than the provider + qInfo() << "provider for " << p->mapType().name() << " timestamp: " << p->timestamp() + << " -- data last modified: " << m_maxMapIdTimestamps[p->mapType().mapId()] << ". Clearing."; + clearMapId(p->mapType().mapId()); + m_maxMapIdTimestamps[p->mapType().mapId()] = p->timestamp(); // don't do it again. + } + } else { + connect(p, &QGeoTileProviderOsm::resolutionFinished, + this, &QGeoFileTileCacheOsm::onProviderResolutionFinished); +#if 0 // If resolution fails, better not try to remove anything. Beside, on error, resolutionFinished is also emitted. + connect(p, &QGeoTileProviderOsm::resolutionError, + this, &QGeoFileTileCacheOsm::onProviderResolutionError); +#endif + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeofiletilecacheosm.h b/src/plugins/geoservices/osm/qgeofiletilecacheosm.h new file mode 100644 index 0000000..da1fd0d --- /dev/null +++ b/src/plugins/geoservices/osm/qgeofiletilecacheosm.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOFILETILECACHEOSM_H +#define QGEOFILETILECACHEOSM_H + +#include "qgeotileproviderosm.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoFileTileCacheOsm : public QGeoFileTileCache +{ + Q_OBJECT +public: + QGeoFileTileCacheOsm(const QVector &providers, + const QString &offlineDirectory = QString(), + const QString &directory = QString(), + QObject *parent = 0); + ~QGeoFileTileCacheOsm(); + + QSharedPointer get(const QGeoTileSpec &spec) override; + +Q_SIGNALS: + void mapDataUpdated(int mapId); + +protected Q_SLOTS: + void onProviderResolutionFinished(const QGeoTileProviderOsm *provider); + void onProviderResolutionError(const QGeoTileProviderOsm *provider, QNetworkReply::NetworkError error); + +protected: + void init() override; + inline QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, int providerId) const; + QString tileSpecToFilename(const QGeoTileSpec &spec, const QString &format, const QString &directory) const override; + QGeoTileSpec filenameToTileSpec(const QString &filename) const override; + QSharedPointer getFromOfflineStorage(const QGeoTileSpec &spec); + void dropTiles(int mapId); + void loadTiles(int mapId); + + void clearObsoleteTiles(const QGeoTileProviderOsm *p); + + QDir m_offlineDirectory; + bool m_offlineData; + QVector m_providers; + QVector m_highDpi; + QVector m_maxMapIdTimestamps; +}; + +QT_END_NAMESPACE + +#endif // QGEOFILETILECACHEOSM_H diff --git a/src/plugins/geoservices/osm/qgeomapreplyosm.cpp b/src/plugins/geoservices/osm/qgeomapreplyosm.cpp new file mode 100644 index 0000000..a06f91f --- /dev/null +++ b/src/plugins/geoservices/osm/qgeomapreplyosm.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeomapreplyosm.h" + +#include + +QGeoMapReplyOsm::QGeoMapReplyOsm(QNetworkReply *reply, + const QGeoTileSpec &spec, + const QString &imageFormat, + QObject *parent) +: QGeoTiledMapReply(spec, parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoTiledMapReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); + setMapImageFormat(imageFormat); +} + +QGeoMapReplyOsm::~QGeoMapReplyOsm() +{ +} + +void QGeoMapReplyOsm::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) // Already handled in networkReplyError + return; + + QByteArray a = reply->readAll(); + + setMapImageData(a); + setFinished(true); +} + +void QGeoMapReplyOsm::networkReplyError(QNetworkReply::NetworkError error) +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + if (error == QNetworkReply::OperationCanceledError) + setFinished(true); + else + setError(QGeoTiledMapReply::CommunicationError, reply->errorString()); + +} diff --git a/src/plugins/geoservices/osm/qgeomapreplyosm.h b/src/plugins/geoservices/osm/qgeomapreplyosm.h new file mode 100644 index 0000000..ef0cbb1 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeomapreplyosm.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPREPLYOSM_H +#define QGEOMAPREPLYOSM_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoMapReplyOsm : public QGeoTiledMapReply +{ + Q_OBJECT + +public: + QGeoMapReplyOsm(QNetworkReply *reply, const QGeoTileSpec &spec, const QString &imageFormat, QObject *parent = 0); + ~QGeoMapReplyOsm(); + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QGEOMAPREPLYOSM_H diff --git a/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp b/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp new file mode 100644 index 0000000..732e8d7 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeoroutereplyosm.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutereplyosm.h" +#include "qgeoroutingmanagerengineosm.h" + +QT_BEGIN_NAMESPACE + +QGeoRouteReplyOsm::QGeoRouteReplyOsm(QNetworkReply *reply, const QGeoRouteRequest &request, + QObject *parent) +: QGeoRouteReply(request, parent) +{ + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + connect(reply, SIGNAL(finished()), this, SLOT(networkReplyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(networkReplyError(QNetworkReply::NetworkError))); + connect(this, &QGeoRouteReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QGeoRouteReplyOsm::~QGeoRouteReplyOsm() +{ +} + +void QGeoRouteReplyOsm::networkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QGeoRoutingManagerEngineOsm *engine = qobject_cast(parent()); + const QGeoRouteParser *parser = engine->routeParser(); + + QList routes; + QString errorString; + QGeoRouteReply::Error error = parser->parseReply(routes, errorString, reply->readAll()); + + if (error == QGeoRouteReply::NoError) { + setRoutes(routes.mid(0, request().numberAlternativeRoutes() + 1)); + // setError(QGeoRouteReply::NoError, status); // can't do this, or NoError is emitted and does damages + setFinished(true); + } else { + setError(error, errorString); + } +} + +void QGeoRouteReplyOsm::networkReplyError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QGeoRouteReply::CommunicationError, reply->errorString()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeoroutereplyosm.h b/src/plugins/geoservices/osm/qgeoroutereplyosm.h new file mode 100644 index 0000000..feaae59 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeoroutereplyosm.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTEREPLYOSM_H +#define QGEOROUTEREPLYOSM_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoRouteReplyOsm : public QGeoRouteReply +{ + Q_OBJECT + +public: + explicit QGeoRouteReplyOsm(QObject *parent = 0); + QGeoRouteReplyOsm(QNetworkReply *reply, const QGeoRouteRequest &request, QObject *parent = 0); + ~QGeoRouteReplyOsm(); + +private Q_SLOTS: + void networkReplyFinished(); + void networkReplyError(QNetworkReply::NetworkError error); +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTEREPLYOSM_H + diff --git a/src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.cpp new file mode 100644 index 0000000..12db22a --- /dev/null +++ b/src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoroutingmanagerengineosm.h" +#include "qgeoroutereplyosm.h" +#include "QtLocation/private/qgeorouteparserosrmv4_p.h" +#include "QtLocation/private/qgeorouteparserosrmv5_p.h" + +#include + +#include + +QGeoRoutingManagerEngineOsm::QGeoRoutingManagerEngineOsm(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) +: QGeoRoutingManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)) +{ + if (parameters.contains(QStringLiteral("osm.useragent"))) + m_userAgent = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1(); + else + m_userAgent = "Qt Location based application"; + + if (parameters.contains(QStringLiteral("osm.routing.host"))) + m_urlPrefix = parameters.value(QStringLiteral("osm.routing.host")).toString().toLatin1(); + else + m_urlPrefix = QStringLiteral("http://router.project-osrm.org/route/v1/driving/"); + // for v4 it was "http://router.project-osrm.org/viaroute" + + if (parameters.contains(QStringLiteral("osm.routing.apiversion")) + && (parameters.value(QStringLiteral("osm.routing.apiversion")).toString().toLatin1() == QByteArray("v4"))) + m_routeParser = new QGeoRouteParserOsrmV4(this); + else + m_routeParser = new QGeoRouteParserOsrmV5(this); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QGeoRoutingManagerEngineOsm::~QGeoRoutingManagerEngineOsm() +{ +} + +QGeoRouteReply* QGeoRoutingManagerEngineOsm::calculateRoute(const QGeoRouteRequest &request) +{ + QNetworkRequest networkRequest; + networkRequest.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + + networkRequest.setUrl(routeParser()->requestUrl(request, m_urlPrefix)); + + QNetworkReply *reply = m_networkManager->get(networkRequest); + + QGeoRouteReplyOsm *routeReply = new QGeoRouteReplyOsm(reply, request, this); + + connect(routeReply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(routeReply, SIGNAL(error(QGeoRouteReply::Error,QString)), + this, SLOT(replyError(QGeoRouteReply::Error,QString))); + + return routeReply; +} + +const QGeoRouteParser *QGeoRoutingManagerEngineOsm::routeParser() const +{ + return m_routeParser; +} + +void QGeoRoutingManagerEngineOsm::replyFinished() +{ + QGeoRouteReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void QGeoRoutingManagerEngineOsm::replyError(QGeoRouteReply::Error errorCode, + const QString &errorString) +{ + QGeoRouteReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} diff --git a/src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.h b/src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.h new file mode 100644 index 0000000..8e2d7f5 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeoroutingmanagerengineosm.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGERENGINEOSM_H +#define QGEOROUTINGMANAGERENGINEOSM_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; + +class QGeoRoutingManagerEngineOsm : public QGeoRoutingManagerEngine +{ + Q_OBJECT + +public: + QGeoRoutingManagerEngineOsm(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString); + ~QGeoRoutingManagerEngineOsm(); + + QGeoRouteReply *calculateRoute(const QGeoRouteRequest &request); + const QGeoRouteParser *routeParser() const; + +private Q_SLOTS: + void replyFinished(); + void replyError(QGeoRouteReply::Error errorCode, const QString &errorString); + +private: + QNetworkAccessManager *m_networkManager; + QGeoRouteParser *m_routeParser; + QByteArray m_userAgent; + QString m_urlPrefix; +}; + +QT_END_NAMESPACE + +#endif // QGEOROUTINGMANAGERENGINEOSM_H + diff --git a/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp b/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp new file mode 100644 index 0000000..b028a94 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderpluginosm.h" +#include "qgeotiledmappingmanagerengineosm.h" +#include "qgeocodingmanagerengineosm.h" +#include "qgeoroutingmanagerengineosm.h" +#include "qplacemanagerengineosm.h" + +QT_BEGIN_NAMESPACE + +QGeoCodingManagerEngine *QGeoServiceProviderFactoryOsm::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new QGeoCodingManagerEngineOsm(parameters, error, errorString); +} + +QGeoMappingManagerEngine *QGeoServiceProviderFactoryOsm::createMappingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new QGeoTiledMappingManagerEngineOsm(parameters, error, errorString); +} + +QGeoRoutingManagerEngine *QGeoServiceProviderFactoryOsm::createRoutingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new QGeoRoutingManagerEngineOsm(parameters, error, errorString); +} + +QPlaceManagerEngine *QGeoServiceProviderFactoryOsm::createPlaceManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) const +{ + return new QPlaceManagerEngineOsm(parameters, error, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.h b/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.h new file mode 100644 index 0000000..5e4ab3e --- /dev/null +++ b/src/plugins/geoservices/osm/qgeoserviceproviderpluginosm.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_OSM_H +#define QGEOSERVICEPROVIDER_OSM_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoServiceProviderFactoryOsm: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "osm_plugin.json") + +public: + QGeoCodingManagerEngine *createGeocodingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoMappingManagerEngine *createMappingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QGeoRoutingManagerEngine *createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/osm/qgeotiledmaposm.cpp b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp new file mode 100644 index 0000000..372d1a3 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotiledmaposm.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmaposm.h" +#include "qgeotiledmappingmanagerengineosm.h" +#include "qgeotilefetcherosm.h" + +#include + +QT_BEGIN_NAMESPACE + +QGeoTiledMapOsm::QGeoTiledMapOsm(QGeoTiledMappingManagerEngineOsm *engine, QObject *parent) +: Map(engine, parent), m_mapId(-1), m_engine(engine) +{ + // Needed because evaluateCopyrights() is only triggered if visible tiles change in the map. + // It fails the first time it gets called if providers aren't resolved, and subsequent calls + // to it will be skipped until visible tiles change. + // This connection makes sure the copyrights are evaluated when copyright data are ready regardless + // of what tiles are visible. + connect(qobject_cast(engine->tileFetcher()), &QGeoTileFetcherOsm::providerDataUpdated, + this, &QGeoTiledMapOsm::onProviderDataUpdated); +} + +QGeoTiledMapOsm::~QGeoTiledMapOsm() +{ +} + +void QGeoTiledMapOsm::evaluateCopyrights(const QSet &visibleTiles) +{ + if (visibleTiles.isEmpty()) + return; + + QGeoTileSpec tile = *visibleTiles.constBegin(); + if (tile.mapId() == m_mapId) + return; + + int providerId = tile.mapId() - 1; + if (providerId < 0 || providerId >= m_engine->providers().size()) + return; + + m_mapId = tile.mapId(); + if (!m_engine->providers().at(providerId)->isValid()) + return; + + onProviderDataUpdated(m_engine->providers().at(providerId)); +} + +void QGeoTiledMapOsm::onProviderDataUpdated(const QGeoTileProviderOsm *provider) +{ + if (!provider->isResolved() || provider->mapType().mapId() != m_mapId) + return; + QString copyRights; + const QString mapCopy = provider->mapCopyRight(); + const QString dataCopy = provider->dataCopyRight(); + const QString styleCopy = provider->styleCopyRight(); + if (!mapCopy.isEmpty()) { + copyRights += QStringLiteral("Map © "); + copyRights += mapCopy; + } + if (!dataCopy.isEmpty()) { + if (!copyRights.isEmpty()) + copyRights += QStringLiteral(" | "); + copyRights += QStringLiteral("Data © "); + copyRights += dataCopy; + } + if (!styleCopy.isEmpty()) { + if (!copyRights.isEmpty()) + copyRights += QStringLiteral(" | "); + copyRights += QStringLiteral("Style © "); + copyRights += styleCopy; + } + + if (copyRights.isEmpty() && provider->mapType().style() == QGeoMapType::CustomMap) + copyRights = m_engine->customCopyright(); + + // Update CameraCapabilities + setCameraCapabilities(provider->cameraCapabilities()); + + emit copyrightsChanged(copyRights); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeotiledmaposm.h b/src/plugins/geoservices/osm/qgeotiledmaposm.h new file mode 100644 index 0000000..cc34979 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotiledmaposm.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPOSM_H +#define QGEOTILEDMAPOSM_H + +#include "qgeotileproviderosm.h" + +#include +#ifdef LOCATIONLABS +#include +typedef QGeoTiledMapLabs Map; +#else +typedef QGeoTiledMap Map; +#endif + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEngineOsm; +class QGeoTiledMapOsm: public Map +{ + Q_OBJECT + +public: + QGeoTiledMapOsm(QGeoTiledMappingManagerEngineOsm *engine, QObject *parent = 0); + ~QGeoTiledMapOsm(); + +protected: + void evaluateCopyrights(const QSet &visibleTiles) override; + +protected Q_SLOTS: + void onProviderDataUpdated(const QGeoTileProviderOsm *provider); + +private: + int m_mapId; + QGeoTiledMappingManagerEngineOsm *m_engine; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp new file mode 100644 index 0000000..9174ad6 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmappingmanagerengineosm.h" +#include "qgeotilefetcherosm.h" +#include "qgeotiledmaposm.h" +#include "qgeofiletilecacheosm.h" + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +QGeoTiledMappingManagerEngineOsm::QGeoTiledMappingManagerEngineOsm(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) +: QGeoTiledMappingManagerEngine() +{ + QGeoCameraCapabilities cameraCaps; + cameraCaps.setMinimumZoomLevel(0.0); + cameraCaps.setMaximumZoomLevel(19.0); + cameraCaps.setSupportsBearing(true); + cameraCaps.setSupportsTilting(true); + cameraCaps.setMinimumTilt(0); + cameraCaps.setMaximumTilt(80); + cameraCaps.setMinimumFieldOfView(20.0); + cameraCaps.setMaximumFieldOfView(120.0); + cameraCaps.setOverzoomEnabled(true); + setCameraCapabilities(cameraCaps); + + setTileSize(QSize(256, 256)); + + QNetworkAccessManager *nm = new QNetworkAccessManager(); + QString domain = QStringLiteral("http://maps-redirect.qt.io/osm/5.8/"); + if (parameters.contains(QStringLiteral("osm.mapping.providersrepository.address"))) { + QString customAddress = parameters.value(QStringLiteral("osm.mapping.providersrepository.address")).toString(); + // Allowing some malformed addresses + if (customAddress.indexOf(QStringLiteral(":")) < 0) // defaulting to http:// if no prefix is found + customAddress = QStringLiteral("http://") + customAddress; + if (customAddress[customAddress.length()-1] != QLatin1Char('/')) + customAddress += QLatin1Char('/'); + if (QUrl(customAddress).isValid()) + domain = customAddress; + else + qWarning() << "Invalid custom providers repository address: " << customAddress; + } + + bool highdpi = false; + if (parameters.contains(QStringLiteral("osm.mapping.highdpi_tiles"))) { + const QString param = parameters.value(QStringLiteral("osm.mapping.highdpi_tiles")).toString().toLower(); + if (param == "true") + highdpi = true; + } + + /* TileProviders setup */ + QVector providers_street; + QVector providers_satellite; + QVector providers_cycle; + QVector providers_transit; + QVector providers_nighttransit; + QVector providers_terrain; + QVector providers_hiking; + if (highdpi) { + providers_street.push_back(new TileProvider(domain + "street-hires", true)); + providers_satellite.push_back(new TileProvider(domain + "satellite-hires", true)); + providers_cycle.push_back(new TileProvider(domain + "cycle-hires", true)); + providers_transit.push_back(new TileProvider(domain + "transit-hires", true)); + providers_nighttransit.push_back(new TileProvider(domain + "night-transit-hires", true)); + providers_terrain.push_back(new TileProvider(domain + "terrain-hires", true)); + providers_hiking.push_back(new TileProvider(domain + "hiking-hires", true)); + } + providers_street.push_back(new TileProvider(domain + "street")); + providers_satellite.push_back(new TileProvider(domain + "satellite")); + providers_cycle.push_back(new TileProvider(domain + "cycle")); + providers_transit.push_back(new TileProvider(domain + "transit")); + providers_nighttransit.push_back(new TileProvider(domain + "night-transit")); + providers_terrain.push_back(new TileProvider(domain + "terrain")); + providers_hiking.push_back(new TileProvider(domain + "hiking")); + // Backups + const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate); + providers_street.push_back( + new TileProvider(QStringLiteral("http://c.tile.openstreetmap.org/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("OpenStreetMap.org"), + QStringLiteral("OpenStreetMap contributors"))); + providers_street.back()->setTimestamp(defaultTs); + + // No available open access satellite backup with satisfactory level of details at the present. + + providers_cycle.push_back( + new TileProvider(QStringLiteral("http://c.tile.opencyclemap.org/cycle/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("Thunderforest"), + QStringLiteral("OpenStreetMap contributors"))); + providers_cycle.back()->setTimestamp(defaultTs); + + providers_transit.push_back( + new TileProvider(QStringLiteral("http://c.tile2.opencyclemap.org/transport/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("Thunderforest"), + QStringLiteral("OpenStreetMap contributors"))); + providers_transit.back()->setTimestamp(defaultTs); + + providers_nighttransit.push_back( + new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/transport-dark/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("Thunderforest"), + QStringLiteral("OpenStreetMap contributors")) ); + providers_nighttransit.back()->setTimestamp(defaultTs); + + providers_terrain.push_back( + new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/landscape/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("Thunderforest"), + QStringLiteral("OpenStreetMap contributors"))); + providers_terrain.back()->setTimestamp(defaultTs); + + providers_hiking.push_back( + new TileProvider(QStringLiteral("http://a.tile.thunderforest.com/outdoors/%z/%x/%y.png"), + QStringLiteral("png"), + QStringLiteral("Thunderforest"), + QStringLiteral("OpenStreetMap contributors"))); + providers_hiking.back()->setTimestamp(defaultTs); + + + /* QGeoTileProviderOsms setup */ + const QByteArray pluginName = "osm"; + m_providers.push_back( new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::StreetMap, tr("Street Map"), tr("Street map view in daylight mode"), false, false, 1, pluginName, cameraCaps), + providers_street, cameraCaps )); + m_providers.push_back( new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::SatelliteMapDay, tr("Satellite Map"), tr("Satellite map view in daylight mode"), false, false, 2, pluginName, cameraCaps), + providers_satellite, cameraCaps )); + m_providers.push_back( new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::CycleMap, tr("Cycle Map"), tr("Cycle map view in daylight mode"), false, false, 3, pluginName, cameraCaps), + providers_cycle, cameraCaps )); + m_providers.push_back( new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::TransitMap, tr("Transit Map"), tr("Public transit map view in daylight mode"), false, false, 4, pluginName, cameraCaps), + providers_transit, cameraCaps )); + m_providers.push_back( new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::TransitMap, tr("Night Transit Map"), tr("Public transit map view in night mode"), false, true, 5, pluginName, cameraCaps), + providers_nighttransit, cameraCaps )); + m_providers.push_back( new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::TerrainMap, tr("Terrain Map"), tr("Terrain map view"), false, false, 6, pluginName, cameraCaps), + providers_terrain, cameraCaps )); + m_providers.push_back( new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::PedestrianMap, tr("Hiking Map"), tr("Hiking map view"), false, false, 7, pluginName, cameraCaps), + providers_hiking, cameraCaps )); + + if (parameters.contains(QStringLiteral("osm.mapping.custom.host")) + || parameters.contains(QStringLiteral("osm.mapping.host"))) { + // Adding a custom provider + QString tmsServer; + if (parameters.contains(QStringLiteral("osm.mapping.host"))) + tmsServer = parameters.value(QStringLiteral("osm.mapping.host")).toString(); + if (parameters.contains(QStringLiteral("osm.mapping.custom.host"))) // priority to the new one + tmsServer = parameters.value(QStringLiteral("osm.mapping.custom.host")).toString(); + + QString mapCopyright; + QString dataCopyright; + if (parameters.contains(QStringLiteral("osm.mapping.custom.mapcopyright"))) + mapCopyright = parameters.value(QStringLiteral("osm.mapping.custom.mapcopyright")).toString(); + if (parameters.contains(QStringLiteral("osm.mapping.custom.datacopyright"))) + dataCopyright = parameters.value(QStringLiteral("osm.mapping.custom.datacopyright")).toString(); + + if (parameters.contains(QStringLiteral("osm.mapping.copyright"))) + m_customCopyright = parameters.value(QStringLiteral("osm.mapping.copyright")).toString(); + + m_providers.push_back( + new QGeoTileProviderOsm( nm, + QGeoMapType(QGeoMapType::CustomMap, tr("Custom URL Map"), tr("Custom url map view set via urlprefix parameter"), false, false, 8, pluginName, cameraCaps), + { new TileProvider(tmsServer + QStringLiteral("%z/%x/%y.png"), + QStringLiteral("png"), + mapCopyright, + dataCopyright) }, cameraCaps + )); + + m_providers.last()->disableRedirection(); + } + + bool disableRedirection = false; + if (parameters.contains(QStringLiteral("osm.mapping.providersrepository.disabled"))) + disableRedirection = parameters.value(QStringLiteral("osm.mapping.providersrepository.disabled")).toBool(); + + for (QGeoTileProviderOsm * provider: qAsConst(m_providers)) { + // Providers are parented inside QGeoFileTileCacheOsm, as they are used in its destructor. + if (disableRedirection) { + provider->disableRedirection(); + } else { + connect(provider, &QGeoTileProviderOsm::resolutionFinished, + this, &QGeoTiledMappingManagerEngineOsm::onProviderResolutionFinished); + connect(provider, &QGeoTileProviderOsm::resolutionError, + this, &QGeoTiledMappingManagerEngineOsm::onProviderResolutionError); + } + } + updateMapTypes(); + + + /* TILE CACHE */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.directory"))) { + m_cacheDirectory = parameters.value(QStringLiteral("osm.mapping.cache.directory")).toString(); + } else { + // managerName() is not yet set, we have to hardcode the plugin name below + m_cacheDirectory = QAbstractGeoTileCache::baseLocationCacheDirectory() + QLatin1String(pluginName); + } + if (parameters.contains(QStringLiteral("osm.mapping.offline.directory"))) + m_offlineDirectory = parameters.value(QStringLiteral("osm.mapping.offline.directory")).toString(); + QGeoFileTileCacheOsm *tileCache = new QGeoFileTileCacheOsm(m_providers, m_offlineDirectory, m_cacheDirectory); + + /* + * Disk cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.disk.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyDisk(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyDisk(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("osm.mapping.cache.disk.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.disk.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxDiskUsage(cacheSize); + } + + /* + * Memory cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.memory.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyMemory(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyMemory(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("osm.mapping.cache.memory.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.memory.size")).toString().toInt(&ok); + if (ok) + tileCache->setMaxMemoryUsage(cacheSize); + } + + /* + * Texture cache setup -- defaults to ByteSize (old behavior) + */ + if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.cost_strategy"))) { + QString cacheStrategy = parameters.value(QStringLiteral("osm.mapping.cache.texture.cost_strategy")).toString().toLower(); + if (cacheStrategy == QLatin1String("bytesize")) + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + else + tileCache->setCostStrategyTexture(QGeoFileTileCache::Unitary); + } else { + tileCache->setCostStrategyTexture(QGeoFileTileCache::ByteSize); + } + if (parameters.contains(QStringLiteral("osm.mapping.cache.texture.size"))) { + bool ok = false; + int cacheSize = parameters.value(QStringLiteral("osm.mapping.cache.texture.size")).toString().toInt(&ok); + if (ok) + tileCache->setExtraTextureUsage(cacheSize); + } + + + setTileCache(tileCache); + + + /* TILE FETCHER */ + QGeoTileFetcherOsm *tileFetcher = new QGeoTileFetcherOsm(m_providers, nm, this); + if (parameters.contains(QStringLiteral("osm.useragent"))) { + const QByteArray ua = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1(); + tileFetcher->setUserAgent(ua); + } + setTileFetcher(tileFetcher); + + /* PREFETCHING */ + if (parameters.contains(QStringLiteral("osm.mapping.prefetching_style"))) { + const QString prefetchingMode = parameters.value(QStringLiteral("osm.mapping.prefetching_style")).toString(); + if (prefetchingMode == QStringLiteral("TwoNeighbourLayers")) + m_prefetchStyle = QGeoTiledMap::PrefetchTwoNeighbourLayers; + else if (prefetchingMode == QStringLiteral("OneNeighbourLayer")) + m_prefetchStyle = QGeoTiledMap::PrefetchNeighbourLayer; + else if (prefetchingMode == QStringLiteral("NoPrefetching")) + m_prefetchStyle = QGeoTiledMap::NoPrefetching; + } + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QGeoTiledMappingManagerEngineOsm::~QGeoTiledMappingManagerEngineOsm() +{ +} + +QGeoMap *QGeoTiledMappingManagerEngineOsm::createMap() +{ + QGeoTiledMap *map = new QGeoTiledMapOsm(this); + connect(qobject_cast(tileCache()), &QGeoFileTileCacheOsm::mapDataUpdated + , map, &QGeoTiledMap::clearScene); + map->setPrefetchStyle(m_prefetchStyle); + return map; +} + +const QVector &QGeoTiledMappingManagerEngineOsm::providers() +{ + return m_providers; +} + +QString QGeoTiledMappingManagerEngineOsm::customCopyright() const +{ + return m_customCopyright; +} + +void QGeoTiledMappingManagerEngineOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider) +{ + if (!provider->isResolved()) + return; + updateMapTypes(); +} + +void QGeoTiledMappingManagerEngineOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider) +{ + if (!provider->isResolved()) + return; + updateMapTypes(); +} + +void QGeoTiledMappingManagerEngineOsm::updateMapTypes() +{ + QList mapTypes; + foreach (QGeoTileProviderOsm * provider, m_providers) { + // assume provider are ok until they have been resolved invalid + if (!provider->isResolved() || provider->isValid()) + mapTypes << provider->mapType(); + } + const QList currentlySupportedMapTypes = supportedMapTypes(); + if (currentlySupportedMapTypes != mapTypes) + // See map type implementations in QGeoTiledMapOsm and QGeoTileFetcherOsm. + setSupportedMapTypes(mapTypes); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h new file mode 100644 index 0000000..12290c8 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPPINGMANAGERENGINEOSM_H +#define QGEOTILEDMAPPINGMANAGERENGINEOSM_H + +#include "qgeotileproviderosm.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoTiledMappingManagerEngineOsm : public QGeoTiledMappingManagerEngine +{ + Q_OBJECT + + friend class QGeoTiledMapOsm; +public: + QGeoTiledMappingManagerEngineOsm(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString); + ~QGeoTiledMappingManagerEngineOsm(); + + QGeoMap *createMap() override; + const QVector &providers(); + QString customCopyright() const; + +protected Q_SLOTS: + void onProviderResolutionFinished(const QGeoTileProviderOsm *provider); + void onProviderResolutionError(const QGeoTileProviderOsm *provider); + +protected: + void updateMapTypes(); + +private: + QVector m_providers; + QString m_customCopyright; + QString m_cacheDirectory; + QString m_offlineDirectory; +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEDMAPPINGMANAGERENGINEOSM_H diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp new file mode 100644 index 0000000..135654a --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotilefetcherosm.h" +#include "qgeomapreplyosm.h" + +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +static bool providersResolved(const QVector &providers) +{ + foreach (const QGeoTileProviderOsm *provider, providers) + if (!provider->isResolved()) + return false; + return true; +} + +class QGeoTileFetcherOsmPrivate : public QGeoTileFetcherPrivate +{ + Q_DECLARE_PUBLIC(QGeoTileFetcherOsm) +public: + QGeoTileFetcherOsmPrivate(); + virtual ~QGeoTileFetcherOsmPrivate(); + +private: + Q_DISABLE_COPY(QGeoTileFetcherOsmPrivate) +}; + +QGeoTileFetcherOsmPrivate::QGeoTileFetcherOsmPrivate() : QGeoTileFetcherPrivate() +{ +} + +QGeoTileFetcherOsmPrivate::~QGeoTileFetcherOsmPrivate() +{ +} + + +QGeoTileFetcherOsm::QGeoTileFetcherOsm(const QVector &providers, + QNetworkAccessManager *nm, + QGeoMappingManagerEngine *parent) +: QGeoTileFetcher(*new QGeoTileFetcherOsmPrivate(), parent), m_userAgent("Qt Location based application"), + m_providers(providers), m_nm(nm), m_ready(true) +{ + m_nm->setParent(this); + foreach (QGeoTileProviderOsm *provider, m_providers) { + if (!provider->isResolved()) { + m_ready = false; + connect(provider, &QGeoTileProviderOsm::resolutionFinished, + this, &QGeoTileFetcherOsm::onProviderResolutionFinished); + connect(provider, &QGeoTileProviderOsm::resolutionError, + this, &QGeoTileFetcherOsm::onProviderResolutionError); + connect(provider, &QGeoTileProviderOsm::resolutionRequired, + this, &QGeoTileFetcherOsm::restartTimer, Qt::QueuedConnection); + provider->resolveProvider(); + } + } + if (m_ready) + readyUpdated(); +} + +void QGeoTileFetcherOsm::setUserAgent(const QByteArray &userAgent) +{ + m_userAgent = userAgent; +} + +bool QGeoTileFetcherOsm::initialized() const +{ + if (!m_ready) { + foreach (QGeoTileProviderOsm *provider, m_providers) + if (!provider->isResolved()) + provider->resolveProvider(); + } + return m_ready; +} + +void QGeoTileFetcherOsm::onProviderResolutionFinished(const QGeoTileProviderOsm *provider) +{ + if ((m_ready = providersResolved(m_providers))) { + qWarning("QGeoTileFetcherOsm: all providers resolved"); + readyUpdated(); + } + emit providerDataUpdated(provider); +} + +void QGeoTileFetcherOsm::onProviderResolutionError(const QGeoTileProviderOsm *provider) +{ + if ((m_ready = providersResolved(m_providers))) { + qWarning("QGeoTileFetcherOsm: all providers resolved"); + readyUpdated(); + } + emit providerDataUpdated(provider); +} + +void QGeoTileFetcherOsm::restartTimer() +{ + Q_D(QGeoTileFetcherOsm); + + if (!d->queue_.isEmpty()) + d->timer_.start(0, this); +} + +QGeoTiledMapReply *QGeoTileFetcherOsm::getTileImage(const QGeoTileSpec &spec) +{ + int id = spec.mapId(); + if (id < 1 || id > m_providers.size()) { + qWarning("Unknown map id %d\n", spec.mapId()); + if (m_providers.isEmpty()) + return nullptr; + else + id = 1; + } + id -= 1; // TODO: make OSM map ids start from 0. + + if (spec.zoom() > m_providers[id]->maximumZoomLevel() || spec.zoom() < m_providers[id]->minimumZoomLevel()) + return nullptr; + + const QUrl url = m_providers[id]->tileAddress(spec.x(), spec.y(), spec.zoom()); + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, m_userAgent); + request.setUrl(url); + QNetworkReply *reply = m_nm->get(request); + return new QGeoMapReplyOsm(reply, spec, m_providers[id]->format()); +} + +void QGeoTileFetcherOsm::readyUpdated() +{ + updateTileRequests(QSet(), QSet()); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeotilefetcherosm.h b/src/plugins/geoservices/osm/qgeotilefetcherosm.h new file mode 100644 index 0000000..682ff68 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotilefetcherosm.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEFETCHEROSM_H +#define QGEOTILEFETCHEROSM_H + +#include "qgeotileproviderosm.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; +class QGeoTileFetcherOsmPrivate; + +class QGeoTileFetcherOsm : public QGeoTileFetcher +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoTileFetcherOsm) + + friend class QGeoMapReplyOsm; + friend class QGeoTiledMappingManagerEngineOsm; +public: + QGeoTileFetcherOsm(const QVector &providers, + QNetworkAccessManager *nm, + QGeoMappingManagerEngine *parent); + + void setUserAgent(const QByteArray &userAgent); + +Q_SIGNALS: + void providerDataUpdated(const QGeoTileProviderOsm *provider); + +protected: + bool initialized() const override; + +protected Q_SLOTS: + void onProviderResolutionFinished(const QGeoTileProviderOsm *provider); + void onProviderResolutionError(const QGeoTileProviderOsm *provider); + void restartTimer(); + +private: + QGeoTiledMapReply *getTileImage(const QGeoTileSpec &spec) override; + void readyUpdated(); + + QByteArray m_userAgent; + QVector m_providers; + QNetworkAccessManager *m_nm; + bool m_ready; +}; + +QT_END_NAMESPACE + +#endif // QGEOTILEFETCHEROSM_H + diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.cpp b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp new file mode 100644 index 0000000..f7ab8c9 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotileproviderosm.cpp @@ -0,0 +1,644 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotileproviderosm.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const int maxValidZoom = 30; +static const QDateTime defaultTs = QDateTime::fromString(QStringLiteral("2016-06-01T00:00:00"), Qt::ISODate); + +QGeoTileProviderOsm::QGeoTileProviderOsm(QNetworkAccessManager *nm, + const QGeoMapType &mapType, + const QVector &providers, + const QGeoCameraCapabilities &cameraCapabilities) +: m_nm(nm), m_provider(nullptr), m_mapType(mapType), m_status(Idle), m_cameraCapabilities(cameraCapabilities) +{ + for (int i = 0; i < providers.size(); ++i) { + TileProvider *p = providers[i]; + if (!m_provider) + m_providerId = i; + addProvider(p); + } + + if (!m_provider || m_provider->isValid()) + m_status = Resolved; + + connect(this, &QGeoTileProviderOsm::resolutionFinished, this, &QGeoTileProviderOsm::updateCameraCapabilities); +} + +QGeoTileProviderOsm::~QGeoTileProviderOsm() +{ +} + +QUrl QGeoTileProviderOsm::tileAddress(int x, int y, int z) const +{ + if (m_status != Resolved || !m_provider) + return QUrl(); + return m_provider->tileAddress(x, y, z); +} + +QString QGeoTileProviderOsm::mapCopyRight() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->mapCopyRight(); +} + +QString QGeoTileProviderOsm::dataCopyRight() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->dataCopyRight(); +} + +QString QGeoTileProviderOsm::styleCopyRight() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->styleCopyRight(); +} + +QString QGeoTileProviderOsm::format() const +{ + if (m_status != Resolved || !m_provider) + return QString(); + return m_provider->format(); +} + +int QGeoTileProviderOsm::minimumZoomLevel() const +{ + if (m_status != Resolved || !m_provider) + return 0; + return m_provider->minimumZoomLevel(); +} + +int QGeoTileProviderOsm::maximumZoomLevel() const +{ + if (m_status != Resolved || !m_provider) + return 20; + return m_provider->maximumZoomLevel(); +} + +bool QGeoTileProviderOsm::isHighDpi() const +{ + if (!m_provider) + return false; + return m_provider->isHighDpi(); +} + +const QDateTime QGeoTileProviderOsm::timestamp() const +{ + if (!m_provider) + return QDateTime(); + return m_provider->timestamp(); +} + +QGeoCameraCapabilities QGeoTileProviderOsm::cameraCapabilities() const +{ + return m_cameraCapabilities; +} + +const QGeoMapType &QGeoTileProviderOsm::mapType() const +{ + return m_mapType; +} + +bool QGeoTileProviderOsm::isValid() const +{ + if (m_status != Resolved || !m_provider) + return false; + return m_provider->isValid(); +} + +bool QGeoTileProviderOsm::isResolved() const +{ + return (m_status == Resolved); +} + +void QGeoTileProviderOsm::resolveProvider() +{ + if (m_status == Resolved || m_status == Resolving) + return; + + m_status = Resolving; + // Provider can't be null while on Idle status. + connect(m_provider, &TileProvider::resolutionFinished, this, &QGeoTileProviderOsm::onResolutionFinished); + connect(m_provider, &TileProvider::resolutionError, this, &QGeoTileProviderOsm::onResolutionError); + m_provider->resolveProvider(); +} + +void QGeoTileProviderOsm::disableRedirection() +{ + if (m_provider && m_provider->isValid()) + return; + bool found = false; + for (TileProvider *p: m_providerList) { + if (p->isValid() && !found) { + m_provider = p; + m_providerId = m_providerList.indexOf(p); + found = true; + } + p->disconnect(this); + } + m_status = Resolved; +} + +void QGeoTileProviderOsm::onResolutionFinished(TileProvider *provider) +{ + Q_UNUSED(provider) + // provider and m_provider are the same, at this point. m_status is Resolving. + m_status = Resolved; + emit resolutionFinished(this); +} + +void QGeoTileProviderOsm::onResolutionError(TileProvider *provider) +{ + Q_UNUSED(provider) + // provider and m_provider are the same at this point. m_status is Resolving. + if (!m_provider || m_provider->isInvalid()) { + m_provider = nullptr; + m_status = Resolved; + if (m_providerId >= m_providerList.size() -1) { // no hope left + emit resolutionError(this); + return; + } + // Advance the pointer in the provider list, and possibly start resolution on the next in the list. + for (int i = m_providerId + 1; i < m_providerList.size(); ++i) { + m_providerId = i; + TileProvider *p = m_providerList[m_providerId]; + if (!p->isInvalid()) { + m_provider = p; + if (!p->isValid()) { + m_status = Idle; +#if 0 // leaving triggering the retry to the tile fetcher, instead of constantly spinning it in here. + m_status = Resolving; + p->resolveProvider(); +#endif + emit resolutionRequired(); + } + break; + } + } + if (!m_provider) + emit resolutionError(this); + } else if (m_provider->isValid()) { + m_status = Resolved; + emit resolutionFinished(this); + } else { // still not resolved. But network error is recoverable. + m_status = Idle; +#if 0 // leaving triggering the retry to the tile fetcher + m_provider->resolveProvider(); +#endif + } +} + +void QGeoTileProviderOsm::updateCameraCapabilities() +{ + // Set proper min/max ZoomLevel coming from the json, if available. + m_cameraCapabilities.setMinimumZoomLevel(minimumZoomLevel()); + m_cameraCapabilities.setMaximumZoomLevel(maximumZoomLevel()); + + m_mapType = QGeoMapType(m_mapType.style(), m_mapType.name(), m_mapType.description(), m_mapType.mobile(), + m_mapType.night(), m_mapType.mapId(), m_mapType.pluginName(), m_cameraCapabilities); +} + +void QGeoTileProviderOsm::addProvider(TileProvider *provider) +{ + if (!provider) + return; + QScopedPointer p(provider); + if (provider->status() == TileProvider::Invalid) + return; // if the provider is already resolved and invalid, no point in adding it. + + provider = p.take(); + provider->setNetworkManager(m_nm); + provider->setParent(this); + m_providerList.append(provider); + if (!m_provider) + m_provider = provider; +} + + +/* + Class TileProvder +*/ + +static void sort2(int &a, int &b) +{ + if (a > b) { + int temp=a; + a=b; + b=temp; + } +} + +TileProvider::TileProvider() : m_status(Invalid), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(false) +{ + +} + +TileProvider::TileProvider(const QUrl &urlRedirector, bool highDpi) +: m_status(Idle), m_urlRedirector(urlRedirector), m_nm(nullptr), m_timestamp(defaultTs), m_highDpi(highDpi) +{ + if (!m_urlRedirector.isValid()) + m_status = Invalid; +} + +TileProvider::TileProvider(const QString &urlTemplate, + const QString &format, + const QString ©RightMap, + const QString ©RightData, + bool highDpi, + int minimumZoomLevel, + int maximumZoomLevel) +: m_status(Invalid), m_nm(nullptr), m_urlTemplate(urlTemplate), + m_format(format), m_copyRightMap(copyRightMap), m_copyRightData(copyRightData), + m_minimumZoomLevel(minimumZoomLevel), m_maximumZoomLevel(maximumZoomLevel), m_timestamp(defaultTs), m_highDpi(highDpi) +{ + setupProvider(); +} + +TileProvider::~TileProvider() +{ +} + +void TileProvider::resolveProvider() +{ + if (!m_nm) + return; + + switch (m_status) { + case Resolving: + case Invalid: + case Valid: + return; + case Idle: + m_status = Resolving; + break; + } + + QNetworkRequest request; + request.setHeader(QNetworkRequest::UserAgentHeader, QByteArrayLiteral("QGeoTileFetcherOsm")); + request.setUrl(m_urlRedirector); + request.setAttribute(QNetworkRequest::BackgroundRequestAttribute, true); + QNetworkReply *reply = m_nm->get(request); + connect(reply, SIGNAL(finished()), this, SLOT(onNetworkReplyFinished()) ); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(onNetworkReplyError(QNetworkReply::NetworkError))); +} + +void TileProvider::handleError(QNetworkReply::NetworkError error) +{ + switch (error) { + case QNetworkReply::ConnectionRefusedError: + case QNetworkReply::TooManyRedirectsError: + case QNetworkReply::InsecureRedirectError: + case QNetworkReply::ContentAccessDenied: + case QNetworkReply::ContentOperationNotPermittedError: + case QNetworkReply::ContentNotFoundError: + case QNetworkReply::AuthenticationRequiredError: + case QNetworkReply::ContentGoneError: + case QNetworkReply::OperationNotImplementedError: + case QNetworkReply::ServiceUnavailableError: + // Errors we don't expect to recover from in the near future, which + // prevent accessing the redirection info but not the actual providers. + m_status = Invalid; + default: + //qWarning() << "QGeoTileProviderOsm network error:" << error; + break; + } +} + +void TileProvider::onNetworkReplyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + switch (m_status) { + case Resolving: + m_status = Idle; + case Idle: // should not happen + case Invalid: // should not happen + break; + case Valid: // should not happen + emit resolutionFinished(this); + return; + } + + QObject errorEmitter; + QMetaObject::Connection errorEmitterConnection = connect(&errorEmitter, &QObject::destroyed, [this](){ this->resolutionError(this); }); + + if (reply->error() != QNetworkReply::NoError) { + handleError(reply->error()); + return; + } + m_status = Invalid; + + /* + * The content of a provider information file must be in JSON format, containing + * (as of Qt 5.6.2) the following fields: + * + * { + * "Enabled" : bool, (optional) + * "UrlTemplate" : "", (mandatory) + * "ImageFormat" : "", (mandatory) + * "MapCopyRight" : "", (mandatory) + * "DataCopyRight" : "", (mandatory) + * "StyleCopyRight" : "", (optional) + * "MinimumZoomLevel" : , (optional) + * "MaximumZoomLevel" : , (optional) + * "Timestamp" : , (optional) + * } + * + * Enabled is optional, and allows to temporarily disable a tile provider if it becomes + * unavailable, without making the osm plugin fire requests to it. Default is true. + * + * MinimumZoomLevel and MaximumZoomLevel are also optional, and allow to prevent invalid tile + * requests to the providers, if they do not support the specific ZL. Default is 0 and 20, + * respectively. + * + * UrlTemplate is required, and is the tile url template, with %x, %y and %z as + * placeholders for the actual parameters. + * Example: + * http://localhost:8080/maps/%z/%x/%y.png + * + * ImageFormat is required, and is the format of the tile. + * Examples: + * "png", "jpg" + * + * MapCopyRight is required and is the string that will be displayed in the "Map (c)" part + * of the on-screen copyright notice. Can be an empty string. + * Example: + * "MapQuest" + * + * DataCopyRight is required and is the string that will be displayed in the "Data (c)" part + * of the on-screen copyright notice. Can be an empty string. + * Example: + * "OpenStreetMap contributors" + * + * StyleCopyRight is optional and is the string that will be displayed in the optional "Style (c)" part + * of the on-screen copyright notice. + * + * Timestamp is optional, and if set will cause QtLocation to clear the content of the cache older + * than this timestamp. The purpose is to prevent mixing tiles from different providers in the cache + * upon provider change. The value must be a string in ISO 8601 format (see Qt::ISODate) + */ + + QJsonParseError error; + QJsonDocument d = QJsonDocument::fromJson(reply->readAll(), &error); + if (error.error != QJsonParseError::NoError) { + qWarning() << "QGeoTileProviderOsm: Error parsing redirection data: "<(sender())->deleteLater(); + emit resolutionError(this); +} + +void TileProvider::setupProvider() +{ + if (m_urlTemplate.isEmpty()) + return; + + if (m_format.isEmpty()) + return; + + if (m_minimumZoomLevel < 0 || m_minimumZoomLevel > 30) + return; + + if (m_maximumZoomLevel < 0 || m_maximumZoomLevel > 30 || m_maximumZoomLevel < m_minimumZoomLevel) + return; + + // Currently supporting only %x, %y and &z + int offset[3]; + offset[0] = m_urlTemplate.indexOf(QLatin1String("%x")); + if (offset[0] < 0) + return; + + offset[1] = m_urlTemplate.indexOf(QLatin1String("%y")); + if (offset[1] < 0) + return; + + offset[2] = m_urlTemplate.indexOf(QLatin1String("%z")); + if (offset[2] < 0) + return; + + int sortedOffsets[3]; + std::copy(offset, offset + 3, sortedOffsets); + sort2(sortedOffsets[0] ,sortedOffsets[1]); + sort2(sortedOffsets[1] ,sortedOffsets[2]); + sort2(sortedOffsets[0] ,sortedOffsets[1]); + + int min = sortedOffsets[0]; + int max = sortedOffsets[2]; + int mid = sortedOffsets[1]; + + // Initing LUT + for (int i=0; i<3; i++) { + if (offset[0] == sortedOffsets[i]) + paramsLUT[i] = 0; + else if (offset[1] == sortedOffsets[i]) + paramsLUT[i] = 1; + else + paramsLUT[i] = 2; + } + + m_urlPrefix = m_urlTemplate.mid(0 , min); + m_urlSuffix = m_urlTemplate.mid(max + 2, m_urlTemplate.size() - max - 2); + + paramsSep[0] = m_urlTemplate.mid(min + 2, mid - min - 2); + paramsSep[1] = m_urlTemplate.mid(mid + 2, max - mid - 2); + m_status = Valid; +} + +bool TileProvider::isValid() const +{ + return m_status == Valid; +} + +bool TileProvider::isInvalid() const +{ + return m_status == Invalid; +} + +bool TileProvider::isResolved() const +{ + return (m_status == Valid || m_status == Invalid); +} + +QString TileProvider::mapCopyRight() const +{ + return m_copyRightMap; +} + +QString TileProvider::dataCopyRight() const +{ + return m_copyRightData; +} + +QString TileProvider::styleCopyRight() const +{ + return m_copyRightStyle; +} + +QString TileProvider::format() const +{ + return m_format; +} + +int TileProvider::minimumZoomLevel() const +{ + return m_minimumZoomLevel; +} + +int TileProvider::maximumZoomLevel() const +{ + return m_maximumZoomLevel; +} + +const QDateTime &TileProvider::timestamp() const +{ + return m_timestamp; +} + +bool TileProvider::isHighDpi() const +{ + return m_highDpi; +} + +void TileProvider::setStyleCopyRight(const QString ©right) +{ + m_copyRightStyle = copyright; +} + +void TileProvider::setTimestamp(const QDateTime ×tamp) +{ + m_timestamp = timestamp; +} + +QUrl TileProvider::tileAddress(int x, int y, int z) const +{ + if (z < m_minimumZoomLevel || z > m_maximumZoomLevel) + return QUrl(); + int params[3] = { x, y, z}; + QString url; + url += m_urlPrefix; + url += QString::number(params[paramsLUT[0]]); + url += paramsSep[0]; + url += QString::number(params[paramsLUT[1]]); + url += paramsSep[1]; + url += QString::number(params[paramsLUT[2]]); + url += m_urlSuffix; + return QUrl(url); +} + +void TileProvider::setNetworkManager(QNetworkAccessManager *nm) +{ + m_nm = nm; +} + +TileProvider::Status TileProvider::status() const +{ + return m_status; +} + + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qgeotileproviderosm.h b/src/plugins/geoservices/osm/qgeotileproviderosm.h new file mode 100644 index 0000000..54f8049 --- /dev/null +++ b/src/plugins/geoservices/osm/qgeotileproviderosm.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTILEPROVIDEROSM_H +#define QTILEPROVIDEROSM_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class TileProvider: public QObject +{ + Q_OBJECT +public: + enum Status {Idle, + Resolving, + Valid, + Invalid }; + + TileProvider(); + // "Online" constructor. Needs resolution to fetch the parameters + TileProvider(const QUrl &urlRedirector, bool highDpi = false); + // Offline constructor. Doesn't need URLRedirector and networkmanager + TileProvider(const QString &urlTemplate, + const QString &format, + const QString ©RightMap, + const QString ©RightData, + bool highDpi = false, + int minimumZoomLevel = 0, + int maximumZoomLevel = 19); + + ~TileProvider(); + void setNetworkManager(QNetworkAccessManager *nm); + + void resolveProvider(); + void handleError(QNetworkReply::NetworkError error); + void setupProvider(); + + inline bool isValid() const; + inline bool isInvalid() const; + inline bool isResolved() const; + inline Status status() const; + + inline QString mapCopyRight() const; + inline QString dataCopyRight() const; + inline QString styleCopyRight() const; + inline QString format() const; + inline int minimumZoomLevel() const; + inline int maximumZoomLevel() const; + inline const QDateTime ×tamp() const; + inline bool isHighDpi() const; + QUrl tileAddress(int x, int y, int z) const; + + // Optional properties, not needed to construct a provider + void setStyleCopyRight(const QString ©right); + void setTimestamp(const QDateTime ×tamp); + + Status m_status; + QUrl m_urlRedirector; // The URL from where to fetch the URL template in case of a provider to resolve. + QNetworkAccessManager *m_nm; + QString m_urlTemplate; + QString m_format; + QString m_copyRightMap; + QString m_copyRightData; + QString m_copyRightStyle; + QString m_urlPrefix; + QString m_urlSuffix; + int m_minimumZoomLevel; + int m_maximumZoomLevel; + QDateTime m_timestamp; + bool m_highDpi; + + int paramsLUT[3]; //Lookup table to handle possibly shuffled x,y,z + QString paramsSep[2]; // what goes in between %x, %y and %z + +Q_SIGNALS: + void resolutionFinished(TileProvider *provider); + void resolutionError(TileProvider *provider); + +public Q_SLOTS: + void onNetworkReplyFinished(); + void onNetworkReplyError(QNetworkReply::NetworkError error); + +friend class QGeoTileProviderOsm; +}; + +class QGeoTileProviderOsm: public QObject +{ + Q_OBJECT + + friend class QGeoTileFetcherOsm; + friend class QGeoMapReplyOsm; + friend class QGeoTiledMappingManagerEngineOsm; +public: + enum Status {Idle, + Resolving, + Resolved }; + + QGeoTileProviderOsm(QNetworkAccessManager *nm, + const QGeoMapType &mapType, + const QVector &providers, + const QGeoCameraCapabilities &cameraCapabilities); + ~QGeoTileProviderOsm(); + + QUrl tileAddress(int x, int y, int z) const; + QString mapCopyRight() const; + QString dataCopyRight() const; + QString styleCopyRight() const; + QString format() const; + int minimumZoomLevel() const; + int maximumZoomLevel() const; + bool isHighDpi() const; + const QGeoMapType &mapType() const; + bool isValid() const; + bool isResolved() const; + const QDateTime timestamp() const; + QGeoCameraCapabilities cameraCapabilities() const; + +Q_SIGNALS: + void resolutionFinished(const QGeoTileProviderOsm *provider); + void resolutionError(const QGeoTileProviderOsm *provider); + void resolutionRequired(); + +public Q_SLOTS: + void resolveProvider(); + void disableRedirection(); + +protected Q_SLOTS: + void onResolutionFinished(TileProvider *provider); + void onResolutionError(TileProvider *provider); + void updateCameraCapabilities(); + +protected: + void addProvider(TileProvider *provider); + +/* Data members */ + + QNetworkAccessManager *m_nm; + QVector m_providerList; + TileProvider *m_provider; + int m_providerId; + QGeoMapType m_mapType; + Status m_status; + QGeoCameraCapabilities m_cameraCapabilities; +}; + +QT_END_NAMESPACE + +#endif // QTILEPROVIDEROSM_H diff --git a/src/plugins/geoservices/osm/qplacecategoriesreplyosm.cpp b/src/plugins/geoservices/osm/qplacecategoriesreplyosm.cpp new file mode 100644 index 0000000..fe506bb --- /dev/null +++ b/src/plugins/geoservices/osm/qplacecategoriesreplyosm.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacecategoriesreplyosm.h" + +QT_BEGIN_NAMESPACE + +QPlaceCategoriesReplyOsm::QPlaceCategoriesReplyOsm(QObject *parent) +: QPlaceReply(parent) +{ +} + +QPlaceCategoriesReplyOsm::~QPlaceCategoriesReplyOsm() +{ +} + +void QPlaceCategoriesReplyOsm::emitFinished() +{ + setFinished(true); + emit finished(); +} + +void QPlaceCategoriesReplyOsm::setError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply::setError(errorCode, errorString); + emit error(errorCode, errorString); +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qplacecategoriesreplyosm.h b/src/plugins/geoservices/osm/qplacecategoriesreplyosm.h new file mode 100644 index 0000000..af2919d --- /dev/null +++ b/src/plugins/geoservices/osm/qplacecategoriesreplyosm.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACECATEGORIESREPLYOSM_H +#define QPLACECATEGORIESREPLYOSM_H + +#include + +QT_BEGIN_NAMESPACE + +class QPlaceCategoriesReplyOsm : public QPlaceReply +{ + Q_OBJECT + +public: + explicit QPlaceCategoriesReplyOsm(QObject *parent = 0); + ~QPlaceCategoriesReplyOsm(); + + void emitFinished(); + void setError(QPlaceReply::Error errorCode, const QString &errorString); +}; + +QT_END_NAMESPACE + +#endif // QPLACECATEGORIESREPLYOSM_H diff --git a/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp new file mode 100644 index 0000000..3c201e4 --- /dev/null +++ b/src/plugins/geoservices/osm/qplacemanagerengineosm.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacemanagerengineosm.h" +#include "qplacesearchreplyosm.h" +#include "qplacecategoriesreplyosm.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ +QString SpecialPhrasesBaseUrl = QStringLiteral("http://wiki.openstreetmap.org/wiki/Special:Export/Nominatim/Special_Phrases/"); + +QString nameForTagKey(const QString &tagKey) +{ + if (tagKey == QLatin1String("aeroway")) + return QPlaceManagerEngineOsm::tr("Aeroway"); + else if (tagKey == QLatin1String("amenity")) + return QPlaceManagerEngineOsm::tr("Amenity"); + else if (tagKey == QLatin1String("building")) + return QPlaceManagerEngineOsm::tr("Building"); + else if (tagKey == QLatin1String("highway")) + return QPlaceManagerEngineOsm::tr("Highway"); + else if (tagKey == QLatin1String("historic")) + return QPlaceManagerEngineOsm::tr("Historic"); + else if (tagKey == QLatin1String("landuse")) + return QPlaceManagerEngineOsm::tr("Land use"); + else if (tagKey == QLatin1String("leisure")) + return QPlaceManagerEngineOsm::tr("Leisure"); + else if (tagKey == QLatin1String("man_made")) + return QPlaceManagerEngineOsm::tr("Man made"); + else if (tagKey == QLatin1String("natural")) + return QPlaceManagerEngineOsm::tr("Natural"); + else if (tagKey == QLatin1String("place")) + return QPlaceManagerEngineOsm::tr("Place"); + else if (tagKey == QLatin1String("railway")) + return QPlaceManagerEngineOsm::tr("Railway"); + else if (tagKey == QLatin1String("shop")) + return QPlaceManagerEngineOsm::tr("Shop"); + else if (tagKey == QLatin1String("tourism")) + return QPlaceManagerEngineOsm::tr("Tourism"); + else if (tagKey == QLatin1String("waterway")) + return QPlaceManagerEngineOsm::tr("Waterway"); + else + return tagKey; +} + +} + +QPlaceManagerEngineOsm::QPlaceManagerEngineOsm(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) +: QPlaceManagerEngine(parameters), m_networkManager(new QNetworkAccessManager(this)), + m_categoriesReply(0) +{ + if (parameters.contains(QStringLiteral("osm.useragent"))) + m_userAgent = parameters.value(QStringLiteral("osm.useragent")).toString().toLatin1(); + else + m_userAgent = "Qt Location based application"; + + if (parameters.contains(QStringLiteral("osm.places.host"))) + m_urlPrefix = parameters.value(QStringLiteral("osm.places.host")).toString(); + else + m_urlPrefix = QStringLiteral("http://nominatim.openstreetmap.org/search"); + + + if (parameters.contains(QStringLiteral("osm.places.debug_query"))) + m_debugQuery = parameters.value(QStringLiteral("osm.places.debug_query")).toBool(); + + if (parameters.contains(QStringLiteral("osm.places.page_size")) + && parameters.value(QStringLiteral("osm.places.page_size")).canConvert()) + m_pageSize = parameters.value(QStringLiteral("osm.places.page_size")).toInt(); + + *error = QGeoServiceProvider::NoError; + errorString->clear(); +} + +QPlaceManagerEngineOsm::~QPlaceManagerEngineOsm() +{ +} + +QPlaceSearchReply *QPlaceManagerEngineOsm::search(const QPlaceSearchRequest &request) +{ + bool unsupported = false; + + // Only public visibility supported + unsupported |= request.visibilityScope() != QLocation::UnspecifiedVisibility && + request.visibilityScope() != QLocation::PublicVisibility; + unsupported |= request.searchTerm().isEmpty() && request.categories().isEmpty(); + + if (unsupported) + return QPlaceManagerEngine::search(request); + + QUrlQuery queryItems; + + queryItems.addQueryItem(QStringLiteral("format"), QStringLiteral("jsonv2")); + + //queryItems.addQueryItem(QStringLiteral("accept-language"), QStringLiteral("en")); + + QGeoRectangle boundingBox = request.searchArea().boundingGeoRectangle(); + + if (!boundingBox.isEmpty()) { + queryItems.addQueryItem(QStringLiteral("bounded"), QStringLiteral("1")); + QString coordinates; + coordinates = QString::number(boundingBox.topLeft().longitude()) + QLatin1Char(',') + + QString::number(boundingBox.topLeft().latitude()) + QLatin1Char(',') + + QString::number(boundingBox.bottomRight().longitude()) + QLatin1Char(',') + + QString::number(boundingBox.bottomRight().latitude()); + queryItems.addQueryItem(QStringLiteral("viewbox"), coordinates); + } + + QStringList queryParts; + if (!request.searchTerm().isEmpty()) + queryParts.append(request.searchTerm()); + + foreach (const QPlaceCategory &category, request.categories()) { + QString id = category.categoryId(); + int index = id.indexOf(QLatin1Char('=')); + if (index != -1) + id = id.mid(index+1); + queryParts.append(QLatin1Char('[') + id + QLatin1Char(']')); + } + + queryItems.addQueryItem(QStringLiteral("q"), queryParts.join(QLatin1Char('+'))); + + QVariantMap parameters = request.searchContext().toMap(); + + QStringList placeIds = parameters.value(QStringLiteral("ExcludePlaceIds")).toStringList(); + if (!placeIds.isEmpty()) + queryItems.addQueryItem(QStringLiteral("exclude_place_ids"), placeIds.join(QLatin1Char(','))); + + queryItems.addQueryItem(QStringLiteral("addressdetails"), QStringLiteral("1")); + queryItems.addQueryItem(QStringLiteral("limit"), (request.limit() > 0) ? QString::number(request.limit()) + : QString::number(m_pageSize)); + + QUrl requestUrl(m_urlPrefix); + requestUrl.setQuery(queryItems); + + QNetworkRequest rq(requestUrl); + rq.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + QNetworkReply *networkReply = m_networkManager->get(rq); + + QPlaceSearchReplyOsm *reply = new QPlaceSearchReplyOsm(request, networkReply, this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + if (m_debugQuery) + reply->requestUrl = requestUrl.toString(); + + return reply; +} + +QPlaceReply *QPlaceManagerEngineOsm::initializeCategories() +{ + // Only fetch categories once + if (m_categories.isEmpty() && !m_categoriesReply) { + m_categoryLocales = m_locales; + m_categoryLocales.append(QLocale(QLocale::English)); + fetchNextCategoryLocale(); + } + + QPlaceCategoriesReplyOsm *reply = new QPlaceCategoriesReplyOsm(this); + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QPlaceReply::Error,QString)), + this, SLOT(replyError(QPlaceReply::Error,QString))); + + // TODO delayed finished() emission + if (!m_categories.isEmpty()) + reply->emitFinished(); + + m_pendingCategoriesReply.append(reply); + return reply; +} + +QString QPlaceManagerEngineOsm::parentCategoryId(const QString &categoryId) const +{ + Q_UNUSED(categoryId) + + // Only a two category levels + return QString(); +} + +QStringList QPlaceManagerEngineOsm::childCategoryIds(const QString &categoryId) const +{ + return m_subcategories.value(categoryId); +} + +QPlaceCategory QPlaceManagerEngineOsm::category(const QString &categoryId) const +{ + return m_categories.value(categoryId); +} + +QList QPlaceManagerEngineOsm::childCategories(const QString &parentId) const +{ + QList categories; + foreach (const QString &id, m_subcategories.value(parentId)) + categories.append(m_categories.value(id)); + return categories; +} + +QList QPlaceManagerEngineOsm::locales() const +{ + return m_locales; +} + +void QPlaceManagerEngineOsm::setLocales(const QList &locales) +{ + m_locales = locales; +} + +void QPlaceManagerEngineOsm::categoryReplyFinished() +{ + QNetworkReply *reply = qobject_cast(sender()); + reply->deleteLater(); + + QXmlStreamReader parser(reply); + while (!parser.atEnd() && parser.readNextStartElement()) { + if (parser.name() == QLatin1String("mediawiki")) + continue; + if (parser.name() == QLatin1String("page")) + continue; + if (parser.name() == QLatin1String("revision")) + continue; + if (parser.name() == QLatin1String("text")) { + // parse + QString page = parser.readElementText(); + QRegularExpression regex(QStringLiteral("\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([^|]+) \\|\\| ([\\-YN])")); + QRegularExpressionMatchIterator i = regex.globalMatch(page); + while (i.hasNext()) { + QRegularExpressionMatch match = i.next(); + QString name = match.capturedRef(1).toString(); + QString tagKey = match.capturedRef(2).toString(); + QString tagValue = match.capturedRef(3).toString(); + QString op = match.capturedRef(4).toString(); + QString plural = match.capturedRef(5).toString(); + + // Only interested in any operator plural forms + if (op != QLatin1String("-") || plural != QLatin1String("Y")) + continue; + + if (!m_categories.contains(tagKey)) { + QPlaceCategory category; + category.setCategoryId(tagKey); + category.setName(nameForTagKey(tagKey)); + m_categories.insert(category.categoryId(), category); + m_subcategories[QString()].append(tagKey); + emit categoryAdded(category, QString()); + } + + QPlaceCategory category; + category.setCategoryId(tagKey + QLatin1Char('=') + tagValue); + category.setName(name); + + if (!m_categories.contains(category.categoryId())) { + m_categories.insert(category.categoryId(), category); + m_subcategories[tagKey].append(category.categoryId()); + emit categoryAdded(category, tagKey); + } + } + } + + parser.skipCurrentElement(); + } + + if (m_categories.isEmpty() && !m_categoryLocales.isEmpty()) { + fetchNextCategoryLocale(); + return; + } else { + m_categoryLocales.clear(); + } + + foreach (QPlaceCategoriesReplyOsm *reply, m_pendingCategoriesReply) + reply->emitFinished(); + m_pendingCategoriesReply.clear(); +} + +void QPlaceManagerEngineOsm::categoryReplyError() +{ + foreach (QPlaceCategoriesReplyOsm *reply, m_pendingCategoriesReply) + reply->setError(QPlaceReply::CommunicationError, tr("Network request error")); +} + +void QPlaceManagerEngineOsm::replyFinished() +{ + QPlaceReply *reply = qobject_cast(sender()); + if (reply) + emit finished(reply); +} + +void QPlaceManagerEngineOsm::replyError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply *reply = qobject_cast(sender()); + if (reply) + emit error(reply, errorCode, errorString); +} + +void QPlaceManagerEngineOsm::fetchNextCategoryLocale() +{ + if (m_categoryLocales.isEmpty()) { + qWarning("No locales specified to fetch categories for"); + return; + } + + QLocale locale = m_categoryLocales.takeFirst(); + + // FIXME: Categories should be cached. + QUrl requestUrl = QUrl(SpecialPhrasesBaseUrl + locale.name().left(2).toUpper()); + + m_categoriesReply = m_networkManager->get(QNetworkRequest(requestUrl)); + connect(m_categoriesReply, SIGNAL(finished()), this, SLOT(categoryReplyFinished())); + connect(m_categoriesReply, SIGNAL(error(QNetworkReply::NetworkError)), + this, SLOT(categoryReplyError())); +} diff --git a/src/plugins/geoservices/osm/qplacemanagerengineosm.h b/src/plugins/geoservices/osm/qplacemanagerengineosm.h new file mode 100644 index 0000000..7758022 --- /dev/null +++ b/src/plugins/geoservices/osm/qplacemanagerengineosm.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMANAGERENGINEOSM_H +#define QPLACEMANAGERENGINEOSM_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkAccessManager; +class QNetworkReply; +class QPlaceCategoriesReplyOsm; + +class QPlaceManagerEngineOsm : public QPlaceManagerEngine +{ + Q_OBJECT + +public: + QPlaceManagerEngineOsm(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString); + ~QPlaceManagerEngineOsm(); + + QPlaceSearchReply *search(const QPlaceSearchRequest &request) override; + + QPlaceReply *initializeCategories() override; + QString parentCategoryId(const QString &categoryId) const override; + QStringList childCategoryIds(const QString &categoryId) const override; + QPlaceCategory category(const QString &categoryId) const override; + + QList childCategories(const QString &parentId) const override; + + QList locales() const override; + void setLocales(const QList &locales) override; + +private slots: + void categoryReplyFinished(); + void categoryReplyError(); + void replyFinished(); + void replyError(QPlaceReply::Error errorCode, const QString &errorString); + +private: + void fetchNextCategoryLocale(); + + QNetworkAccessManager *m_networkManager; + QByteArray m_userAgent; + QString m_urlPrefix; + QList m_locales; + bool m_debugQuery = false; + int m_pageSize = 50; // the default page size of the public nominatim server + + QNetworkReply *m_categoriesReply; + QList m_pendingCategoriesReply; + QHash m_categories; + QHash m_subcategories; + + QList m_categoryLocales; +}; + +QT_END_NAMESPACE + +#endif // QPLACEMANAGERENGINEOSM_H diff --git a/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp b/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp new file mode 100644 index 0000000..0228a97 --- /dev/null +++ b/src/plugins/geoservices/osm/qplacesearchreplyosm.cpp @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qplacesearchreplyosm.h" +#include "qplacemanagerengineosm.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QPlaceSearchReplyOsm::QPlaceSearchReplyOsm(const QPlaceSearchRequest &request, + QNetworkReply *reply, QPlaceManagerEngineOsm *parent) +: QPlaceSearchReply(parent) +{ + Q_ASSERT(parent); + if (!reply) { + setError(UnknownError, QStringLiteral("Null reply")); + return; + } + setRequest(request); + + connect(reply, SIGNAL(finished()), this, SLOT(replyFinished())); + connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), this, SLOT(networkError(QNetworkReply::NetworkError))); + connect(this, &QPlaceReply::aborted, reply, &QNetworkReply::abort); + connect(this, &QObject::destroyed, reply, &QObject::deleteLater); +} + +QPlaceSearchReplyOsm::~QPlaceSearchReplyOsm() +{ +} + +void QPlaceSearchReplyOsm::setError(QPlaceReply::Error errorCode, const QString &errorString) +{ + QPlaceReply::setError(errorCode, errorString); + emit error(errorCode, errorString); + setFinished(true); + emit finished(); +} + +static QGeoRectangle parseBoundingBox(const QJsonArray &coordinates) +{ + if (coordinates.count() != 4) + return QGeoRectangle(); + + double bottom = coordinates.at(0).toString().toDouble(); + double top = coordinates.at(1).toString().toDouble(); + double left = coordinates.at(2).toString().toDouble(); + double right = coordinates.at(3).toString().toDouble(); + + return QGeoRectangle(QGeoCoordinate(top, left), QGeoCoordinate(bottom, right)); +} + +void QPlaceSearchReplyOsm::replyFinished() +{ + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + + if (reply->error() != QNetworkReply::NoError) + return; + + QJsonDocument document = QJsonDocument::fromJson(reply->readAll()); + if (!document.isArray()) { + setError(ParseError, tr("Response parse error")); + return; + } + + QJsonArray resultsArray = document.array(); + + QGeoCoordinate searchCenter = request().searchArea().center(); + + QStringList placeIds; + + QList results; + for (int i = 0; i < resultsArray.count(); ++i) { + QJsonObject item = resultsArray.at(i).toObject(); + QPlaceResult pr = parsePlaceResult(item); + pr.setDistance(searchCenter.distanceTo(pr.place().location().coordinate())); + placeIds.append(pr.place().placeId()); + results.append(pr); + } + + QVariantMap searchContext = request().searchContext().toMap(); + QStringList excludePlaceIds = + searchContext.value(QStringLiteral("ExcludePlaceIds")).toStringList(); + + if (!excludePlaceIds.isEmpty()) { + QPlaceSearchRequest r = request(); + QVariantMap parameters = searchContext; + + QStringList epi = excludePlaceIds; + epi.removeLast(); + + parameters.insert(QStringLiteral("ExcludePlaceIds"), epi); + r.setSearchContext(parameters); + setPreviousPageRequest(r); + } + + if (!placeIds.isEmpty()) { + QPlaceSearchRequest r = request(); + QVariantMap parameters = searchContext; + + QStringList epi = excludePlaceIds; + epi.append(placeIds.join(QLatin1Char(','))); + + parameters.insert(QStringLiteral("ExcludePlaceIds"), epi); + r.setSearchContext(parameters); + setNextPageRequest(r); + } + + setResults(results); + + setFinished(true); + emit finished(); +} + +void QPlaceSearchReplyOsm::networkError(QNetworkReply::NetworkError error) +{ + Q_UNUSED(error) + QNetworkReply *reply = static_cast(sender()); + reply->deleteLater(); + setError(QPlaceReply::CommunicationError, reply->errorString()); +} + +QPlaceResult QPlaceSearchReplyOsm::parsePlaceResult(const QJsonObject &item) const +{ + QPlace place; + + QGeoCoordinate coordinate = QGeoCoordinate(item.value(QStringLiteral("lat")).toString().toDouble(), + item.value(QStringLiteral("lon")).toString().toDouble()); + + //const QString placeRank = item.value(QStringLiteral("place_rank")).toString(); + //const QString category = item.value(QStringLiteral("category")).toString(); + const QString type = item.value(QStringLiteral("type")).toString(); + //double importance = item.value(QStringLiteral("importance")).toDouble(); + + place.setAttribution(item.value(QStringLiteral("licence")).toString()); + place.setPlaceId(item.value(QStringLiteral("place_id")).toString()); + + QVariantMap iconParameters; + iconParameters.insert(QPlaceIcon::SingleUrl, + QUrl(item.value(QStringLiteral("icon")).toString())); + QPlaceIcon icon; + icon.setParameters(iconParameters); + place.setIcon(icon); + + QJsonObject addressDetails = item.value(QStringLiteral("address")).toObject(); + + const QString title = addressDetails.value(type).toString(); + + place.setName(title); + + if (!requestUrl.isEmpty()) { + QPlaceAttribute attribute; + attribute.setLabel("requestUrl"); + attribute.setText(requestUrl); + place.setExtendedAttribute("requestUrl", attribute); + } + + QGeoAddress address; + address.setCity(addressDetails.value(QStringLiteral("city")).toString()); + address.setCountry(addressDetails.value(QStringLiteral("country")).toString()); + // FIXME: country_code is alpha-2 setCountryCode takes alpha-3 + //address.setCountryCode(addressDetails.value(QStringLiteral("country_code")).toString()); + address.setPostalCode(addressDetails.value(QStringLiteral("postcode")).toString()); + address.setStreet(addressDetails.value(QStringLiteral("road")).toString()); + address.setState(addressDetails.value(QStringLiteral("state")).toString()); + address.setDistrict(addressDetails.value(QStringLiteral("suburb")).toString()); + + QGeoLocation location; + location.setCoordinate(coordinate); + location.setAddress(address); + location.setBoundingBox(parseBoundingBox(item.value(QStringLiteral("boundingbox")).toArray())); + + place.setLocation(location); + + QPlaceResult result; + result.setIcon(icon); + result.setPlace(place); + result.setTitle(title); + + return result; +} + +QT_END_NAMESPACE diff --git a/src/plugins/geoservices/osm/qplacesearchreplyosm.h b/src/plugins/geoservices/osm/qplacesearchreplyosm.h new file mode 100644 index 0000000..fd9f976 --- /dev/null +++ b/src/plugins/geoservices/osm/qplacesearchreplyosm.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtFoo module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACESEARCHREPLYOSM_H +#define QPLACESEARCHREPLYOSM_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QNetworkReply; +class QPlaceManagerEngineOsm; +class QPlaceResult; + +class QPlaceSearchReplyOsm : public QPlaceSearchReply +{ + Q_OBJECT + +public: + QPlaceSearchReplyOsm(const QPlaceSearchRequest &request, QNetworkReply *reply, + QPlaceManagerEngineOsm *parent); + ~QPlaceSearchReplyOsm(); + + QString requestUrl; + +private slots: + void setError(QPlaceReply::Error errorCode, const QString &errorString); + void replyFinished(); + void networkError(QNetworkReply::NetworkError error); + +private: + QPlaceResult parsePlaceResult(const QJsonObject &item) const; +}; + +QT_END_NAMESPACE + +#endif // QPLACESEARCHREPLYOSM_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 0000000..35b462d --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +qtHaveModule(positioning): SUBDIRS += position +qtHaveModule(location): SUBDIRS += geoservices diff --git a/src/plugins/position/android/android.pro b/src/plugins/position/android/android.pro new file mode 100644 index 0000000..0dc6a3f --- /dev/null +++ b/src/plugins/position/android/android.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += jar src diff --git a/src/plugins/position/android/jar/AndroidManifest.xml b/src/plugins/position/android/jar/AndroidManifest.xml new file mode 100644 index 0000000..2d066db --- /dev/null +++ b/src/plugins/position/android/jar/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + diff --git a/src/plugins/position/android/jar/jar.pro b/src/plugins/position/android/jar/jar.pro new file mode 100644 index 0000000..661f172 --- /dev/null +++ b/src/plugins/position/android/jar/jar.pro @@ -0,0 +1,16 @@ +TARGET = QtPositioning + +load(qt_build_paths) + +CONFIG += java +DESTDIR = $$MODULE_BASE_OUTDIR/jar + +JAVACLASSPATH += $$PWD/src + +JAVASOURCES += \ + $$PWD/src/org/qtproject/qt5/android/positioning/QtPositioning.java + +# install +target.path = $$[QT_INSTALL_PREFIX]/jar +INSTALLS += target + diff --git a/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java b/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java new file mode 100644 index 0000000..1ea0c07 --- /dev/null +++ b/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java @@ -0,0 +1,589 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +package org.qtproject.qt5.android.positioning; + +import android.content.Context; +import android.location.GpsSatellite; +import android.location.GpsStatus; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; + +import android.util.Log; + +public class QtPositioning implements LocationListener +{ + + private static final String TAG = "QtPositioning"; + static LocationManager locationManager = null; + static Object m_syncObject = new Object(); + static HashMap runningListeners = new HashMap(); + + /* + The positionInfo instance to which this + QtPositioning instance is attached to. + */ + private int nativeClassReference = 0; + + /* + The provider type requested by Qt + */ + private int expectedProviders = 0; + + public static final int QT_GPS_PROVIDER = 1; + public static final int QT_NETWORK_PROVIDER = 2; + + /* The following values must match the corresponding error enums in the Qt API*/ + public static final int QT_ACCESS_ERROR = 0; + public static final int QT_CLOSED_ERROR = 1; + public static final int QT_POSITION_UNKNOWN_SOURCE_ERROR = 2; + public static final int QT_POSITION_NO_ERROR = 3; + public static final int QT_SATELLITE_NO_ERROR = 2; + public static final int QT_SATELLITE_UNKNOWN_SOURCE_ERROR = -1; + + /* True, if updates were caused by requestUpdate() */ + private boolean isSingleUpdate = false; + /* The length requested for regular intervals in msec. */ + private int updateIntervalTime = 0; + + /* The last received GPS update */ + private Location lastGps = null; + /* The last received network update */ + private Location lastNetwork = null; + /* If true this class acts as satellite signal monitor rather than location monitor */ + private boolean isSatelliteUpdate = false; + + private PositioningLooper looperThread; + + static public void setContext(Context context) + { + try { + locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE); + } catch(Exception e) { + e.printStackTrace(); + } + } + + static private int[] providerList() + { + if (locationManager == null) { + Log.w(TAG, "No locationManager available in QtPositioning"); + return new int[0]; + } + List providers = locationManager.getAllProviders(); + int retList[] = new int[providers.size()]; + for (int i = 0; i < providers.size(); i++) { + if (providers.get(i).equals(LocationManager.GPS_PROVIDER)) { + //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_GPS + retList[i] = 0; + } else if (providers.get(i).equals(LocationManager.NETWORK_PROVIDER)) { + //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_NETWORK + retList[i] = 1; + } else if (providers.get(i).equals(LocationManager.PASSIVE_PROVIDER)) { + //must be in sync with AndroidPositioning::PositionProvider::PROVIDER_PASSIVE + retList[i] = 2; + } else { + retList[i] = -1; + } + } + return retList; + } + + static public Location lastKnownPosition(boolean fromSatelliteOnly) + { + Location gps = null; + Location network = null; + try { + gps = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); + if (!fromSatelliteOnly) + network = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + } catch(Exception e) { + e.printStackTrace(); + gps = network = null; + } + + if (gps != null && network != null) { + //we return the most recent location but slightly prefer GPS + //prefer GPS if it is max 4 hrs older than network + long delta = network.getTime() - gps.getTime(); + if (delta < 4*60*60*1000) { + return gps; + } else { + return network; + } + } else if (gps != null ) { + return gps; + } else if (network != null) { + return network; + } + + return null; + } + + /* Returns true if at least on of the given providers is enabled. */ + static private boolean expectedProvidersAvailable(int desiredProviders) + { + List enabledProviders = locationManager.getProviders(true); + if ((desiredProviders & QT_GPS_PROVIDER) > 0) { //gps desired + if (enabledProviders.contains(LocationManager.GPS_PROVIDER)) { + return true; + } + } + if ((desiredProviders & QT_NETWORK_PROVIDER) > 0) { //network desired + if (enabledProviders.contains(LocationManager.NETWORK_PROVIDER)) { + return true; + } + } + + return false; + } + + + static private void addActiveListener(QtPositioning listener, String provider) + { + int androidClassKey = listener.nativeClassReference; + //start update thread + listener.setActiveLooper(true); + + if (runningListeners.containsKey(androidClassKey) && runningListeners.get(androidClassKey) != listener) { + removeActiveListener(androidClassKey); + } + + locationManager.requestSingleUpdate(provider, + listener, + listener.looper()); + + runningListeners.put(androidClassKey, listener); + } + + + static private void addActiveListener(QtPositioning listener, String provider, long minTime, float minDistance) + { + int androidClassKey = listener.nativeClassReference; + //start update thread + listener.setActiveLooper(true); + + if (runningListeners.containsKey(androidClassKey) && runningListeners.get(androidClassKey) != listener) { + removeActiveListener(androidClassKey); + } + + locationManager.requestLocationUpdates(provider, + minTime, minDistance, + listener, + listener.looper()); + + runningListeners.put(androidClassKey, listener); + } + + + static private void removeActiveListener(QtPositioning listener) + { + removeActiveListener(listener.nativeClassReference); + } + + + static private void removeActiveListener(int androidClassKey) + { + QtPositioning listener = runningListeners.remove(androidClassKey); + + if (listener != null) { + locationManager.removeUpdates(listener); + listener.setActiveLooper(false); + } + } + + + static public int startUpdates(int androidClassKey, int locationProvider, int updateInterval) + { + synchronized (m_syncObject) { + try { + boolean exceptionOccurred = false; + QtPositioning positioningListener = new QtPositioning(); + positioningListener.nativeClassReference = androidClassKey; + positioningListener.expectedProviders = locationProvider; + positioningListener.isSatelliteUpdate = false; + + if (updateInterval == 0) + updateInterval = 50; //don't update more often than once per 50ms + + positioningListener.updateIntervalTime = updateInterval; + if ((locationProvider & QT_GPS_PROVIDER) > 0) { + Log.d(TAG, "Regular updates using GPS " + updateInterval); + try { + addActiveListener(positioningListener, + LocationManager.GPS_PROVIDER, + updateInterval, 0); + } catch (SecurityException se) { + se.printStackTrace(); + exceptionOccurred = true; + } + } + + if ((locationProvider & QT_NETWORK_PROVIDER) > 0) { + Log.d(TAG, "Regular updates using network " + updateInterval); + try { + addActiveListener(positioningListener, + LocationManager.NETWORK_PROVIDER, + updateInterval, 0); + } catch (SecurityException se) { + se.printStackTrace(); + exceptionOccurred = true; + } + } + if (exceptionOccurred) { + removeActiveListener(positioningListener); + return QT_ACCESS_ERROR; + } + + if (!expectedProvidersAvailable(locationProvider)) { + //all location providers unavailbe -> when they come back we resume automatically + return QT_CLOSED_ERROR; + } + + } catch(Exception e) { + e.printStackTrace(); + return QT_POSITION_UNKNOWN_SOURCE_ERROR; + } + + return QT_POSITION_NO_ERROR; + } + } + + static public void stopUpdates(int androidClassKey) + { + synchronized (m_syncObject) { + try { + Log.d(TAG, "Stopping updates"); + removeActiveListener(androidClassKey); + } catch(Exception e) { + e.printStackTrace(); + return; + } + } + } + + static public int requestUpdate(int androidClassKey, int locationProvider) + { + synchronized (m_syncObject) { + try { + boolean exceptionOccurred = false; + QtPositioning positioningListener = new QtPositioning(); + positioningListener.nativeClassReference = androidClassKey; + positioningListener.isSingleUpdate = true; + positioningListener.expectedProviders = locationProvider; + positioningListener.isSatelliteUpdate = false; + + if ((locationProvider & QT_GPS_PROVIDER) > 0) { + Log.d(TAG, "Single update using GPS"); + try { + addActiveListener(positioningListener, LocationManager.GPS_PROVIDER); + } catch (SecurityException se) { + se.printStackTrace(); + exceptionOccurred = true; + } + } + + if ((locationProvider & QT_NETWORK_PROVIDER) > 0) { + Log.d(TAG, "Single update using network"); + try { + addActiveListener(positioningListener, LocationManager.NETWORK_PROVIDER); + } catch (SecurityException se) { + se.printStackTrace(); + exceptionOccurred = true; + } + } + if (exceptionOccurred) { + removeActiveListener(positioningListener); + return QT_ACCESS_ERROR; + } + + if (!expectedProvidersAvailable(locationProvider)) { + //all location providers unavailable -> when they come back we resume automatically + //in the mean time return ClosedError + return QT_CLOSED_ERROR; + } + + } catch(Exception e) { + e.printStackTrace(); + return QT_POSITION_UNKNOWN_SOURCE_ERROR; + } + + return QT_POSITION_NO_ERROR; + } + } + + static public int startSatelliteUpdates(int androidClassKey, int updateInterval, boolean isSingleRequest) + { + synchronized (m_syncObject) { + try { + boolean exceptionOccurred = false; + QtPositioning positioningListener = new QtPositioning(); + positioningListener.isSatelliteUpdate = true; + positioningListener.nativeClassReference = androidClassKey; + positioningListener.expectedProviders = 1; //always satellite provider + positioningListener.isSingleUpdate = isSingleRequest; + + if (updateInterval == 0) + updateInterval = 50; //don't update more often than once per 50ms + + if (isSingleRequest) + Log.d(TAG, "Single update for Satellites " + updateInterval); + else + Log.d(TAG, "Regular updates for Satellites " + updateInterval); + try { + addActiveListener(positioningListener, LocationManager.GPS_PROVIDER, + updateInterval, 0); + } catch (SecurityException se) { + se.printStackTrace(); + exceptionOccurred = true; + } + + if (exceptionOccurred) { + removeActiveListener(positioningListener); + return QT_ACCESS_ERROR; + } + + if (!expectedProvidersAvailable(positioningListener.expectedProviders)) { + //all location providers unavailable -> when they come back we resume automatically + //in the mean time return ClosedError + return QT_CLOSED_ERROR; + } + + } catch(Exception e) { + e.printStackTrace(); + return QT_SATELLITE_UNKNOWN_SOURCE_ERROR; + } + + return QT_SATELLITE_NO_ERROR; + } + } + + public QtPositioning() + { + looperThread = new PositioningLooper(); + } + + public Looper looper() + { + return looperThread.looper(); + } + + private void setActiveLooper(boolean setActive) + { + try{ + if (setActive) { + if (looperThread.isAlive()) + return; + + if (isSatelliteUpdate) + looperThread.isSatelliteListener(true); + + long start = System.currentTimeMillis(); + looperThread.start(); + + //busy wait but lasts ~20-30 ms only + while (!looperThread.isReady()); + + long stop = System.currentTimeMillis(); + Log.d(TAG, "Looper Thread startup time in ms: " + (stop-start)); + } else { + looperThread.quitLooper(); + } + } catch(Exception e) { + e.printStackTrace(); + } + } + + private class PositioningLooper extends Thread implements GpsStatus.Listener{ + private boolean looperRunning; + private Looper posLooper; + private boolean isSatelliteLooper = false; + private LocationManager locManager = null; + + private PositioningLooper() + { + looperRunning = false; + } + + public void run() + { + Looper.prepare(); + Handler handler = new Handler(); + + if (isSatelliteLooper) { + try { + locationManager.addGpsStatusListener(this); + } catch(Exception e) { + e.printStackTrace(); + } + } + + posLooper = Looper.myLooper(); + synchronized (this) { + looperRunning = true; + } + Looper.loop(); + synchronized (this) { + looperRunning = false; + } + } + + public void quitLooper() + { + if (isSatelliteLooper) + locationManager.removeGpsStatusListener(this); + looper().quit(); + } + + public synchronized boolean isReady() + { + return looperRunning; + } + + public void isSatelliteListener(boolean isListener) + { + isSatelliteLooper = isListener; + } + + public Looper looper() + { + return posLooper; + } + + @Override + public void onGpsStatusChanged(int event) { + switch (event) { + case GpsStatus.GPS_EVENT_FIRST_FIX: + break; + case GpsStatus.GPS_EVENT_SATELLITE_STATUS: + GpsStatus status = locationManager.getGpsStatus(null); + Iterable iterable = status.getSatellites(); + Iterator it = iterable.iterator(); + + ArrayList list = new ArrayList(); + while (it.hasNext()) { + GpsSatellite sat = (GpsSatellite) it.next(); + list.add(sat); + } + GpsSatellite[] sats = list.toArray(new GpsSatellite[list.size()]); + satelliteUpdated(sats, nativeClassReference, isSingleUpdate); + + break; + case GpsStatus.GPS_EVENT_STARTED: + break; + case GpsStatus.GPS_EVENT_STOPPED: + break; + } + } + } + + + + public static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate); + public static native void locationProvidersDisabled(int androidClassKey); + public static native void satelliteUpdated(GpsSatellite[] update, int androidClassKey, boolean isSingleUpdate); + + @Override + public void onLocationChanged(Location location) { + //Log.d(TAG, "**** Position Update ****: " + location.toString() + " " + isSingleUpdate); + if (location == null) + return; + + if (isSatelliteUpdate) //we are a QGeoSatelliteInfoSource -> ignore + return; + + if (isSingleUpdate || expectedProviders < 3) { + positionUpdated(location, nativeClassReference, isSingleUpdate); + return; + } + + /* + We can use GPS and Network, pick the better location provider. + Generally we prefer GPS data due to their higher accurancy but we + let Network data pass until GPS fix is available + */ + + if (location.getProvider().equals(LocationManager.GPS_PROVIDER)) { + lastGps = location; + + // assumption: GPS always better -> pass it on + positionUpdated(location, nativeClassReference, isSingleUpdate); + } else if (location.getProvider().equals(LocationManager.NETWORK_PROVIDER)) { + lastNetwork = location; + + if (lastGps == null) { //no GPS fix yet use network location + positionUpdated(location, nativeClassReference, isSingleUpdate); + return; + } + + long delta = location.getTime() - lastGps.getTime(); + + // Ignore if network update is older than last GPS (delta < 0) + // Ignore if gps update still has time to provide next location (delta < updateInterval) + if (delta < updateIntervalTime) + return; + + // Use network data -> GPS has timed out on updateInterval + positionUpdated(location, nativeClassReference, isSingleUpdate); + } + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) {} + + @Override + public void onProviderEnabled(String provider) { + Log.d(TAG, "Enabled provider: " + provider); + } + + @Override + public void onProviderDisabled(String provider) { + Log.d(TAG, "Disabled provider: " + provider); + if (!expectedProvidersAvailable(expectedProviders)) + locationProvidersDisabled(nativeClassReference); + } +} diff --git a/src/plugins/position/android/src/jnipositioning.cpp b/src/plugins/position/android/src/jnipositioning.cpp new file mode 100644 index 0000000..9bef8d3 --- /dev/null +++ b/src/plugins/position/android/src/jnipositioning.cpp @@ -0,0 +1,606 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "qgeopositioninfosource_android_p.h" +#include "qgeosatelliteinfosource_android_p.h" + +#include "jnipositioning.h" + +static JavaVM *javaVM = 0; +jclass positioningClass; + +static jmethodID providerListMethodId; +static jmethodID lastKnownPositionMethodId; +static jmethodID startUpdatesMethodId; +static jmethodID stopUpdatesMethodId; +static jmethodID requestUpdateMethodId; +static jmethodID startSatelliteUpdatesMethodId; + +static const char logTag[] = "QtPositioning"; +static const char classErrorMsg[] = "Can't find class \"%s\""; +static const char methodErrorMsg[] = "Can't find method \"%s%s\""; + +namespace AndroidPositioning { + typedef QMap PositionSourceMap; + typedef QMap SatelliteSourceMap; + + Q_GLOBAL_STATIC(PositionSourceMap, idToPosSource) + + Q_GLOBAL_STATIC(SatelliteSourceMap, idToSatSource) + + struct AttachedJNIEnv + { + AttachedJNIEnv() + { + attached = false; + if (javaVM && javaVM->GetEnv((void**)&jniEnv, JNI_VERSION_1_6) < 0) { + if (javaVM->AttachCurrentThread(&jniEnv, NULL) < 0) { + __android_log_print(ANDROID_LOG_ERROR, logTag, "AttachCurrentThread failed"); + jniEnv = 0; + return; + } + attached = true; + } + } + + ~AttachedJNIEnv() + { + if (attached) + javaVM->DetachCurrentThread(); + } + bool attached; + JNIEnv *jniEnv; + }; + + int registerPositionInfoSource(QObject *obj) + { + static bool firstInit = true; + if (firstInit) { + firstInit = false; + } + + int key = -1; + if (obj->inherits("QGeoPositionInfoSource")) { + QGeoPositionInfoSourceAndroid *src = qobject_cast(obj); + Q_ASSERT(src); + do { + key = QRandomGenerator::global()->generate(); + } while (idToPosSource()->contains(key)); + + idToPosSource()->insert(key, src); + } else if (obj->inherits("QGeoSatelliteInfoSource")) { + QGeoSatelliteInfoSourceAndroid *src = qobject_cast(obj); + Q_ASSERT(src); + do { + key = QRandomGenerator::global()->generate(); + } while (idToSatSource()->contains(key)); + + idToSatSource()->insert(key, src); + } + + return key; + } + + void unregisterPositionInfoSource(int key) + { + idToPosSource()->remove(key); + idToSatSource()->remove(key); + } + + enum PositionProvider + { + PROVIDER_GPS = 0, + PROVIDER_NETWORK = 1, + PROVIDER_PASSIVE = 2 + }; + + + QGeoPositionInfoSource::PositioningMethods availableProviders() + { + QGeoPositionInfoSource::PositioningMethods ret = + static_cast(0); + AttachedJNIEnv env; + if (!env.jniEnv) + return ret; + jintArray jProviders = static_cast(env.jniEnv->CallStaticObjectMethod( + positioningClass, providerListMethodId)); + jint *providers = env.jniEnv->GetIntArrayElements(jProviders, 0); + const uint size = env.jniEnv->GetArrayLength(jProviders); + for (uint i = 0; i < size; i++) { + switch (providers[i]) { + case PROVIDER_GPS: + ret |= QGeoPositionInfoSource::SatellitePositioningMethods; + break; + case PROVIDER_NETWORK: + ret |= QGeoPositionInfoSource::NonSatellitePositioningMethods; + break; + case PROVIDER_PASSIVE: + //we ignore as Qt doesn't have interface for it right now + break; + default: + __android_log_print(ANDROID_LOG_INFO, logTag, "Unknown positioningMethod"); + } + } + + env.jniEnv->ReleaseIntArrayElements(jProviders, providers, 0); + env.jniEnv->DeleteLocalRef(jProviders); + + return ret; + } + + //caching originally taken from corelib/kernel/qjni.cpp + typedef QHash JMethodIDHash; + Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID) + + static jmethodID getCachedMethodID(JNIEnv *env, + jclass clazz, + const char *name, + const char *sig) + { + jmethodID id = 0; + int offset_name = qstrlen(name); + int offset_signal = qstrlen(sig); + QByteArray key(offset_name + offset_signal, Qt::Uninitialized); + memcpy(key.data(), name, offset_name); + memcpy(key.data()+offset_name, sig, offset_signal); + QHash::iterator it = cachedMethodID->find(key); + if (it == cachedMethodID->end()) { + id = env->GetMethodID(clazz, name, sig); + if (env->ExceptionCheck()) { + id = 0; + #ifdef QT_DEBUG + env->ExceptionDescribe(); + #endif // QT_DEBUG + env->ExceptionClear(); + } + + cachedMethodID->insert(key, id); + } else { + id = it.value(); + } + return id; + } + + QGeoPositionInfo positionInfoFromJavaLocation(JNIEnv * jniEnv, const jobject &location) + { + QGeoPositionInfo info; + jclass thisClass = jniEnv->GetObjectClass(location); + if (!thisClass) + return QGeoPositionInfo(); + + jmethodID mid = getCachedMethodID(jniEnv, thisClass, "getLatitude", "()D"); + jdouble latitude = jniEnv->CallDoubleMethod(location, mid); + mid = getCachedMethodID(jniEnv, thisClass, "getLongitude", "()D"); + jdouble longitude = jniEnv->CallDoubleMethod(location, mid); + QGeoCoordinate coordinate(latitude, longitude); + + //altitude + mid = getCachedMethodID(jniEnv, thisClass, "hasAltitude", "()Z"); + jboolean attributeExists = jniEnv->CallBooleanMethod(location, mid); + if (attributeExists) { + mid = getCachedMethodID(jniEnv, thisClass, "getAltitude", "()D"); + jdouble value = jniEnv->CallDoubleMethod(location, mid); + coordinate.setAltitude(value); + } + + info.setCoordinate(coordinate); + + //time stamp + mid = getCachedMethodID(jniEnv, thisClass, "getTime", "()J"); + jlong timestamp = jniEnv->CallLongMethod(location, mid); + info.setTimestamp(QDateTime::fromMSecsSinceEpoch(timestamp, Qt::UTC)); + + //accuracy + mid = getCachedMethodID(jniEnv, thisClass, "hasAccuracy", "()Z"); + attributeExists = jniEnv->CallBooleanMethod(location, mid); + if (attributeExists) { + mid = getCachedMethodID(jniEnv, thisClass, "getAccuracy", "()F"); + jfloat accuracy = jniEnv->CallFloatMethod(location, mid); + info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy); + } + + //ground speed + mid = getCachedMethodID(jniEnv, thisClass, "hasSpeed", "()Z"); + attributeExists = jniEnv->CallBooleanMethod(location, mid); + if (attributeExists) { + mid = getCachedMethodID(jniEnv, thisClass, "getSpeed", "()F"); + jfloat speed = jniEnv->CallFloatMethod(location, mid); + info.setAttribute(QGeoPositionInfo::GroundSpeed, speed); + } + + //bearing + mid = getCachedMethodID(jniEnv, thisClass, "hasBearing", "()Z"); + attributeExists = jniEnv->CallBooleanMethod(location, mid); + if (attributeExists) { + mid = getCachedMethodID(jniEnv, thisClass, "getBearing", "()F"); + jfloat bearing = jniEnv->CallFloatMethod(location, mid); + info.setAttribute(QGeoPositionInfo::Direction, bearing); + } + + jniEnv->DeleteLocalRef(thisClass); + return info; + } + + QList satelliteInfoFromJavaLocation(JNIEnv *jniEnv, + jobjectArray satellites, + QList* usedInFix) + { + QList sats; + jsize length = jniEnv->GetArrayLength(satellites); + for (int i = 0; iGetObjectArrayElement(satellites, i); + if (jniEnv->ExceptionOccurred()) { + qWarning() << "Cannot process all satellite data due to exception."; + break; + } + + jclass thisClass = jniEnv->GetObjectClass(element); + if (!thisClass) + continue; + + QGeoSatelliteInfo info; + + //signal strength + jmethodID mid = getCachedMethodID(jniEnv, thisClass, "getSnr", "()F"); + jfloat snr = jniEnv->CallFloatMethod(element, mid); + info.setSignalStrength((int)snr); + + //ignore any satellite with no signal whatsoever + if (qFuzzyIsNull(snr)) + continue; + + //prn + mid = getCachedMethodID(jniEnv, thisClass, "getPrn", "()I"); + jint prn = jniEnv->CallIntMethod(element, mid); + info.setSatelliteIdentifier(prn); + + if (prn >= 1 && prn <= 32) + info.setSatelliteSystem(QGeoSatelliteInfo::GPS); + else if (prn >= 65 && prn <= 96) + info.setSatelliteSystem(QGeoSatelliteInfo::GLONASS); + + //azimuth + mid = getCachedMethodID(jniEnv, thisClass, "getAzimuth", "()F"); + jfloat azimuth = jniEnv->CallFloatMethod(element, mid); + info.setAttribute(QGeoSatelliteInfo::Azimuth, azimuth); + + //elevation + mid = getCachedMethodID(jniEnv, thisClass, "getElevation", "()F"); + jfloat elevation = jniEnv->CallFloatMethod(element, mid); + info.setAttribute(QGeoSatelliteInfo::Elevation, elevation); + + //used in a fix + mid = getCachedMethodID(jniEnv, thisClass, "usedInFix", "()Z"); + jboolean inFix = jniEnv->CallBooleanMethod(element, mid); + + sats.append(info); + + if (inFix) + usedInFix->append(info); + + jniEnv->DeleteLocalRef(thisClass); + jniEnv->DeleteLocalRef(element); + } + + return sats; + } + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return QGeoPositionInfo(); + + jobject location = env.jniEnv->CallStaticObjectMethod(positioningClass, + lastKnownPositionMethodId, + fromSatellitePositioningMethodsOnly); + if (location == 0) + return QGeoPositionInfo(); + + const QGeoPositionInfo info = positionInfoFromJavaLocation(env.jniEnv, location); + env.jniEnv->DeleteLocalRef(location); + + return info; + } + + inline int positioningMethodToInt(QGeoPositionInfoSource::PositioningMethods m) + { + int providerSelection = 0; + if (m & QGeoPositionInfoSource::SatellitePositioningMethods) + providerSelection |= 1; + if (m & QGeoPositionInfoSource::NonSatellitePositioningMethods) + providerSelection |= 2; + + return providerSelection; + } + + QGeoPositionInfoSource::Error startUpdates(int androidClassKey) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return QGeoPositionInfoSource::UnknownSourceError; + + QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); + + if (source) { + // Android v23+ requires runtime permission check and requests + QString permission(QLatin1String("android.permission.ACCESS_FINE_LOCATION")); + + if (QtAndroidPrivate::checkPermission(permission) == QtAndroidPrivate::PermissionsResult::Denied) { + const QHash results = + QtAndroidPrivate::requestPermissionsSync(env.jniEnv, QStringList() << permission); + if (!results.contains(permission) + || results[permission] == QtAndroidPrivate::PermissionsResult::Denied) + { + qWarning() << "Position retrieval not possible due to missing permission (ACCESS_FINE_LOCATION)"; + return QGeoPositionInfoSource::AccessError; + } + } + + int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startUpdatesMethodId, + androidClassKey, + positioningMethodToInt(source->preferredPositioningMethods()), + source->updateInterval()); + switch (errorCode) { + case 0: + case 1: + case 2: + case 3: + return static_cast(errorCode); + default: + break; + } + } + + return QGeoPositionInfoSource::UnknownSourceError; + } + + //used for stopping regular and single updates + void stopUpdates(int androidClassKey) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return; + + env.jniEnv->CallStaticVoidMethod(positioningClass, stopUpdatesMethodId, androidClassKey); + } + + QGeoPositionInfoSource::Error requestUpdate(int androidClassKey) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return QGeoPositionInfoSource::UnknownSourceError; + + QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); + + if (source) { + // Android v23+ requires runtime permission check and requests + QString permission(QLatin1String("android.permission.ACCESS_FINE_LOCATION")); + + if (QtAndroidPrivate::checkPermission(permission) == QtAndroidPrivate::PermissionsResult::Denied) { + const QHash results = + QtAndroidPrivate::requestPermissionsSync(env.jniEnv, QStringList() << permission); + if (!results.contains(permission) + || results[permission] == QtAndroidPrivate::PermissionsResult::Denied) + { + qWarning() << "Position update not possible due to missing permission (ACCESS_FINE_LOCATION)"; + return QGeoPositionInfoSource::AccessError; + } + } + + int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, requestUpdateMethodId, + androidClassKey, + positioningMethodToInt(source->preferredPositioningMethods())); + switch (errorCode) { + case 0: + case 1: + case 2: + case 3: + return static_cast(errorCode); + default: + break; + } + } + return QGeoPositionInfoSource::UnknownSourceError; + } + + QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, bool isSingleRequest, int requestTimeout) + { + AttachedJNIEnv env; + if (!env.jniEnv) + return QGeoSatelliteInfoSource::UnknownSourceError; + + QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey); + + if (source) { + int interval = source->updateInterval(); + if (isSingleRequest) + interval = requestTimeout; + int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startSatelliteUpdatesMethodId, + androidClassKey, + interval, isSingleRequest); + switch (errorCode) { + case -1: + case 0: + case 1: + case 2: + return static_cast(errorCode); + default: + qWarning() << "startSatelliteUpdates: Unknown error code " << errorCode; + break; + } + } + return QGeoSatelliteInfoSource::UnknownSourceError; + } +} + + +static void positionUpdated(JNIEnv *env, jobject /*thiz*/, jobject location, jint androidClassKey, jboolean isSingleUpdate) +{ + QGeoPositionInfo info = AndroidPositioning::positionInfoFromJavaLocation(env, location); + + QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); + if (!source) { + qWarning("positionUpdated: source == 0"); + return; + } + + //we need to invoke indirectly as the Looper thread is likely to be not the same thread + if (!isSingleUpdate) + QMetaObject::invokeMethod(source, "processPositionUpdate", Qt::AutoConnection, + Q_ARG(QGeoPositionInfo, info)); + else + QMetaObject::invokeMethod(source, "processSinglePositionUpdate", Qt::AutoConnection, + Q_ARG(QGeoPositionInfo, info)); +} + +static void locationProvidersDisabled(JNIEnv *env, jobject /*thiz*/, jint androidClassKey) +{ + Q_UNUSED(env); + QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey); + if (!source) + source = AndroidPositioning::idToSatSource()->value(androidClassKey); + if (!source) { + qWarning("locationProvidersDisabled: source == 0"); + return; + } + + QMetaObject::invokeMethod(source, "locationProviderDisabled", Qt::AutoConnection); +} + +static void satelliteUpdated(JNIEnv *env, jobject /*thiz*/, jobjectArray satellites, jint androidClassKey, jboolean isSingleUpdate) +{ + QList inUse; + QList sats = AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse); + + QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey); + if (!source) { + qFatal("satelliteUpdated: source == 0"); + return; + } + + QMetaObject::invokeMethod(source, "processSatelliteUpdateInView", Qt::AutoConnection, + Q_ARG(QList, sats), Q_ARG(bool, isSingleUpdate)); + + QMetaObject::invokeMethod(source, "processSatelliteUpdateInUse", Qt::AutoConnection, + Q_ARG(QList, inUse), Q_ARG(bool, isSingleUpdate)); +} + + +#define FIND_AND_CHECK_CLASS(CLASS_NAME) \ +clazz = env->FindClass(CLASS_NAME); \ +if (!clazz) { \ + __android_log_print(ANDROID_LOG_FATAL, logTag, classErrorMsg, CLASS_NAME); \ + return JNI_FALSE; \ +} + +#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \ +VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \ +if (!VAR) { \ + __android_log_print(ANDROID_LOG_FATAL, logTag, methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \ + return JNI_FALSE; \ +} + +static JNINativeMethod methods[] = { + {"positionUpdated", "(Landroid/location/Location;IZ)V", (void *)positionUpdated}, + {"locationProvidersDisabled", "(I)V", (void *) locationProvidersDisabled}, + {"satelliteUpdated", "([Landroid/location/GpsSatellite;IZ)V", (void *)satelliteUpdated} +}; + +static bool registerNatives(JNIEnv *env) +{ + jclass clazz; + FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/positioning/QtPositioning"); + positioningClass = static_cast(env->NewGlobalRef(clazz)); + + if (env->RegisterNatives(positioningClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "RegisterNatives failed"); + return JNI_FALSE; + } + + GET_AND_CHECK_STATIC_METHOD(providerListMethodId, positioningClass, "providerList", "()[I"); + GET_AND_CHECK_STATIC_METHOD(lastKnownPositionMethodId, positioningClass, "lastKnownPosition", "(Z)Landroid/location/Location;"); + GET_AND_CHECK_STATIC_METHOD(startUpdatesMethodId, positioningClass, "startUpdates", "(III)I"); + GET_AND_CHECK_STATIC_METHOD(stopUpdatesMethodId, positioningClass, "stopUpdates", "(I)V"); + GET_AND_CHECK_STATIC_METHOD(requestUpdateMethodId, positioningClass, "requestUpdate", "(II)I"); + GET_AND_CHECK_STATIC_METHOD(startSatelliteUpdatesMethodId, positioningClass, "startSatelliteUpdates", "(IIZ)I"); + + return true; +} + +Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/) +{ + static bool initialized = false; + if (initialized) + return JNI_VERSION_1_6; + initialized = true; + + typedef union { + JNIEnv *nativeEnvironment; + void *venv; + } UnionJNIEnvToVoid; + + __android_log_print(ANDROID_LOG_INFO, logTag, "Positioning start"); + UnionJNIEnvToVoid uenv; + uenv.venv = NULL; + javaVM = 0; + + if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed"); + return -1; + } + JNIEnv *env = uenv.nativeEnvironment; + if (!registerNatives(env)) { + __android_log_print(ANDROID_LOG_FATAL, logTag, "registerNatives failed"); + return -1; + } + + javaVM = vm; + return JNI_VERSION_1_4; +} + diff --git a/src/plugins/position/android/src/jnipositioning.h b/src/plugins/position/android/src/jnipositioning.h new file mode 100644 index 0000000..0de6a5a --- /dev/null +++ b/src/plugins/position/android/src/jnipositioning.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef JNIPOSITIONING_H +#define JNIPOSITIONING_H + +#include +#include + +namespace AndroidPositioning +{ + int registerPositionInfoSource(QObject *obj); + void unregisterPositionInfoSource(int key); + + QGeoPositionInfoSource::PositioningMethods availableProviders(); + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly); + + QGeoPositionInfoSource::Error startUpdates(int androidClassKey); + void stopUpdates(int androidClassKey); + QGeoPositionInfoSource::Error requestUpdate(int androidClassKey); + + QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, + bool isSingleRequest, + int updateRequestTimeout); +} + +#endif // JNIPOSITIONING_H diff --git a/src/plugins/position/android/src/plugin.json b/src/plugins/position/android/src/plugin.json new file mode 100644 index 0000000..4fd8789 --- /dev/null +++ b/src/plugins/position/android/src/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["android"], + "Provider": "android", + "Position": true, + "Satellite": true, + "Monitor": false, + "Priority": 1000, + "Testable": false +} diff --git a/src/plugins/position/android/src/positionfactory_android.cpp b/src/plugins/position/android/src/positionfactory_android.cpp new file mode 100644 index 0000000..25d6ed0 --- /dev/null +++ b/src/plugins/position/android/src/positionfactory_android.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "positionfactory_android.h" +#include "qgeopositioninfosource_android_p.h" +#include "qgeosatelliteinfosource_android_p.h" + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryAndroid::positionInfoSource(QObject *parent) +{ + QGeoPositionInfoSourceAndroid *src = new QGeoPositionInfoSourceAndroid(parent); + return src; +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryAndroid::satelliteInfoSource(QObject *parent) +{ + QGeoSatelliteInfoSourceAndroid *src = new QGeoSatelliteInfoSourceAndroid(parent); + return src; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryAndroid::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} diff --git a/src/plugins/position/android/src/positionfactory_android.h b/src/plugins/position/android/src/positionfactory_android.h new file mode 100644 index 0000000..cdab6f1 --- /dev/null +++ b/src/plugins/position/android/src/positionfactory_android.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef POSITIONPOLLFACTORY_H +#define POSITIONPOLLFACTORY_H + +#include +#include + +class QGeoPositionInfoSourceFactoryAndroid : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +#endif // POSITIONPOLLFACTORY_H diff --git a/src/plugins/position/android/src/qgeopositioninfosource_android.cpp b/src/plugins/position/android/src/qgeopositioninfosource_android.cpp new file mode 100644 index 0000000..7b4706d --- /dev/null +++ b/src/plugins/position/android/src/qgeopositioninfosource_android.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosource_android_p.h" +#include "jnipositioning.h" +//#include +#include + +#define UPDATE_FROM_COLD_START 2*60*1000 + + +QGeoPositionInfoSourceAndroid::QGeoPositionInfoSourceAndroid(QObject *parent) : + QGeoPositionInfoSource(parent), updatesRunning(false), m_error(NoError), m_requestTimer(this) +{ + androidClassKeyForUpdate = AndroidPositioning::registerPositionInfoSource(this); + androidClassKeyForSingleRequest = AndroidPositioning::registerPositionInfoSource(this); + + //qDebug() << "androidClassKey: " << androidClassKeyForUpdate << androidClassKeyForSingleRequest; + //by default use all methods + setPreferredPositioningMethods(AllPositioningMethods); + + m_requestTimer.setSingleShot(true); + QObject::connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestTimeout())); +} + +QGeoPositionInfoSourceAndroid::~QGeoPositionInfoSourceAndroid() +{ + stopUpdates(); + + if (m_requestTimer.isActive()) { + m_requestTimer.stop(); + AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest); + } + + AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForUpdate); + AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForSingleRequest); +} + +void QGeoPositionInfoSourceAndroid::setUpdateInterval(int msec) +{ + int previousInterval = updateInterval(); + msec = (((msec > 0) && (msec < minimumUpdateInterval())) || msec < 0)? minimumUpdateInterval() : msec; + + if (msec == previousInterval) + return; + + QGeoPositionInfoSource::setUpdateInterval(msec); + + if (updatesRunning) + reconfigureRunningSystem(); +} + +QGeoPositionInfo QGeoPositionInfoSourceAndroid::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const +{ + return AndroidPositioning::lastKnownPosition(fromSatellitePositioningMethodsOnly); +} + +QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceAndroid::supportedPositioningMethods() const +{ + return AndroidPositioning::availableProviders(); +} + +void QGeoPositionInfoSourceAndroid::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods) +{ + PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); + QGeoPositionInfoSource::setPreferredPositioningMethods(methods); + if (previousPreferredPositioningMethods == preferredPositioningMethods()) + return; + + if (updatesRunning) + reconfigureRunningSystem(); +} + +int QGeoPositionInfoSourceAndroid::minimumUpdateInterval() const +{ + return 50; +} + +QGeoPositionInfoSource::Error QGeoPositionInfoSourceAndroid::error() const +{ + return m_error; +} + +void QGeoPositionInfoSourceAndroid::setError(Error error) +{ + // qDebug() << "setError: " << error; + if (error != QGeoPositionInfoSource::NoError) + { + m_error = error; + emit QGeoPositionInfoSource::error(m_error); + } +} + +void QGeoPositionInfoSourceAndroid::startUpdates() +{ + if (updatesRunning) + return; + + if (preferredPositioningMethods() == 0) { + setError(UnknownSourceError); + return; + } + + updatesRunning = true; + QGeoPositionInfoSource::Error error = AndroidPositioning::startUpdates(androidClassKeyForUpdate); + if (error != QGeoPositionInfoSource::NoError) + updatesRunning = false; + + setError(error); +} + +void QGeoPositionInfoSourceAndroid::stopUpdates() +{ + if (!updatesRunning) + return; + + updatesRunning = false; + AndroidPositioning::stopUpdates(androidClassKeyForUpdate); +} + +void QGeoPositionInfoSourceAndroid::requestUpdate(int timeout) +{ + if (m_requestTimer.isActive()) + return; + + if (timeout != 0 && timeout < minimumUpdateInterval()) { + emit updateTimeout(); + return; + } + + if (timeout == 0) + timeout = UPDATE_FROM_COLD_START; + + m_requestTimer.start(timeout); + + // if updates already running with interval equal to timeout + // then we wait for next update coming through + // assume that a single update will not be quicker than regular updates anyway + if (updatesRunning && updateInterval() <= timeout) + return; + + QGeoPositionInfoSource::Error error = AndroidPositioning::requestUpdate(androidClassKeyForSingleRequest); + if (error != QGeoPositionInfoSource::NoError) + m_requestTimer.stop(); + + setError(error); +} + +void QGeoPositionInfoSourceAndroid::processPositionUpdate(const QGeoPositionInfo &pInfo) +{ + //single update request and served as part of regular update + if (m_requestTimer.isActive()) + m_requestTimer.stop(); + + emit positionUpdated(pInfo); +} + +// Might still be called multiple times (once for each provider) +void QGeoPositionInfoSourceAndroid::processSinglePositionUpdate(const QGeoPositionInfo &pInfo) +{ + //timeout but we received a late update -> ignore + if (!m_requestTimer.isActive()) + return; + + queuedSingleUpdates.append(pInfo); +} + +void QGeoPositionInfoSourceAndroid::locationProviderDisabled() +{ + setError(QGeoPositionInfoSource::ClosedError); +} + +void QGeoPositionInfoSourceAndroid::requestTimeout() +{ + AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest); + //no queued update to process -> timeout + const int count = queuedSingleUpdates.count(); + + if (!count) { + emit updateTimeout(); + return; + } + + //pick best + QGeoPositionInfo best = queuedSingleUpdates[0]; + for (int i = 1; i < count; i++) { + const QGeoPositionInfo info = queuedSingleUpdates[i]; + + //anything newer by 20s is always better + const int timeDelta = best.timestamp().secsTo(info.timestamp()); + if (abs(timeDelta) > 20) { + if (timeDelta > 0) + best = info; + continue; + } + + //compare accuracy + if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy) && + info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + { + best = info.attribute(QGeoPositionInfo::HorizontalAccuracy) < + best.attribute(QGeoPositionInfo::HorizontalAccuracy) ? info : best; + continue; + } + + //prefer info with accuracy information + if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + best = info; + } + + queuedSingleUpdates.clear(); + emit positionUpdated(best); +} + +/* + Updates the system assuming that updateInterval + and/or preferredPositioningMethod have changed. + */ +void QGeoPositionInfoSourceAndroid::reconfigureRunningSystem() +{ + if (!updatesRunning) + return; + + stopUpdates(); + startUpdates(); +} diff --git a/src/plugins/position/android/src/qgeopositioninfosource_android_p.h b/src/plugins/position/android/src/qgeopositioninfosource_android_p.h new file mode 100644 index 0000000..dbb27f8 --- /dev/null +++ b/src/plugins/position/android/src/qgeopositioninfosource_android_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCE_ANDROID_P_H +#define QGEOPOSITIONINFOSOURCE_ANDROID_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +class QGeoPositionInfoSourceAndroid : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + QGeoPositionInfoSourceAndroid(QObject *parent = 0); + ~QGeoPositionInfoSourceAndroid(); + + // From QGeoPositionInfoSource + void setUpdateInterval(int msec); + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + PositioningMethods supportedPositioningMethods() const; + void setPreferredPositioningMethods(PositioningMethods methods); + int minimumUpdateInterval() const; + Error error() const; + +public Q_SLOTS: + virtual void startUpdates(); + virtual void stopUpdates(); + + virtual void requestUpdate(int timeout = 0); + + void processPositionUpdate(const QGeoPositionInfo& pInfo); + void processSinglePositionUpdate(const QGeoPositionInfo& pInfo); + + void locationProviderDisabled(); +private Q_SLOTS: + void requestTimeout(); + +private: + void reconfigureRunningSystem(); + void setError(Error error); + + bool updatesRunning; + int androidClassKeyForUpdate; + int androidClassKeyForSingleRequest; + QList queuedSingleUpdates; + Error m_error; + QTimer m_requestTimer; +}; + +#endif // QGEOPOSITIONINFOSOURCE_ANDROID_P_H diff --git a/src/plugins/position/android/src/qgeosatelliteinfosource_android.cpp b/src/plugins/position/android/src/qgeosatelliteinfosource_android.cpp new file mode 100644 index 0000000..f89f666 --- /dev/null +++ b/src/plugins/position/android/src/qgeosatelliteinfosource_android.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qgeosatelliteinfosource_android_p.h" +#include "jnipositioning.h" + +Q_DECLARE_METATYPE(QGeoSatelliteInfo) +Q_DECLARE_METATYPE(QList) + +#define UPDATE_FROM_COLD_START 2*60*1000 + +QGeoSatelliteInfoSourceAndroid::QGeoSatelliteInfoSourceAndroid(QObject *parent) : + QGeoSatelliteInfoSource(parent), m_error(NoError), updatesRunning(false) +{ + qRegisterMetaType< QGeoSatelliteInfo >(); + qRegisterMetaType< QList >(); + androidClassKeyForUpdate = AndroidPositioning::registerPositionInfoSource(this); + androidClassKeyForSingleRequest = AndroidPositioning::registerPositionInfoSource(this); + + requestTimer.setSingleShot(true); + QObject::connect(&requestTimer, SIGNAL(timeout()), + this, SLOT(requestTimeout())); +} + +QGeoSatelliteInfoSourceAndroid::~QGeoSatelliteInfoSourceAndroid() +{ + stopUpdates(); + + if (requestTimer.isActive()) { + requestTimer.stop(); + AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest); + } + + AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForUpdate); + AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForSingleRequest); +} + + +void QGeoSatelliteInfoSourceAndroid::setUpdateInterval(int msec) +{ + int previousInterval = updateInterval(); + msec = (((msec > 0) && (msec < minimumUpdateInterval())) || msec < 0)? minimumUpdateInterval() : msec; + + if (msec == previousInterval) + return; + + QGeoSatelliteInfoSource::setUpdateInterval(msec); + + if (updatesRunning) + reconfigureRunningSystem(); +} + +int QGeoSatelliteInfoSourceAndroid::minimumUpdateInterval() const +{ + return 50; +} + +QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSourceAndroid::error() const +{ + return m_error; +} + +void QGeoSatelliteInfoSourceAndroid::startUpdates() +{ + if (updatesRunning) + return; + + updatesRunning = true; + + QGeoSatelliteInfoSource::Error error = AndroidPositioning::startSatelliteUpdates( + androidClassKeyForUpdate, false, updateInterval()); + if (error != QGeoSatelliteInfoSource::NoError) { + updatesRunning = false; + m_error = error; + emit QGeoSatelliteInfoSource::error(m_error); + } +} + +void QGeoSatelliteInfoSourceAndroid::stopUpdates() +{ + if (!updatesRunning) + return; + + updatesRunning = false; + AndroidPositioning::stopUpdates(androidClassKeyForUpdate); +} + +void QGeoSatelliteInfoSourceAndroid::requestUpdate(int timeout) +{ + if (requestTimer.isActive()) + return; + + if (timeout != 0 && timeout < minimumUpdateInterval()) { + emit requestTimeout(); + return; + } + + if (timeout == 0) + timeout = UPDATE_FROM_COLD_START; + + requestTimer.start(timeout); + + // if updates already running with interval equal or less then timeout + // then we wait for next update coming through + // assume that a single update will not be quicker than regular updates anyway + if (updatesRunning && updateInterval() <= timeout) + return; + + QGeoSatelliteInfoSource::Error error = AndroidPositioning::startSatelliteUpdates( + androidClassKeyForSingleRequest, true, timeout); + if (error != QGeoSatelliteInfoSource::NoError) { + requestTimer.stop(); + m_error = error; + emit QGeoSatelliteInfoSource::error(m_error); + } +} + +void QGeoSatelliteInfoSourceAndroid::processSatelliteUpdateInView(const QList &satsInView, bool isSingleUpdate) +{ + if (!isSingleUpdate) { + //if requested while regular updates were running + if (requestTimer.isActive()) + requestTimer.stop(); + emit QGeoSatelliteInfoSource::satellitesInViewUpdated(satsInView); + return; + } + + m_satsInView = satsInView; +} + +void QGeoSatelliteInfoSourceAndroid::processSatelliteUpdateInUse(const QList &satsInUse, bool isSingleUpdate) +{ + if (!isSingleUpdate) { + //if requested while regular updates were running + if (requestTimer.isActive()) + requestTimer.stop(); + emit QGeoSatelliteInfoSource::satellitesInUseUpdated(satsInUse); + return; + } + + m_satsInUse = satsInUse; +} + +void QGeoSatelliteInfoSourceAndroid::requestTimeout() +{ + AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest); + + const int count = m_satsInView.count(); + if (!count) { + emit requestTimeout(); + return; + } + + emit QGeoSatelliteInfoSource::satellitesInViewUpdated(m_satsInView); + emit QGeoSatelliteInfoSource::satellitesInUseUpdated(m_satsInUse); + + m_satsInUse.clear(); + m_satsInView.clear(); +} + +/* + Updates the system assuming that updateInterval + and/or preferredPositioningMethod have changed. + */ +void QGeoSatelliteInfoSourceAndroid::reconfigureRunningSystem() +{ + if (!updatesRunning) + return; + + stopUpdates(); + startUpdates(); +} + +void QGeoSatelliteInfoSourceAndroid::locationProviderDisabled() +{ + m_error = QGeoSatelliteInfoSource::ClosedError; + emit QGeoSatelliteInfoSource::error(m_error); +} diff --git a/src/plugins/position/android/src/qgeosatelliteinfosource_android_p.h b/src/plugins/position/android/src/qgeosatelliteinfosource_android_p.h new file mode 100644 index 0000000..37c64ad --- /dev/null +++ b/src/plugins/position/android/src/qgeosatelliteinfosource_android_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGEOSATELLITEINFOSOURCEANDROID_H +#define QGEOSATELLITEINFOSOURCEANDROID_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +class QGeoSatelliteInfoSourceAndroid : public QGeoSatelliteInfoSource +{ + Q_OBJECT +public: + explicit QGeoSatelliteInfoSourceAndroid(QObject *parent = 0); + ~QGeoSatelliteInfoSourceAndroid(); + + //From QGeoSatelliteInfoSource + void setUpdateInterval(int msec); + int minimumUpdateInterval() const; + + Error error() const; + +public Q_SLOTS: + void startUpdates(); + void stopUpdates(); + void requestUpdate(int timeout = 0); + + void processSatelliteUpdateInView(const QList &satsInView, bool isSingleUpdate); + void processSatelliteUpdateInUse(const QList &satsInUse, bool isSingleUpdate); + + void locationProviderDisabled(); +private Q_SLOTS: + void requestTimeout(); + +private: + void reconfigureRunningSystem(); + + Error m_error; + int androidClassKeyForUpdate; + int androidClassKeyForSingleRequest; + bool updatesRunning; + + QTimer requestTimer; + QList m_satsInUse; + QList m_satsInView; + +}; + +#endif // QGEOSATELLITEINFOSOURCEANDROID_H diff --git a/src/plugins/position/android/src/src.pro b/src/plugins/position/android/src/src.pro new file mode 100644 index 0000000..36facc5 --- /dev/null +++ b/src/plugins/position/android/src/src.pro @@ -0,0 +1,21 @@ +TARGET = qtposition_android + +QT = core core-private positioning + +HEADERS = \ + positionfactory_android.h \ + qgeopositioninfosource_android_p.h \ + jnipositioning.h \ + qgeosatelliteinfosource_android_p.h + +SOURCES = \ + positionfactory_android.cpp \ + qgeopositioninfosource_android.cpp \ + jnipositioning.cpp \ + qgeosatelliteinfosource_android.cpp + +OTHER_FILES = plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryAndroid +load(qt_plugin) diff --git a/src/plugins/position/corelocation/corelocation.pro b/src/plugins/position/corelocation/corelocation.pro new file mode 100644 index 0000000..6b7ba82 --- /dev/null +++ b/src/plugins/position/corelocation/corelocation.pro @@ -0,0 +1,24 @@ +TARGET = qtposition_cl + +QT = core core-private positioning + +OBJECTIVE_SOURCES += \ + qgeopositioninfosource_cl.mm \ + qgeopositioninfosourcefactory_cl.mm + +HEADERS += \ + qgeopositioninfosource_cl_p.h \ + qgeopositioninfosourcefactory_cl.h + +OTHER_FILES += \ + plugin.json + +LIBS += -framework Foundation -framework CoreLocation + +!darwin { + DISTFILES += $$OBJECTIVE_SOURCES +} + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryCL +load(qt_plugin) diff --git a/src/plugins/position/corelocation/plugin.json b/src/plugins/position/corelocation/plugin.json new file mode 100644 index 0000000..58e3acd --- /dev/null +++ b/src/plugins/position/corelocation/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["corelocation"], + "Provider": "corelocation", + "Position": true, + "Satellite": false, + "Monitor" : false, + "Priority": 1000, + "Testable": false +} diff --git a/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm new file mode 100644 index 0000000..94c5b80 --- /dev/null +++ b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qgeopositioninfosource_cl_p.h" + +#define MINIMUM_UPDATE_INTERVAL 1000 + +@interface PositionLocationDelegate : NSObject +{ + QGeoPositionInfoSourceCL *m_positionInfoSource; +} +@end + +@implementation PositionLocationDelegate +- (id)initWithInfoSource:(QGeoPositionInfoSourceCL*) positionInfoSource +{ + self = [super init]; + if (self) { + m_positionInfoSource = positionInfoSource; + } + return self; +} + +- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation +{ + Q_UNUSED(manager) + Q_UNUSED(oldLocation) + + // Convert location timestamp to QDateTime + QDateTime timeStamp; + NSTimeInterval locationTimeStamp = [newLocation.timestamp timeIntervalSince1970]; + timeStamp.setTime_t((uint) locationTimeStamp); + timeStamp.setTime(timeStamp.time().addMSecs((uint)(locationTimeStamp * 1000) % 1000)); + + // Construct position info from location data + QGeoPositionInfo location(QGeoCoordinate(newLocation.coordinate.latitude, + newLocation.coordinate.longitude, + newLocation.altitude), + timeStamp); + if (newLocation.horizontalAccuracy >= 0) + location.setAttribute(QGeoPositionInfo::HorizontalAccuracy, newLocation.horizontalAccuracy); + if (newLocation.verticalAccuracy >= 0) + location.setAttribute(QGeoPositionInfo::VerticalAccuracy, newLocation.verticalAccuracy); +#ifndef Q_OS_TVOS + if (newLocation.course >= 0) + location.setAttribute(QGeoPositionInfo::Direction, newLocation.course); + if (newLocation.speed >= 0) + location.setAttribute(QGeoPositionInfo::GroundSpeed, newLocation.speed); +#endif + + m_positionInfoSource->locationDataAvailable(location); +} + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error +{ + Q_UNUSED(manager) + m_positionInfoSource->setError(QGeoPositionInfoSource::AccessError); + + qWarning() << QString::fromNSString([error localizedDescription]); + + if ([error code] == 0 + && QString::fromNSString([error domain]) == QStringLiteral("kCLErrorDomain")) + qWarning() << "(is Wi-Fi turned on?)"; +} +@end + +QT_BEGIN_NAMESPACE + +QGeoPositionInfoSourceCL::QGeoPositionInfoSourceCL(QObject *parent) + : QGeoPositionInfoSource(parent) + , m_locationManager(0) + , m_started(false) + , m_updateTimer(0) + , m_updateTimeout(0) + , m_positionError(QGeoPositionInfoSource::NoError) +{ +} + +QGeoPositionInfoSourceCL::~QGeoPositionInfoSourceCL() +{ + stopUpdates(); + [m_locationManager release]; +} + +void QGeoPositionInfoSourceCL::setUpdateInterval(int msec) +{ + // If msec is 0 we send updates as data becomes available, otherwise we force msec to be equal + // to or larger than the minimum update interval. + if (msec != 0 && msec < minimumUpdateInterval()) + msec = minimumUpdateInterval(); + + QGeoPositionInfoSource::setUpdateInterval(msec); + + // Must timeout if update takes longer than specified interval + m_updateTimeout = msec; + if (m_started) setTimeoutInterval(m_updateTimeout); +} + +bool QGeoPositionInfoSourceCL::enableLocationManager() +{ + if (!m_locationManager) { + m_locationManager = [[CLLocationManager alloc] init]; + +#if defined(Q_OS_IOS) || defined(Q_OS_WATCHOS) + if (__builtin_available(watchOS 4.0, *)) { + NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary]; + if (id value = [infoDict objectForKey:@"UIBackgroundModes"]) { + if ([value isKindOfClass:[NSArray class]]) { + NSArray *modes = static_cast(value); + for (id mode in modes) { + if ([@"location" isEqualToString:mode]) { + m_locationManager.allowsBackgroundLocationUpdates = YES; + break; + } + } + } + } + } +#endif + + m_locationManager.desiredAccuracy = kCLLocationAccuracyBest; + m_locationManager.delegate = [[PositionLocationDelegate alloc] initWithInfoSource:this]; + + // These two methods are new in iOS 8. They require NSLocationAlwaysUsageDescription + // and NSLocationWhenInUseUsageDescription to be set in Info.plist to work (methods are + // noop if there are no such entries in plist). +#ifndef Q_OS_MACOS +#ifndef Q_OS_TVOS + [m_locationManager requestAlwaysAuthorization]; +#endif + [m_locationManager requestWhenInUseAuthorization]; +#endif + } + + return (m_locationManager != 0); +} + +void QGeoPositionInfoSourceCL::setTimeoutInterval(int msec) +{ + // Start timeout timer + if (m_updateTimer) killTimer(m_updateTimer); + if (msec > 0) m_updateTimer = startTimer(msec); + else m_updateTimer = 0; +} + +void QGeoPositionInfoSourceCL::startUpdates() +{ + if (enableLocationManager()) { +#ifdef Q_OS_TVOS + [m_locationManager requestLocation]; // service will run long enough for one location update +#else + [m_locationManager startUpdatingLocation]; +#endif + m_started = true; + + setTimeoutInterval(m_updateTimeout); + } else setError(QGeoPositionInfoSource::AccessError); +} + +void QGeoPositionInfoSourceCL::stopUpdates() +{ + if (m_locationManager) { + [m_locationManager stopUpdatingLocation]; + m_started = false; + + // Stop timeout timer + setTimeoutInterval(0); + } else setError(QGeoPositionInfoSource::AccessError); +} + +void QGeoPositionInfoSourceCL::requestUpdate(int timeout) +{ + // Get a single update within timeframe + if (timeout < minimumUpdateInterval() && timeout != 0) + emit updateTimeout(); + else if (enableLocationManager()) { + // This will force LM to generate a new update + [m_locationManager stopUpdatingLocation]; +#ifdef Q_OS_TVOS + [m_locationManager requestLocation]; // service will run long enough for one location update +#else + [m_locationManager startUpdatingLocation]; +#endif + + setTimeoutInterval(timeout); + } else setError(QGeoPositionInfoSource::AccessError); +} + +void QGeoPositionInfoSourceCL::timerEvent( QTimerEvent * event ) +{ + // Update timed out? + if (event->timerId() == m_updateTimer) { + emit updateTimeout(); + + // Only timeout once since last data + setTimeoutInterval(0); + + // Started for single update? + if (!m_started) stopUpdates(); + } +} + +QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceCL::supportedPositioningMethods() const +{ + // CoreLocation doesn't say which positioning method(s) it used + return QGeoPositionInfoSource::AllPositioningMethods; +} + +int QGeoPositionInfoSourceCL::minimumUpdateInterval() const +{ + return MINIMUM_UPDATE_INTERVAL; +} + +void QGeoPositionInfoSourceCL::locationDataAvailable(QGeoPositionInfo location) +{ + // Signal position data available + m_lastUpdate = location; + emit positionUpdated(location); + + // Started for single update? + if (!m_started) stopUpdates(); + // ...otherwise restart timeout timer + else setTimeoutInterval(m_updateTimeout); +} + +QGeoPositionInfo QGeoPositionInfoSourceCL::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const +{ + Q_UNUSED(fromSatellitePositioningMethodsOnly) + + return m_lastUpdate; +} + +QGeoPositionInfoSource::Error QGeoPositionInfoSourceCL::error() const +{ + return m_positionError; +} + +void QGeoPositionInfoSourceCL::setError(QGeoPositionInfoSource::Error positionError) +{ + m_positionError = positionError; + emit QGeoPositionInfoSource::error(positionError); +} + +#include "moc_qgeopositioninfosource_cl_p.cpp" + +QT_END_NAMESPACE diff --git a/src/plugins/position/corelocation/qgeopositioninfosource_cl_p.h b/src/plugins/position/corelocation/qgeopositioninfosource_cl_p.h new file mode 100644 index 0000000..cfd66bf --- /dev/null +++ b/src/plugins/position/corelocation/qgeopositioninfosource_cl_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCECL_H +#define QGEOPOSITIONINFOSOURCECL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#import + +#include "qgeopositioninfosource.h" +#include "qgeopositioninfo.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QGeoPositionInfoSourceCL : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + QGeoPositionInfoSourceCL(QObject *parent = 0); + ~QGeoPositionInfoSourceCL(); + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + PositioningMethods supportedPositioningMethods() const; + + void setUpdateInterval(int msec); + int minimumUpdateInterval() const; + Error error() const; + + void locationDataAvailable(QGeoPositionInfo location); + void setError(QGeoPositionInfoSource::Error positionError); + +private: + bool enableLocationManager(); + void setTimeoutInterval(int msec); + +public Q_SLOTS: + void startUpdates(); + void stopUpdates(); + + void requestUpdate(int timeout = 0); + +protected: + virtual void timerEvent(QTimerEvent *event); + +private: + Q_DISABLE_COPY(QGeoPositionInfoSourceCL); + CLLocationManager *m_locationManager; + bool m_started; + + QGeoPositionInfo m_lastUpdate; + + int m_updateTimer; + int m_updateTimeout; + + QGeoPositionInfoSource::Error m_positionError; +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCECL_H diff --git a/src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.h b/src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.h new file mode 100644 index 0000000..5ab1ce6 --- /dev/null +++ b/src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_CL_H +#define QGEOPOSITIONINFOSOURCEFACTORY_CL_H + +#include +#include + +class QGeoPositionInfoSourceFactoryCL : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +#endif // QGEOPOSITIONINFOSOURCEFACTORY_CL_H diff --git a/src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.mm b/src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.mm new file mode 100644 index 0000000..06a3ad3 --- /dev/null +++ b/src/plugins/position/corelocation/qgeopositioninfosourcefactory_cl.mm @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosource_cl_p.h" +#include "qgeopositioninfosourcefactory_cl.h" + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryCL::positionInfoSource(QObject *parent) +{ + return new QGeoPositionInfoSourceCL(parent); +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryCL::satelliteInfoSource(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryCL::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} diff --git a/src/plugins/position/geoclue/geoclue.pro b/src/plugins/position/geoclue/geoclue.pro new file mode 100644 index 0000000..3f75cbf --- /dev/null +++ b/src/plugins/position/geoclue/geoclue.pro @@ -0,0 +1,38 @@ +TARGET = qtposition_geoclue + +QT = core positioning dbus + +HEADERS += \ + qgeopositioninfosource_geocluemaster.h \ + qgeosatelliteinfosource_geocluemaster.h \ + qgeopositioninfosourcefactory_geoclue.h \ + qgeocluemaster.h \ + geocluetypes.h + +SOURCES += \ + qgeopositioninfosource_geocluemaster.cpp \ + qgeosatelliteinfosource_geocluemaster.cpp \ + qgeopositioninfosourcefactory_geoclue.cpp \ + qgeocluemaster.cpp \ + geocluetypes.cpp + +QDBUSXML2CPP_INTERFACE_HEADER_FLAGS += "-N -i geocluetypes.h" +DBUS_INTERFACES += \ + org.freedesktop.Geoclue.MasterClient.xml \ + org.freedesktop.Geoclue.Master.xml \ + org.freedesktop.Geoclue.Position.xml \ + org.freedesktop.Geoclue.Velocity.xml \ + org.freedesktop.Geoclue.Satellite.xml \ + org.freedesktop.Geoclue.xml + +OTHER_FILES += \ + $$DBUS_INTERFACES + +INCLUDEPATH += $$QT.location.includes $$OUT_PWD + +OTHER_FILES += \ + plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryGeoclue +load(qt_plugin) diff --git a/src/plugins/position/geoclue/geocluetypes.cpp b/src/plugins/position/geoclue/geocluetypes.cpp new file mode 100644 index 0000000..d50e624 --- /dev/null +++ b/src/plugins/position/geoclue/geocluetypes.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "geocluetypes.h" + +const QDBusArgument &dbus_argument_helper(const QDBusArgument &arg, Accuracy &accuracy) +{ + arg.beginStructure(); + qint32 level; + arg >> level; + accuracy.m_level = static_cast(level); + arg >> accuracy.m_horizontal; + arg >> accuracy.m_vertical; + arg.endStructure(); + + return arg; +} + +QT_BEGIN_NAMESPACE + +QDBusArgument &operator<<(QDBusArgument &arg, const Accuracy &accuracy) +{ + arg.beginStructure(); + arg << qint32(accuracy.level()); + arg << accuracy.horizontal(); + arg << accuracy.vertical(); + arg.endStructure(); + + return arg; +} + +const QDBusArgument &operator>>(const QDBusArgument &arg, Accuracy &accuracy) +{ + return dbus_argument_helper(arg, accuracy); +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QGeoSatelliteInfo &si) +{ + qint32 a; + + argument.beginStructure(); + argument >> a; + si.setSatelliteIdentifier(a); + argument >> a; + si.setAttribute(QGeoSatelliteInfo::Elevation, a); + argument >> a; + si.setAttribute(QGeoSatelliteInfo::Azimuth, a); + argument >> a; + si.setSignalStrength(a); + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QList &sis) +{ + sis.clear(); + + argument.beginArray(); + while (!argument.atEnd()) { + QGeoSatelliteInfo si; + argument >> si; + sis.append(si); + } + argument.endArray(); + + return argument; +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue/geocluetypes.h b/src/plugins/position/geoclue/geocluetypes.h new file mode 100644 index 0000000..0e87b73 --- /dev/null +++ b/src/plugins/position/geoclue/geocluetypes.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef GEOCLUETYPES_H +#define GEOCLUETYPES_H + +#include +#include + +class Accuracy +{ +public: + enum Level { + None = 0, + Country, + Region, + Locality, + PostalCode, + Street, + Detailed + }; + + Accuracy() + : m_level(None), m_horizontal(0), m_vertical(0) + { + } + + inline Level level() const { return m_level; } + inline double horizontal() const { return m_horizontal; } + inline double vertical() const { return m_vertical; } + +private: + Level m_level; + double m_horizontal; + double m_vertical; + + friend const QDBusArgument &dbus_argument_helper(const QDBusArgument &arg, Accuracy &accuracy); +}; + +Q_DECLARE_METATYPE(Accuracy) +Q_DECLARE_METATYPE(QList) + + +QT_BEGIN_NAMESPACE + +Q_DECLARE_TYPEINFO(Accuracy, Q_MOVABLE_TYPE); + +QDBusArgument &operator<<(QDBusArgument &arg, const Accuracy &accuracy); +const QDBusArgument &operator>>(const QDBusArgument &arg, Accuracy &accuracy); + +const QDBusArgument &operator>>(const QDBusArgument &arg, QGeoSatelliteInfo &si); +const QDBusArgument &operator>>(const QDBusArgument &arg, QList &sis); + +QT_END_NAMESPACE + +#endif // GEOCLUETYPES_H + diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Master.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Master.xml new file mode 100644 index 0000000..e7df140 --- /dev/null +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Master.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.MasterClient.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.MasterClient.xml new file mode 100644 index 0000000..29c9588 --- /dev/null +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.MasterClient.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml new file mode 100644 index 0000000..8f7f70d --- /dev/null +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Position.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml new file mode 100644 index 0000000..b892e26 --- /dev/null +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Satellite.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + ' + + + + + diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.Velocity.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Velocity.xml new file mode 100644 index 0000000..a1be122 --- /dev/null +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.Velocity.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/position/geoclue/org.freedesktop.Geoclue.xml b/src/plugins/position/geoclue/org.freedesktop.Geoclue.xml new file mode 100644 index 0000000..c9b6f63 --- /dev/null +++ b/src/plugins/position/geoclue/org.freedesktop.Geoclue.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/plugins/position/geoclue/plugin.json b/src/plugins/position/geoclue/plugin.json new file mode 100644 index 0000000..82f8afc --- /dev/null +++ b/src/plugins/position/geoclue/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["geoclue"], + "Provider": "geoclue", + "Position": true, + "Satellite": true, + "Monitor": false, + "Priority": 999, + "Testable": false +} diff --git a/src/plugins/position/geoclue/qgeocluemaster.cpp b/src/plugins/position/geoclue/qgeocluemaster.cpp new file mode 100644 index 0000000..962cc7f --- /dev/null +++ b/src/plugins/position/geoclue/qgeocluemaster.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd, author: Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocluemaster.h" + +#include +#include +#include + +#include + +Q_DECLARE_LOGGING_CATEGORY(lcPositioningGeoclue) + +QT_BEGIN_NAMESPACE + +QGeoclueMaster::QGeoclueMaster(QObject *parent) +: QObject(parent), m_master(0), m_provider(0), m_client(0) +{ +} + +QGeoclueMaster::~QGeoclueMaster() +{ + releaseMasterClient(); + + delete m_master; +} + +bool QGeoclueMaster::hasMasterClient() const +{ + return m_client; +} + +bool QGeoclueMaster::createMasterClient(Accuracy::Level accuracyLevel, ResourceFlags resourceFlags) +{ + Q_ASSERT(!m_provider || !m_client); + + if (!m_master) { + qCDebug(lcPositioningGeoclue) << "creating master interface"; + m_master = new OrgFreedesktopGeoclueMasterInterface(QStringLiteral("org.freedesktop.Geoclue.Master"), + QStringLiteral("/org/freedesktop/Geoclue/Master"), + QDBusConnection::sessionBus()); + } + + qCDebug(lcPositioningGeoclue) << "creating client"; + QDBusPendingReply client = m_master->Create(); + if (client.isError()) { + QDBusError e = client.error(); + qCritical("Failed to create Geoclue client interface. Geoclue error: %s", + qPrintable(e.errorString(e.type()))); + return false; + } + + qCDebug(lcPositioningGeoclue) << "Geoclue client path:" << client.value().path(); + + m_provider = new OrgFreedesktopGeoclueInterface(QStringLiteral("org.freedesktop.Geoclue.Master"), + client.value().path(), QDBusConnection::sessionBus()); + m_provider->AddReference(); + + m_client = new OrgFreedesktopGeoclueMasterClientInterface(QStringLiteral("org.freedesktop.Geoclue.Master"), + client.value().path(), + QDBusConnection::sessionBus()); + + connect(m_client, SIGNAL(PositionProviderChanged(QString,QString,QString,QString)), + this, SIGNAL(positionProviderChanged(QString,QString,QString,QString))); + + QDBusPendingReply<> reply = m_client->SetRequirements(accuracyLevel, 0, true, resourceFlags); + if (reply.isError()) { + QDBusError e = reply.error(); + qCritical("Failed to set Geoclue positioning requirements. Geoclue error: %s", + qPrintable(e.errorString(e.type()))); + + releaseMasterClient(); + return false; + } + + // Need to create the master position interface even though it will not be used, otherwise + // GetPositionProvider always returns empty strings. + reply = m_client->PositionStart(); + if (reply.isError()) { + QDBusError e = reply.error(); + qCritical("Failed to start positioning. Geoclue error: %s", + qPrintable(e.errorString(e.type()))); + + releaseMasterClient(); + return false; + } + + return true; +} + +void QGeoclueMaster::releaseMasterClient() +{ + if (m_provider) + m_provider->RemoveReference(); + delete m_provider; + m_provider = 0; + delete m_client; + m_client = 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue/qgeocluemaster.h b/src/plugins/position/geoclue/qgeocluemaster.h new file mode 100644 index 0000000..c623dbd --- /dev/null +++ b/src/plugins/position/geoclue/qgeocluemaster.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd, author: Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCLUEMASTER_H +#define QGEOCLUEMASTER_H + +#include "geocluetypes.h" + +#include + +class OrgFreedesktopGeoclueMasterInterface; +class OrgFreedesktopGeoclueInterface; +class OrgFreedesktopGeoclueMasterClientInterface; + +QT_BEGIN_NAMESPACE + +class QGeoclueMaster : public QObject +{ + Q_OBJECT + +public: + QGeoclueMaster(QObject *parent = 0); + ~QGeoclueMaster(); + + enum ResourceFlag + { + ResourceNone = 0, + ResourceNetwork = 1 << 0, + ResourceCell = 1 << 1, + ResourceGps = 1 << 2, + ResourceAll = (1 << 10) - 1 + }; + + Q_DECLARE_FLAGS(ResourceFlags, ResourceFlag) + + bool hasMasterClient() const; + bool createMasterClient(Accuracy::Level accuracyLevel, ResourceFlags resourceFlags); + void releaseMasterClient(); + +signals: + void positionProviderChanged(const QString &name, const QString &description, + const QString &service, const QString &path); + +private: + OrgFreedesktopGeoclueMasterInterface *m_master; + OrgFreedesktopGeoclueInterface *m_provider; + OrgFreedesktopGeoclueMasterClientInterface *m_client; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoclueMaster::ResourceFlags) + +QT_END_NAMESPACE + +#endif // QGEOCLUEMASTER_H diff --git a/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp new file mode 100644 index 0000000..6dfdc37 --- /dev/null +++ b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.cpp @@ -0,0 +1,495 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosource_geocluemaster.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef QT_NO_DATASTREAM +#include +#endif + +Q_DECLARE_LOGGING_CATEGORY(lcPositioningGeoclue) + +#define MINIMUM_UPDATE_INTERVAL 1000 +#define UPDATE_TIMEOUT_COLD_START 120000 + +QT_BEGIN_NAMESPACE + +namespace +{ + +double knotsToMetersPerSecond(double knots) +{ + return knots * 1852.0 / 3600.0; +} + +} + +QGeoPositionInfoSourceGeoclueMaster::QGeoPositionInfoSourceGeoclueMaster(QObject *parent) +: QGeoPositionInfoSource(parent), m_master(new QGeoclueMaster(this)), m_provider(0), m_pos(0), + m_vel(0), m_requestTimer(this), m_lastVelocityIsFresh(false), m_regularUpdateTimedOut(false), + m_lastVelocity(qQNaN()), m_lastDirection(qQNaN()), m_lastClimb(qQNaN()), m_lastPositionFromSatellite(false), + m_running(false), m_error(NoError) +{ + qDBusRegisterMetaType(); + +#ifndef QT_NO_DATASTREAM + // Load the last known location + QFile file(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + + QStringLiteral("/qtposition-geoclue")); + if (file.open(QIODevice::ReadOnly)) { + QDataStream out(&file); + out >> m_lastPosition; + } +#endif + + connect(m_master, SIGNAL(positionProviderChanged(QString,QString,QString,QString)), + this, SLOT(positionProviderChanged(QString,QString,QString,QString))); + + m_requestTimer.setSingleShot(true); + connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout())); + + setPreferredPositioningMethods(AllPositioningMethods); +} + +QGeoPositionInfoSourceGeoclueMaster::~QGeoPositionInfoSourceGeoclueMaster() +{ +#if !defined(QT_NO_DATASTREAM) && QT_CONFIG(temporaryfile) + if (m_lastPosition.isValid()) { + QSaveFile file(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + + QStringLiteral("/qtposition-geoclue")); + if (file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + QDataStream out(&file); + // Only save position and timestamp. + out << QGeoPositionInfo(m_lastPosition.coordinate(), m_lastPosition.timestamp()); + file.commit(); + } + } +#endif + + cleanupPositionSource(); +} + +void QGeoPositionInfoSourceGeoclueMaster::positionUpdateFailed() +{ + qCDebug(lcPositioningGeoclue) << "position update failed."; + + m_lastVelocityIsFresh = false; + if (m_running && !m_regularUpdateTimedOut) { + m_regularUpdateTimedOut = true; + emit updateTimeout(); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::updatePosition(PositionFields fields, int timestamp, + double latitude, double longitude, + double altitude, Accuracy accuracy) +{ + if (m_requestTimer.isActive()) + m_requestTimer.stop(); + + QGeoCoordinate coordinate(latitude, longitude); + if (fields & Altitude) + coordinate.setAltitude(altitude); + + m_lastPosition = QGeoPositionInfo(coordinate, QDateTime::fromTime_t(timestamp)); + + m_lastPositionFromSatellite = accuracy.level() == Accuracy::Detailed; + + if (!qIsNaN(accuracy.horizontal())) + m_lastPosition.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy.horizontal()); + if (!qIsNaN(accuracy.vertical())) + m_lastPosition.setAttribute(QGeoPositionInfo::VerticalAccuracy, accuracy.vertical()); + + if (m_lastVelocityIsFresh) { + if (!qIsNaN(m_lastVelocity)) + m_lastPosition.setAttribute(QGeoPositionInfo::GroundSpeed, m_lastVelocity); + if (!qIsNaN(m_lastDirection)) + m_lastPosition.setAttribute(QGeoPositionInfo::Direction, m_lastDirection); + if (!qIsNaN(m_lastClimb)) + m_lastPosition.setAttribute(QGeoPositionInfo::VerticalSpeed, m_lastClimb); + m_lastVelocityIsFresh = false; + } + + m_regularUpdateTimedOut = false; + + emit positionUpdated(m_lastPosition); + + qCDebug(lcPositioningGeoclue) << m_lastPosition; + + // Only stop positioning if regular updates not active. + if (!m_running) { + cleanupPositionSource(); + m_master->releaseMasterClient(); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::velocityUpdateFailed() +{ + qCDebug(lcPositioningGeoclue) << "velocity update failed."; + + // Set the velocitydata non-fresh. + m_lastVelocityIsFresh = false; +} + +void QGeoPositionInfoSourceGeoclueMaster::updateVelocity(VelocityFields fields, int timestamp, + double speed, double direction, + double climb) +{ + Q_UNUSED(timestamp) + + // Store the velocity and mark it as fresh. Simple but hopefully adequate. + m_lastVelocity = (fields & Speed) ? knotsToMetersPerSecond(speed) : qQNaN(); + m_lastDirection = (fields & Direction) ? direction : qQNaN(); + m_lastClimb = (fields & Climb) ? climb : qQNaN(); + m_lastVelocityIsFresh = true; + + qCDebug(lcPositioningGeoclue) << m_lastVelocity << m_lastDirection << m_lastClimb; +} + +void QGeoPositionInfoSourceGeoclueMaster::cleanupPositionSource() +{ + qCDebug(lcPositioningGeoclue) << "cleaning up position source"; + + if (m_provider) + m_provider->RemoveReference(); + delete m_provider; + m_provider = 0; + delete m_pos; + m_pos = 0; + delete m_vel; + m_vel = 0; +} + +void QGeoPositionInfoSourceGeoclueMaster::setOptions() +{ + if (!m_provider) + return; + + QVariantMap options; + options.insert(QStringLiteral("UpdateInterval"), updateInterval()); + + m_provider->SetOptions(options); +} + +void QGeoPositionInfoSourceGeoclueMaster::setUpdateInterval(int msec) +{ + QGeoPositionInfoSource::setUpdateInterval(qMax(minimumUpdateInterval(), msec)); + setOptions(); +} + +void QGeoPositionInfoSourceGeoclueMaster::setPreferredPositioningMethods(PositioningMethods methods) +{ + PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); + QGeoPositionInfoSource::setPreferredPositioningMethods(methods); + if (previousPreferredPositioningMethods == preferredPositioningMethods()) + return; + + qCDebug(lcPositioningGeoclue) << "requested to set methods to" << methods + << ", and set them to:" << preferredPositioningMethods(); + + m_lastVelocityIsFresh = false; + m_regularUpdateTimedOut = false; + + // Don't start Geoclue provider until necessary. Don't currently have a master client, no need + // no recreate one. + if (!m_master->hasMasterClient()) + return; + + // Free potential previous sources, because new requirements can't be set for the client + // (creating a position object after changing requirements seems to fail). + cleanupPositionSource(); + m_master->releaseMasterClient(); + + // Restart Geoclue provider with new requirements. + configurePositionSource(); + setOptions(); +} + +QGeoPositionInfo QGeoPositionInfoSourceGeoclueMaster::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const +{ + if (fromSatellitePositioningMethodsOnly && !m_lastPositionFromSatellite) + return QGeoPositionInfo(); + + return m_lastPosition; +} + +QGeoPositionInfoSourceGeoclueMaster::PositioningMethods QGeoPositionInfoSourceGeoclueMaster::supportedPositioningMethods() const +{ + return AllPositioningMethods; +} + +void QGeoPositionInfoSourceGeoclueMaster::startUpdates() +{ + if (m_running) { + qCDebug(lcPositioningGeoclue) << "already running."; + return; + } + + m_running = true; + + qCDebug(lcPositioningGeoclue) << "starting updates"; + + // Start Geoclue provider. + if (!m_master->hasMasterClient()) { + configurePositionSource(); + setOptions(); + } + + // Emit last known position on start. + if (m_lastPosition.isValid()) { + QMetaObject::invokeMethod(this, "positionUpdated", Qt::QueuedConnection, + Q_ARG(QGeoPositionInfo, m_lastPosition)); + } +} + +int QGeoPositionInfoSourceGeoclueMaster::minimumUpdateInterval() const +{ + return MINIMUM_UPDATE_INTERVAL; +} + +void QGeoPositionInfoSourceGeoclueMaster::stopUpdates() +{ + if (!m_running) { + qCDebug(lcPositioningGeoclue) << "already stopped."; + return; + } + + qCDebug(lcPositioningGeoclue) << "stopping updates"; + + if (m_pos) { + disconnect(m_pos, SIGNAL(PositionChanged(qint32,qint32,double,double,double,Accuracy)), + this, SLOT(positionChanged(qint32,qint32,double,double,double,Accuracy))); + } + + if (m_vel) { + disconnect(m_vel, SIGNAL(VelocityChanged(qint32,qint32,double,double,double)), + this, SLOT(velocityChanged(qint32,qint32,double,double,double))); + } + + m_running = false; + + // Only stop positioning if single update not requested. + if (!m_requestTimer.isActive()) { + cleanupPositionSource(); + m_master->releaseMasterClient(); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::requestUpdate(int timeout) +{ + if (timeout < minimumUpdateInterval() && timeout != 0) { + emit updateTimeout(); + return; + } + if (m_requestTimer.isActive()) { + qCDebug(lcPositioningGeoclue) << "request timer was active, ignoring startUpdates."; + return; + } + + if (!m_master->hasMasterClient()) { + configurePositionSource(); + setOptions(); + } + + // Create better logic for timeout value (specs leave it impl dependant). + // Especially if there are active updates ongoing, there is no point of waiting + // for whole cold start time. + m_requestTimer.start(timeout ? timeout : UPDATE_TIMEOUT_COLD_START); + + if (m_pos) { + QDBusPendingReply reply = m_pos->GetPosition(); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(getPositionFinished(QDBusPendingCallWatcher*))); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::positionProviderChanged(const QString &name, + const QString &description, + const QString &service, + const QString &path) +{ + Q_UNUSED(name) + Q_UNUSED(description) + + cleanupPositionSource(); + + if (service.isEmpty() || path.isEmpty()) { + if (!m_regularUpdateTimedOut) { + m_regularUpdateTimedOut = true; + emit updateTimeout(); + } + return; + } + + qCDebug(lcPositioningGeoclue) << "position provider changed to" << name; + + m_provider = new OrgFreedesktopGeoclueInterface(service, path, QDBusConnection::sessionBus()); + m_provider->AddReference(); + + m_pos = new OrgFreedesktopGeocluePositionInterface(service, path, QDBusConnection::sessionBus()); + + if (m_running) { + connect(m_pos, SIGNAL(PositionChanged(qint32,qint32,double,double,double,Accuracy)), + this, SLOT(positionChanged(qint32,qint32,double,double,double,Accuracy))); + } + + // Get the current position immediately. + QDBusPendingReply reply = m_pos->GetPosition(); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(getPositionFinished(QDBusPendingCallWatcher*))); + + setOptions(); + + m_vel = new OrgFreedesktopGeoclueVelocityInterface(service, path, QDBusConnection::sessionBus()); + if (m_vel->isValid() && m_running) { + connect(m_vel, SIGNAL(VelocityChanged(qint32,qint32,double,double,double)), + this, SLOT(velocityChanged(qint32,qint32,double,double,double))); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::requestUpdateTimeout() +{ + qCDebug(lcPositioningGeoclue) << "request update timeout occurred."; + + // If we end up here, there has not been valid position update. + emit updateTimeout(); + + // Only stop positioning if regular updates not active. + if (!m_running) { + cleanupPositionSource(); + m_master->releaseMasterClient(); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::getPositionFinished(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply reply = *watcher; + watcher->deleteLater(); + + if (reply.isError()) + return; + + PositionFields fields = static_cast(reply.argumentAt<0>()); + + qCDebug(lcPositioningGeoclue) << "got position update with fields" << int(fields); + + if (fields & Latitude && fields & Longitude) { + qint32 timestamp = reply.argumentAt<1>(); + double latitude = reply.argumentAt<2>(); + double longitude = reply.argumentAt<3>(); + double altitude = reply.argumentAt<4>(); + Accuracy accuracy = reply.argumentAt<5>(); + updatePosition(fields, timestamp, latitude, longitude, altitude, accuracy); + } +} + +void QGeoPositionInfoSourceGeoclueMaster::positionChanged(qint32 fields, qint32 timestamp, double latitude, double longitude, double altitude, const Accuracy &accuracy) +{ + PositionFields pFields = static_cast(fields); + + qCDebug(lcPositioningGeoclue) << "position changed with fields" << fields; + + if (pFields & Latitude && pFields & Longitude) + updatePosition(pFields, timestamp, latitude, longitude, altitude, accuracy); + else + positionUpdateFailed(); +} + +void QGeoPositionInfoSourceGeoclueMaster::velocityChanged(qint32 fields, qint32 timestamp, double speed, double direction, double climb) +{ + VelocityFields vFields = static_cast(fields); + + if (vFields == NoVelocityFields) + velocityUpdateFailed(); + else + updateVelocity(vFields, timestamp, speed, direction, climb); +} + +void QGeoPositionInfoSourceGeoclueMaster::configurePositionSource() +{ + qCDebug(lcPositioningGeoclue); + + bool created = false; + + switch (preferredPositioningMethods()) { + case SatellitePositioningMethods: + created = m_master->createMasterClient(Accuracy::Detailed, QGeoclueMaster::ResourceGps); + break; + case NonSatellitePositioningMethods: + created = m_master->createMasterClient(Accuracy::None, QGeoclueMaster::ResourceCell | QGeoclueMaster::ResourceNetwork); + break; + case AllPositioningMethods: + created = m_master->createMasterClient(Accuracy::None, QGeoclueMaster::ResourceAll); + break; + default: + qWarning("QGeoPositionInfoSourceGeoclueMaster unknown preferred method."); + m_error = UnknownSourceError; + emit QGeoPositionInfoSource::error(m_error); + return; + } + + if (!created) { + m_error = UnknownSourceError; + emit QGeoPositionInfoSource::error(m_error); + } +} + +QGeoPositionInfoSource::Error QGeoPositionInfoSourceGeoclueMaster::error() const +{ + return m_error; +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h new file mode 100644 index 0000000..b9c4774 --- /dev/null +++ b/src/plugins/position/geoclue/qgeopositioninfosource_geocluemaster.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCE_GEOCLUEMASTER_H +#define QGEOPOSITIONINFOSOURCE_GEOCLUEMASTER_H + +#include "qgeocluemaster.h" +#include "geocluetypes.h" + +#include +#include + +class OrgFreedesktopGeoclueInterface; +class OrgFreedesktopGeocluePositionInterface; +class OrgFreedesktopGeoclueVelocityInterface; + +QT_BEGIN_NAMESPACE + +class QDBusPendingCallWatcher; + +class QGeoPositionInfoSourceGeoclueMaster : public QGeoPositionInfoSource +{ + Q_OBJECT + +public: + explicit QGeoPositionInfoSourceGeoclueMaster(QObject *parent = 0); + ~QGeoPositionInfoSourceGeoclueMaster(); + + // From QGeoPositionInfoSource + void setUpdateInterval(int msec) override; + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const override; + PositioningMethods supportedPositioningMethods() const override; + void setPreferredPositioningMethods(PositioningMethods methods) override; + int minimumUpdateInterval() const override; + + Error error() const override; + + void startUpdates() override; + void stopUpdates() override; + void requestUpdate(int timeout = 5000) override; + +private slots: + void positionProviderChanged(const QString &name, const QString &description, + const QString &service, const QString &path); + void requestUpdateTimeout(); + + void getPositionFinished(QDBusPendingCallWatcher *watcher); + void positionChanged(qint32 fields, qint32 timestamp, double latitude, double longitude, + double altitude, const Accuracy &accuracy); + void velocityChanged(qint32 fields, qint32 timestamp, double speed, double direction, + double climb); + +private: + void configurePositionSource(); + void cleanupPositionSource(); + void setOptions(); + + enum PositionField + { + NoPositionFields = 0, + Latitude = 1 << 0, + Longitude = 1 << 1, + Altitude = 1 << 2 + }; + Q_DECLARE_FLAGS(PositionFields, PositionField) + + void updatePosition(PositionFields fields, int timestamp, double latitude, + double longitude, double altitude, Accuracy accuracy); + void positionUpdateFailed(); + + enum VelocityField + { + NoVelocityFields = 0, + Speed = 1 << 0, + Direction = 1 << 1, + Climb = 1 << 2 + }; + Q_DECLARE_FLAGS(VelocityFields, VelocityField) + + void updateVelocity(VelocityFields fields, int timestamp, double speed, double direction, + double climb); + void velocityUpdateFailed(); + +private: + QGeoclueMaster *m_master; + + OrgFreedesktopGeoclueInterface *m_provider; + OrgFreedesktopGeocluePositionInterface *m_pos; + OrgFreedesktopGeoclueVelocityInterface *m_vel; + + QTimer m_requestTimer; + bool m_lastVelocityIsFresh; + bool m_regularUpdateTimedOut; + double m_lastVelocity; + double m_lastDirection; + double m_lastClimb; + bool m_lastPositionFromSatellite; + QGeoPositionInfo m_lastPosition; + bool m_running; + QGeoPositionInfoSource::Error m_error; +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCE_GEOCLUEMASTER_H diff --git a/src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.cpp b/src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.cpp new file mode 100644 index 0000000..d39d5f1 --- /dev/null +++ b/src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosourcefactory_geoclue.h" +#include "qgeopositioninfosource_geocluemaster.h" +#include "qgeosatelliteinfosource_geocluemaster.h" + +#include + +Q_LOGGING_CATEGORY(lcPositioningGeoclue, "qt.positioning.geoclue") + +QT_BEGIN_NAMESPACE + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryGeoclue::positionInfoSource(QObject *parent) +{ + return new QGeoPositionInfoSourceGeoclueMaster(parent); +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryGeoclue::satelliteInfoSource(QObject *parent) +{ + return new QGeoSatelliteInfoSourceGeoclueMaster(parent); +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryGeoclue::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent) + return 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.h b/src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.h new file mode 100644 index 0000000..d27721d --- /dev/null +++ b/src/plugins/position/geoclue/qgeopositioninfosourcefactory_geoclue.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_GEOCLUE_H +#define QGEOPOSITIONINFOSOURCEFACTORY_GEOCLUE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +/* + Qt Positioning plugin for Geoclue. This plugin supports Geoclue version 0.12.99. +*/ +class QGeoPositionInfoSourceFactoryGeoclue : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + + Q_INTERFACES(QGeoPositionInfoSourceFactory) + +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent) override; + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent) override; + QGeoAreaMonitorSource *areaMonitor(QObject *parent) override; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.cpp b/src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.cpp new file mode 100644 index 0000000..b7524d8 --- /dev/null +++ b/src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd, author: Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeosatelliteinfosource_geocluemaster.h" + +#include +#include + +#include +#include + +Q_DECLARE_LOGGING_CATEGORY(lcPositioningGeoclue) + +#define MINIMUM_UPDATE_INTERVAL 1000 + +QT_BEGIN_NAMESPACE + +QGeoSatelliteInfoSourceGeoclueMaster::QGeoSatelliteInfoSourceGeoclueMaster(QObject *parent) +: QGeoSatelliteInfoSource(parent), m_master(new QGeoclueMaster(this)), m_provider(0), m_sat(0), + m_requestTimer(this), m_error(NoError), m_satellitesChangedConnected(false), m_running(false) +{ + connect(m_master, SIGNAL(positionProviderChanged(QString,QString,QString,QString)), + this, SLOT(positionProviderChanged(QString,QString,QString,QString))); + + m_requestTimer.setSingleShot(true); + connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout())); +} + +QGeoSatelliteInfoSourceGeoclueMaster::~QGeoSatelliteInfoSourceGeoclueMaster() +{ + cleanupSatelliteSource(); +} + +int QGeoSatelliteInfoSourceGeoclueMaster::minimumUpdateInterval() const +{ + return MINIMUM_UPDATE_INTERVAL; +} + +void QGeoSatelliteInfoSourceGeoclueMaster::setUpdateInterval(int msec) +{ + if (msec < 0 || (msec > 0 && msec < MINIMUM_UPDATE_INTERVAL)) + msec = MINIMUM_UPDATE_INTERVAL; + + QGeoSatelliteInfoSource::setUpdateInterval(msec); +} + +QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSourceGeoclueMaster::error() const +{ + return m_error; +} + +void QGeoSatelliteInfoSourceGeoclueMaster::startUpdates() +{ + if (m_running) + return; + + m_running = true; + + // Start Geoclue provider. + if (!m_master->hasMasterClient()) + configureSatelliteSource(); + + m_requestTimer.start(qMax(updateInterval(), minimumUpdateInterval())); +} + +void QGeoSatelliteInfoSourceGeoclueMaster::stopUpdates() +{ + if (!m_running) + return; + + if (m_sat) { + disconnect(m_sat, SIGNAL(SatelliteChanged(qint32,qint32,qint32,QList,QList)), + this, SLOT(satelliteChanged(qint32,qint32,qint32,QList,QList))); + } + + m_running = false; + + // Only stop positioning if single update not requested. + if (!m_requestTimer.isActive()) { + cleanupSatelliteSource(); + m_master->releaseMasterClient(); + } +} + +void QGeoSatelliteInfoSourceGeoclueMaster::requestUpdate(int timeout) +{ + if (timeout < minimumUpdateInterval() && timeout != 0) { + emit requestTimeout(); + return; + } + + if (m_requestTimer.isActive()) + return; + + if (!m_master->hasMasterClient()) + configureSatelliteSource(); + + m_requestTimer.start(qMax(timeout, minimumUpdateInterval())); + + if (m_sat) { + QDBusPendingReply, QList > reply = + m_sat->GetSatellite(); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(reply, this); + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)), + this, SLOT(getSatelliteFinished(QDBusPendingCallWatcher*))); + } +} + +void QGeoSatelliteInfoSourceGeoclueMaster::updateSatelliteInfo(int timestamp, int satellitesUsed, + int satellitesVisible, + const QList &usedPrn, + const QList &satInfos) +{ + Q_UNUSED(timestamp) + + QList inUse; + + foreach (const QGeoSatelliteInfo &si, satInfos) + if (usedPrn.contains(si.satelliteIdentifier())) + inUse.append(si); + + if (satInfos.length() != satellitesVisible) { + qWarning("QGeoSatelliteInfoSourceGeoclueMaster number of in view QGeoSatelliteInfos (%d) " + "does not match expected number of in view satellites (%d).", satInfos.length(), + satellitesVisible); + } + + if (inUse.length() != satellitesUsed) { + qWarning("QGeoSatelliteInfoSourceGeoclueMaster number of in use QGeoSatelliteInfos (%d) " + "does not match expected number of in use satellites (%d).", inUse.length(), + satellitesUsed); + } + + m_inView = satInfos; + emit satellitesInViewUpdated(m_inView); + + m_inUse = inUse; + emit satellitesInUseUpdated(m_inUse); + + m_requestTimer.start(qMax(updateInterval(), minimumUpdateInterval())); +} + +void QGeoSatelliteInfoSourceGeoclueMaster::requestUpdateTimeout() +{ + // If we end up here, there has not been a valid satellite info update. + if (m_running) { + m_inView.clear(); + m_inUse.clear(); + emit satellitesInViewUpdated(m_inView); + emit satellitesInUseUpdated(m_inUse); + } else { + emit requestTimeout(); + + // Only stop satellite info if regular updates not active. + cleanupSatelliteSource(); + m_master->releaseMasterClient(); + } +} + +void QGeoSatelliteInfoSourceGeoclueMaster::getSatelliteFinished(QDBusPendingCallWatcher *watcher) +{ + QDBusPendingReply, QList > reply = *watcher; + watcher->deleteLater(); + + if (reply.isError()) + return; + + m_requestTimer.stop(); + updateSatelliteInfo(reply.argumentAt<0>(), reply.argumentAt<1>(), reply.argumentAt<2>(), + reply.argumentAt<3>(), reply.argumentAt<4>()); +} + +void QGeoSatelliteInfoSourceGeoclueMaster::satelliteChanged(int timestamp, int satellitesUsed, int satellitesVisible, const QList &usedPrn, const QList &satInfos) +{ + updateSatelliteInfo(timestamp, satellitesUsed, satellitesVisible, usedPrn, satInfos); +} + +void QGeoSatelliteInfoSourceGeoclueMaster::positionProviderChanged(const QString &name, + const QString &description, + const QString &service, + const QString &path) +{ + Q_UNUSED(name) + Q_UNUSED(description) + + cleanupSatelliteSource(); + + QString providerService; + QString providerPath; + + if (service.isEmpty() || path.isEmpty()) { + // No valid position provider has been selected. This probably means that the GPS provider + // has not yet obtained a position fix. It can still provide satellite information though. + if (!m_satellitesChangedConnected) { + QDBusConnection conn = QDBusConnection::sessionBus(); + conn.connect(QString(), QString(), QStringLiteral("org.freedesktop.Geoclue.Satellite"), + QStringLiteral("SatelliteChanged"), this, + SLOT(satelliteChanged(QDBusMessage))); + m_satellitesChangedConnected = true; + return; + } + } else { + if (m_satellitesChangedConnected) { + QDBusConnection conn = QDBusConnection::sessionBus(); + conn.disconnect(QString(), QString(), + QStringLiteral("org.freedesktop.Geoclue.Satellite"), + QStringLiteral("SatelliteChanged"), this, + SLOT(satelliteChanged(QDBusMessage))); + m_satellitesChangedConnected = false; + } + + providerService = service; + providerPath = path; + } + + if (providerService.isEmpty() || providerPath.isEmpty()) { + m_error = AccessError; + emit QGeoSatelliteInfoSource::error(m_error); + return; + } + + m_provider = new OrgFreedesktopGeoclueInterface(providerService, providerPath, QDBusConnection::sessionBus()); + m_provider->AddReference(); + + m_sat = new OrgFreedesktopGeoclueSatelliteInterface(providerService, providerPath, QDBusConnection::sessionBus()); + + if (m_running) { + connect(m_sat, SIGNAL(SatelliteChanged(qint32,qint32,qint32,QList,QList)), + this, SLOT(satelliteChanged(qint32,qint32,qint32,QList,QList))); + } +} + +void QGeoSatelliteInfoSourceGeoclueMaster::satelliteChanged(const QDBusMessage &message) +{ + QVariantList arguments = message.arguments(); + if (arguments.length() != 5) + return; + + int timestamp = arguments.at(0).toInt(); + int usedSatellites = arguments.at(1).toInt(); + int visibleSatellites = arguments.at(2).toInt(); + + QDBusArgument dbusArgument = arguments.at(3).value(); + + QList usedPrn; + dbusArgument >> usedPrn; + + dbusArgument = arguments.at(4).value(); + + QList satelliteInfos; + dbusArgument >> satelliteInfos; + + satelliteChanged(timestamp, usedSatellites, visibleSatellites, usedPrn, satelliteInfos); +} + +void QGeoSatelliteInfoSourceGeoclueMaster::configureSatelliteSource() +{ + if (!m_master->createMasterClient(Accuracy::Detailed, QGeoclueMaster::ResourceGps)) { + m_error = UnknownSourceError; + emit QGeoSatelliteInfoSource::error(m_error); + } +} + +void QGeoSatelliteInfoSourceGeoclueMaster::cleanupSatelliteSource() +{ + if (m_provider) + m_provider->RemoveReference(); + delete m_provider; + m_provider = 0; + delete m_sat; + m_sat = 0; +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.h b/src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.h new file mode 100644 index 0000000..254a17a --- /dev/null +++ b/src/plugins/position/geoclue/qgeosatelliteinfosource_geocluemaster.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd, author: Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSATELLITEINFOSOURCE_GEOCLUEMASTER_H +#define QGEOSATELLITEINFOSOURCE_GEOCLUEMASTER_H + +#include "qgeocluemaster.h" + +#include +#include + +class OrgFreedesktopGeoclueInterface; +class OrgFreedesktopGeoclueSatelliteInterface; + +QT_BEGIN_NAMESPACE + +class QDBusMessage; +class QDBusPendingCallWatcher; + +class QGeoSatelliteInfoSourceGeoclueMaster : public QGeoSatelliteInfoSource +{ + Q_OBJECT + +public: + explicit QGeoSatelliteInfoSourceGeoclueMaster(QObject *parent = 0); + ~QGeoSatelliteInfoSourceGeoclueMaster(); + + int minimumUpdateInterval() const override; + void setUpdateInterval(int msec) override; + + Error error() const override; + + void startUpdates() override; + void stopUpdates() override; + void requestUpdate(int timeout = 0) override; + +private slots: + void positionProviderChanged(const QString &name, const QString &description, + const QString &service, const QString &path); + void requestUpdateTimeout(); + + void getSatelliteFinished(QDBusPendingCallWatcher *watcher); + void satelliteChanged(int timestamp, int satellitesUsed, int satellitesVisible, + const QList &usedPrn, const QList &satInfos); + void satelliteChanged(const QDBusMessage &message); + +private: + void configureSatelliteSource(); + void cleanupSatelliteSource(); + + void updateSatelliteInfo(int timestamp, int satellitesUsed, int satellitesVisible, + const QList &usedPrn, const QList &satInfos); + + QGeoclueMaster *m_master; + + OrgFreedesktopGeoclueInterface *m_provider; + OrgFreedesktopGeoclueSatelliteInterface *m_sat; + + QTimer m_requestTimer; + QList m_inView; + QList m_inUse; + Error m_error; + bool m_satellitesChangedConnected; + bool m_running; +}; + +QT_END_NAMESPACE + +#endif // QGEOSATELLITEINFOSOURCE_GEOCLUEMASTER_H diff --git a/src/plugins/position/gypsy/gypsy.pro b/src/plugins/position/gypsy/gypsy.pro new file mode 100644 index 0000000..c7b7432 --- /dev/null +++ b/src/plugins/position/gypsy/gypsy.pro @@ -0,0 +1,20 @@ +TARGET = qtposition_gypsy + +QT = core positioning-private + +HEADERS += \ + qgeosatelliteinfosource_gypsy_p.h \ + qgeopositioninfosourcefactory_gypsy.h + +SOURCES += \ + qgeosatelliteinfosource_gypsy.cpp \ + qgeopositioninfosourcefactory_gypsy.cpp + +QMAKE_USE_PRIVATE += gypsy + +OTHER_FILES += \ + plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryGypsy +load(qt_plugin) diff --git a/src/plugins/position/gypsy/plugin.json b/src/plugins/position/gypsy/plugin.json new file mode 100644 index 0000000..9cef03f --- /dev/null +++ b/src/plugins/position/gypsy/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["gypsy"], + "Provider": "gypsy", + "Position": false, + "Satellite": true, + "Monitor" : false, + "Priority": 1000, + "Testable": false +} diff --git a/src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.cpp b/src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.cpp new file mode 100644 index 0000000..ecb1b2e --- /dev/null +++ b/src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.cpp @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosourcefactory_gypsy.h" +#include "qgeosatelliteinfosource_gypsy_p.h" + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryGypsy::positionInfoSource(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryGypsy::satelliteInfoSource(QObject *parent) +{ + QGeoSatelliteInfoSourceGypsy *src = new QGeoSatelliteInfoSourceGypsy(parent); + if (src->init() < 0) { + delete src; + src = 0; + } + return src; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryGypsy::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} diff --git a/src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.h b/src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.h new file mode 100644 index 0000000..fa18822 --- /dev/null +++ b/src/plugins/position/gypsy/qgeopositioninfosourcefactory_gypsy.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_GYPSY_H +#define QGEOPOSITIONINFOSOURCEFACTORY_GYPSY_H + +#include +#include + +class QGeoPositionInfoSourceFactoryGypsy : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) + +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +#endif diff --git a/src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy.cpp b/src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy.cpp new file mode 100644 index 0000000..1694fd3 --- /dev/null +++ b/src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy.cpp @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeosatelliteinfosource_gypsy_p.h" + +#ifdef Q_LOCATION_GYPSY_DEBUG +#include +#endif +#include + +QT_BEGIN_NAMESPACE + +#define UPDATE_TIMEOUT_COLD_START 120000 + + +// Callback function for 'satellites-changed' -signal +static void satellites_changed (GypsySatellite *satellite, + GPtrArray *satellites, + gpointer userdata) +{ +#ifdef Q_LOCATION_GYPSY_DEBUG + qDebug() << "QGeoSatelliteInfoSourceGypsy Gypsy satellites-changed -signal received."; +#endif + ((QGeoSatelliteInfoSourceGypsy *)userdata)->satellitesChanged(satellite, satellites); +} + +SatelliteGypsyEngine::SatelliteGypsyEngine(QGeoSatelliteInfoSource *parent) : + m_owner(parent) +{ +} +SatelliteGypsyEngine::~SatelliteGypsyEngine() +{ +} + +// Glib symbols +gulong SatelliteGypsyEngine::eng_g_signal_connect(gpointer instance, + const gchar *detailed_signal, + GCallback c_handler, + gpointer data) +{ + return ::g_signal_connect(instance, detailed_signal, c_handler, data); +} +guint SatelliteGypsyEngine::eng_g_signal_handlers_disconnect_by_func (gpointer instance, + gpointer func, + gpointer data) +{ + return ::g_signal_handlers_disconnect_by_func(instance, func, data); +} + +void SatelliteGypsyEngine::eng_g_free(gpointer mem) +{ + return ::g_free(mem); +} +// Gypsy symbols +GypsyControl *SatelliteGypsyEngine::eng_gypsy_control_get_default (void) +{ + return ::gypsy_control_get_default(); +} +char *SatelliteGypsyEngine::eng_gypsy_control_create (GypsyControl *control, const char *device_name, GError **error) +{ + return ::gypsy_control_create(control, device_name, error); +} +GypsyDevice *SatelliteGypsyEngine::eng_gypsy_device_new (const char *object_path) +{ + return ::gypsy_device_new(object_path); +} +GypsySatellite *SatelliteGypsyEngine::eng_gypsy_satellite_new (const char *object_path) +{ + return ::gypsy_satellite_new (object_path); +} +gboolean SatelliteGypsyEngine::eng_gypsy_device_start (GypsyDevice *device, GError **error) +{ + return ::gypsy_device_start(device, error); +} +gboolean SatelliteGypsyEngine::eng_gypsy_device_stop (GypsyDevice *device, GError **error) +{ + // Unfortunately this cannot be done; calling this will stop the GPS device + // (basically makes gypsy-daemon unusable for anyone), regardless of applications + // using it (see bug http://bugs.meego.com/show_bug.cgi?id=11707). + Q_UNUSED(device); + Q_UNUSED(error); + return true; + //return ::gypsy_device_stop (device, error); +} +GypsyDeviceFixStatus SatelliteGypsyEngine::eng_gypsy_device_get_fix_status (GypsyDevice *device, GError **error) +{ + return ::gypsy_device_get_fix_status (device, error); +} +GPtrArray *SatelliteGypsyEngine::eng_gypsy_satellite_get_satellites (GypsySatellite *satellite, GError **error) +{ + return ::gypsy_satellite_get_satellites (satellite, error); +} +void SatelliteGypsyEngine::eng_gypsy_satellite_free_satellite_array (GPtrArray *satellites) +{ + return ::gypsy_satellite_free_satellite_array(satellites); +} +// GConf symbols (mockability due to X11 requirement) +GConfClient *SatelliteGypsyEngine::eng_gconf_client_get_default(void) +{ + return ::gconf_client_get_default(); +} +gchar *SatelliteGypsyEngine::eng_gconf_client_get_string(GConfClient *client, const gchar *key, GError** err) +{ + return ::gconf_client_get_string(client, key, err); +} + +QGeoSatelliteInfoSourceGypsy::QGeoSatelliteInfoSourceGypsy(QObject *parent) : QGeoSatelliteInfoSource(parent), + m_engine(0), m_satellite(0), m_device(0), m_requestTimer(this), m_updatesOngoing(false), m_requestOngoing(false) +{ + m_requestTimer.setSingleShot(true); + QObject::connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestUpdateTimeout())); +} + +void QGeoSatelliteInfoSourceGypsy::createEngine() +{ + delete m_engine; + m_engine = new SatelliteGypsyEngine(this); +} + +QGeoSatelliteInfoSourceGypsy::~QGeoSatelliteInfoSourceGypsy() +{ + GError *error = NULL; + if (m_device) { + m_engine->eng_gypsy_device_stop (m_device, &error); + g_object_unref(m_device); + } + if (m_satellite) + g_object_unref(m_satellite); + if (error) + g_error_free(error); + delete m_engine; +} + +void QGeoSatelliteInfoSourceGypsy::satellitesChanged(GypsySatellite *satellite, + GPtrArray *satellites) +{ + if (!satellite || !satellites) + return; + // We have satellite data and assume it is valid. + // If a single updateRequest was active, send signals right away. + // If a periodic timer was running (meaning that the client wishes + // to have updates at defined intervals), store the data for later sending. + QList lastSatellitesInView; + QList lastSatellitesInUse; + + unsigned int i; + for (i = 0; i < satellites->len; i++) { + GypsySatelliteDetails *details = (GypsySatelliteDetails *)satellites->pdata[i]; + QGeoSatelliteInfo info; + info.setAttribute(QGeoSatelliteInfo::Elevation, details->elevation); + info.setAttribute(QGeoSatelliteInfo::Azimuth, details->azimuth); + info.setSignalStrength(details->snr); + if (details->in_use) + lastSatellitesInUse.append(info); + lastSatellitesInView.append(info); + } + bool sendUpdates(false); + // If a single updateRequest() has been issued: + if (m_requestOngoing) { + sendUpdates = true; + m_requestTimer.stop(); + m_requestOngoing = false; + // If there is no regular updates ongoing, disconnect now. + if (!m_updatesOngoing) { + m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this); + } + } + // If regular updates are to be delivered as they come: + if (m_updatesOngoing) + sendUpdates = true; + + if (sendUpdates) { + emit satellitesInUseUpdated(lastSatellitesInUse); + emit satellitesInViewUpdated(lastSatellitesInView); + } +} + +int QGeoSatelliteInfoSourceGypsy::init() +{ + GError *error = NULL; + char *path; + GConfClient *client; + gchar *device_name; + + g_type_init (); + createEngine(); + + client = m_engine->eng_gconf_client_get_default(); + if (!client) { + qWarning ("QGeoSatelliteInfoSourceGypsy client creation failed."); + return -1; + } + device_name = m_engine->eng_gconf_client_get_string(client, "/apps/geoclue/master/org.freedesktop.Geoclue.GPSDevice", NULL); + g_object_unref(client); + QString deviceName(QString::fromLatin1(device_name)); + if (deviceName.isEmpty() || + (deviceName.trimmed().at(0) == '/' && !QFile::exists(deviceName.trimmed()))) { + qWarning ("QGeoSatelliteInfoSourceGypsy Empty/nonexistent GPS device name detected."); + qWarning ("Use gconftool-2 to set it, e.g. on terminal: "); + qWarning ("gconftool-2 -t string -s /apps/geoclue/master/org.freedesktop.Geoclue.GPSDevice /dev/ttyUSB0"); + m_engine->eng_g_free(device_name); + return -1; + } + GypsyControl *control = NULL; + control = m_engine->eng_gypsy_control_get_default(); + if (!control) { + qWarning("QGeoSatelliteInfoSourceGypsy unable to create Gypsy control."); + m_engine->eng_g_free(device_name); + return -1; + } + // (path is the DBus path) + path = m_engine->eng_gypsy_control_create (control, device_name, &error); + m_engine->eng_g_free(device_name); + g_object_unref(control); + if (!path) { + qWarning ("QGeoSatelliteInfoSourceGypsy error creating client."); + if (error) { + qWarning ("error message: %s", error->message); + g_error_free (error); + } + return -1; + } + m_device = m_engine->eng_gypsy_device_new (path); + m_satellite = m_engine->eng_gypsy_satellite_new (path); + m_engine->eng_g_free(path); + if (!m_device || !m_satellite) { + qWarning ("QGeoSatelliteInfoSourceGypsy error creating satellite device."); + qWarning ("Is GPS device set correctly? If not, use gconftool-2 to set it, e.g.: "); + qWarning ("gconftool-2 -t string -s /apps/geoclue/master/org.freedesktop.Geoclue.GPSDevice /dev/ttyUSB0"); + if (m_device) + g_object_unref(m_device); + if (m_satellite) + g_object_unref(m_satellite); + return -1; + } + m_engine->eng_gypsy_device_start (m_device, &error); + if (error) { + qWarning ("QGeoSatelliteInfoSourceGypsy error starting device: %s ", + error->message); + g_error_free(error); + g_object_unref(m_device); + g_object_unref(m_satellite); + return -1; + } + return 0; +} + +int QGeoSatelliteInfoSourceGypsy::minimumUpdateInterval() const +{ + return 1; +} + +QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSourceGypsy::error() const +{ + return NoError; +} + +void QGeoSatelliteInfoSourceGypsy::startUpdates() +{ + if (m_updatesOngoing) + return; + // If there is a request timer ongoing, we've connected to the signal already + if (!m_requestTimer.isActive()) { + m_engine->eng_g_signal_connect (m_satellite, "satellites-changed", + G_CALLBACK (satellites_changed), this); + } + m_updatesOngoing = true; +} + +void QGeoSatelliteInfoSourceGypsy::stopUpdates() +{ + if (!m_updatesOngoing) + return; + m_updatesOngoing = false; + // Disconnect only if there is no single update request ongoing. Once single update request + // is completed and it notices that there is no active update ongoing, it will disconnect + // the signal. + if (!m_requestTimer.isActive()) + m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this); +} + +void QGeoSatelliteInfoSourceGypsy::requestUpdate(int timeout) +{ + if (m_requestOngoing) + return; + if (timeout < 0) { + emit requestTimeout(); + return; + } + m_requestOngoing = true; + GError *error = 0; + // If GPS has a fix a already, request current data. + GypsyDeviceFixStatus fixStatus = m_engine->eng_gypsy_device_get_fix_status(m_device, &error); + if (!error && (fixStatus != GYPSY_DEVICE_FIX_STATUS_INVALID && + fixStatus != GYPSY_DEVICE_FIX_STATUS_NONE)) { +#ifdef Q_LOCATION_GYPSY_DEBUG + qDebug() << "QGeoSatelliteInfoSourceGypsy fix available, requesting current satellite data"; +#endif + GPtrArray *satelliteData = m_engine->eng_gypsy_satellite_get_satellites(m_satellite, &error); + if (!error) { + // The fix was available and we have satellite data to deliver right away. + satellitesChanged(m_satellite, satelliteData); + m_engine->eng_gypsy_satellite_free_satellite_array(satelliteData); + return; + } + } + // No fix is available. If updates are not ongoing already, start them. + m_requestTimer.setInterval(timeout == 0? UPDATE_TIMEOUT_COLD_START: timeout); + if (!m_updatesOngoing) { + m_engine->eng_g_signal_connect (m_satellite, "satellites-changed", + G_CALLBACK (satellites_changed), this); + } + m_requestTimer.start(); + if (error) { +#ifdef Q_LOCATION_GYPSY_DEBUG + qDebug() << "QGeoSatelliteInfoSourceGypsy error asking fix status or satellite data: " << error->message; +#endif + g_error_free(error); + } +} + +void QGeoSatelliteInfoSourceGypsy::requestUpdateTimeout() +{ +#ifdef Q_LOCATION_GYPSY_DEBUG + qDebug("QGeoSatelliteInfoSourceGypsy request update timeout occurred."); +#endif + // If we end up here, there has not been valid satellite update. + // Emit timeout and disconnect from signal if regular updates are not + // ongoing (as we were listening just for one single requestUpdate). + if (!m_updatesOngoing) { + m_engine->eng_g_signal_handlers_disconnect_by_func(G_OBJECT(m_satellite), (void *)satellites_changed, this); + } + m_requestOngoing = false; + emit requestTimeout(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy_p.h b/src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy_p.h new file mode 100644 index 0000000..ea6b6bc --- /dev/null +++ b/src/plugins/position/gypsy/qgeosatelliteinfosource_gypsy_p.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSATELLITEINFOSOURCE_GYPSY_H +#define QGEOSATELLITEINFOSOURCE_GYPSY_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeosatelliteinfosource.h" +#include "qgeosatelliteinfo.h" +#include +#include +#include +#include +#include + +// #define Q_LOCATION_GYPSY_DEBUG + +QT_BEGIN_NAMESPACE + +// An engine that encapsulates all symbols we want +// to be able to mock (for unit/autotest purposes). +class SatelliteGypsyEngine +{ +public: + SatelliteGypsyEngine(QGeoSatelliteInfoSource *parent = 0); + virtual ~SatelliteGypsyEngine(); + // Glib symbols + virtual gulong eng_g_signal_connect(gpointer instance, + const gchar *detailed_signal, + GCallback c_handler, + gpointer data); + virtual guint eng_g_signal_handlers_disconnect_by_func(gpointer instance, + gpointer func, + gpointer data); + virtual void eng_g_free(gpointer mem); + // Gypsy symbols + virtual GypsyControl *eng_gypsy_control_get_default (void); + virtual char *eng_gypsy_control_create (GypsyControl *control, const char *device_name, GError **error); + virtual GypsyDevice *eng_gypsy_device_new (const char *object_path); + virtual GypsySatellite *eng_gypsy_satellite_new (const char *object_path); + virtual gboolean eng_gypsy_device_start (GypsyDevice *device, GError **error); + virtual gboolean eng_gypsy_device_stop (GypsyDevice *device, GError **error); + virtual GypsyDeviceFixStatus eng_gypsy_device_get_fix_status (GypsyDevice *device, GError **error); + virtual GPtrArray *eng_gypsy_satellite_get_satellites (GypsySatellite *satellite, GError **error); + virtual void eng_gypsy_satellite_free_satellite_array (GPtrArray *satellites); + // GConf symbols (mockability due to X11 requirement) + virtual GConfClient *eng_gconf_client_get_default(void); + virtual gchar *eng_gconf_client_get_string(GConfClient *client, const gchar *key, GError** err); +protected: + QGeoSatelliteInfoSource *m_owner; +}; + +class QGeoSatelliteInfoSourceGypsy : public QGeoSatelliteInfoSource + { + Q_OBJECT + +public: + explicit QGeoSatelliteInfoSourceGypsy(QObject *parent = 0); + ~QGeoSatelliteInfoSourceGypsy(); + int init(); + + int minimumUpdateInterval() const; + Error error() const; + +public slots: + virtual void startUpdates(); + void stopUpdates(); + void requestUpdate(int timeout = 5000); + void satellitesChanged(GypsySatellite *satellite, GPtrArray *satellites); + +signals: + void satellitesInViewUpdated(const QList &satellites); + void satellitesInUseUpdated(const QList &satellites); + +private slots: + void requestUpdateTimeout(); + +protected: + // Creates an engine which encapsulates all used symbols + // that we want to be also able to mock. + virtual void createEngine(); + SatelliteGypsyEngine *m_engine; + +private: + Q_DISABLE_COPY(QGeoSatelliteInfoSourceGypsy) + GypsySatellite *m_satellite; + GypsyDevice *m_device; + QTimer m_requestTimer; + bool m_updatesOngoing; + bool m_requestOngoing; + }; + +QT_END_NAMESPACE + +#endif // QGEOSATELLITEINFOSOURCE_GYPSY_H diff --git a/src/plugins/position/position.pro b/src/plugins/position/position.pro new file mode 100644 index 0000000..b9832ff --- /dev/null +++ b/src/plugins/position/position.pro @@ -0,0 +1,14 @@ +TEMPLATE = subdirs + +QT_FOR_CONFIG += positioning-private + +qtHaveModule(dbus):SUBDIRS += geoclue +qtConfig(gypsy):SUBDIRS += gypsy +qtConfig(winrt_geolocation):SUBDIRS += winrt +qtHaveModule(simulator):SUBDIRS += simulator +osx|ios|tvos:SUBDIRS += corelocation +android:SUBDIRS += android +qtHaveModule(serialport):SUBDIRS += serialnmea + +SUBDIRS += \ + positionpoll diff --git a/src/plugins/position/positionpoll/plugin.json b/src/plugins/position/positionpoll/plugin.json new file mode 100644 index 0000000..df1f47d --- /dev/null +++ b/src/plugins/position/positionpoll/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["positionpoll"], + "Provider": "positionpoll", + "Position": false, + "Satellite": false, + "Monitor": true, + "Priority": 1000, + "Testable": true +} diff --git a/src/plugins/position/positionpoll/positionpoll.pro b/src/plugins/position/positionpoll/positionpoll.pro new file mode 100644 index 0000000..be60bf4 --- /dev/null +++ b/src/plugins/position/positionpoll/positionpoll.pro @@ -0,0 +1,18 @@ +TARGET = qtposition_positionpoll + +QT = core positioning + +SOURCES += \ + qgeoareamonitor_polling.cpp \ + positionpollfactory.cpp + +HEADERS += \ + qgeoareamonitor_polling.h \ + positionpollfactory.h + +OTHER_FILES += \ + plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryPoll +load(qt_plugin) diff --git a/src/plugins/position/positionpoll/positionpollfactory.cpp b/src/plugins/position/positionpoll/positionpollfactory.cpp new file mode 100644 index 0000000..20e2774 --- /dev/null +++ b/src/plugins/position/positionpoll/positionpollfactory.cpp @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "positionpollfactory.h" +#include "qgeoareamonitor_polling.h" + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryPoll::positionInfoSource(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryPoll::satelliteInfoSource(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryPoll::areaMonitor(QObject *parent) +{ + QGeoAreaMonitorPolling *ret = new QGeoAreaMonitorPolling(parent); + if (ret && ret->isValid()) + return ret; + delete ret; + return 0; +} diff --git a/src/plugins/position/positionpoll/positionpollfactory.h b/src/plugins/position/positionpoll/positionpollfactory.h new file mode 100644 index 0000000..6841fd0 --- /dev/null +++ b/src/plugins/position/positionpoll/positionpollfactory.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef POSITIONPOLLFACTORY_H +#define POSITIONPOLLFACTORY_H + +#include +#include + +class QGeoPositionInfoSourceFactoryPoll : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +#endif // POSITIONPOLLFACTORY_H diff --git a/src/plugins/position/positionpoll/qgeoareamonitor_polling.cpp b/src/plugins/position/positionpoll/qgeoareamonitor_polling.cpp new file mode 100644 index 0000000..e39a621 --- /dev/null +++ b/src/plugins/position/positionpoll/qgeoareamonitor_polling.cpp @@ -0,0 +1,497 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoareamonitor_polling.h" +#include +#include +#include + +#include +#include +#include +#include + +#define UPDATE_INTERVAL_5S 5000 + +typedef QHash MonitorTable; + + +static QMetaMethod areaEnteredSignal() +{ + static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::areaEntered); + return signal; +} + +static QMetaMethod areaExitedSignal() +{ + static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::areaExited); + return signal; +} + +static QMetaMethod monitorExpiredSignal() +{ + static QMetaMethod signal = QMetaMethod::fromSignal(&QGeoAreaMonitorPolling::monitorExpired); + return signal; +} + +class QGeoAreaMonitorPollingPrivate : public QObject +{ + Q_OBJECT +public: + QGeoAreaMonitorPollingPrivate() : source(0), mutex(QMutex::Recursive) + { + nextExpiryTimer = new QTimer(this); + nextExpiryTimer->setSingleShot(true); + connect(nextExpiryTimer, SIGNAL(timeout()), + this, SLOT(timeout())); + } + + void startMonitoring(const QGeoAreaMonitorInfo &monitor) + { + QMutexLocker locker(&mutex); + + activeMonitorAreas.insert(monitor.identifier(), monitor); + singleShotTrigger.remove(monitor.identifier()); + + checkStartStop(); + setupNextExpiryTimeout(); + } + + void requestUpdate(const QGeoAreaMonitorInfo &monitor, int signalId) + { + QMutexLocker locker(&mutex); + + activeMonitorAreas.insert(monitor.identifier(), monitor); + singleShotTrigger.insert(monitor.identifier(), signalId); + + checkStartStop(); + setupNextExpiryTimeout(); + } + + QGeoAreaMonitorInfo stopMonitoring(const QGeoAreaMonitorInfo &monitor) + { + QMutexLocker locker(&mutex); + + QGeoAreaMonitorInfo mon = activeMonitorAreas.take(monitor.identifier()); + + checkStartStop(); + setupNextExpiryTimeout(); + + return mon; + } + + void registerClient(QGeoAreaMonitorPolling *client) + { + QMutexLocker locker(&mutex); + + connect(this, SIGNAL(timeout(QGeoAreaMonitorInfo)), + client, SLOT(timeout(QGeoAreaMonitorInfo))); + + connect(this, SIGNAL(positionError(QGeoPositionInfoSource::Error)), + client, SLOT(positionError(QGeoPositionInfoSource::Error))); + + connect(this, SIGNAL(areaEventDetected(QGeoAreaMonitorInfo,QGeoPositionInfo,bool)), + client, SLOT(processAreaEvent(QGeoAreaMonitorInfo,QGeoPositionInfo,bool))); + + registeredClients.append(client); + } + + void deregisterClient(QGeoAreaMonitorPolling *client) + { + QMutexLocker locker(&mutex); + + registeredClients.removeAll(client); + if (registeredClients.isEmpty()) + checkStartStop(); + } + + void setPositionSource(QGeoPositionInfoSource *newSource) + { + QMutexLocker locker(&mutex); + + if (newSource == source) + return; + + if (source) + delete source; + + source = newSource; + + if (source) { + source->setParent(this); + source->moveToThread(this->thread()); + if (source->updateInterval() == 0) + source->setUpdateInterval(UPDATE_INTERVAL_5S); + disconnect(source, 0, 0, 0); //disconnect all + connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdated(QGeoPositionInfo))); + connect(source, SIGNAL(error(QGeoPositionInfoSource::Error)), + this, SIGNAL(positionError(QGeoPositionInfoSource::Error))); + checkStartStop(); + } + } + + QGeoPositionInfoSource* positionSource() const + { + QMutexLocker locker(&mutex); + return source; + } + + MonitorTable activeMonitors() const + { + QMutexLocker locker(&mutex); + + return activeMonitorAreas; + } + + void checkStartStop() + { + QMutexLocker locker(&mutex); + + bool signalsConnected = false; + foreach (const QGeoAreaMonitorPolling *client, registeredClients) { + if (client->signalsAreConnected) { + signalsConnected = true; + break; + } + } + + if (signalsConnected && !activeMonitorAreas.isEmpty()) { + if (source) + source->startUpdates(); + else + //translated to InsufficientPositionInfo + emit positionError(QGeoPositionInfoSource::ClosedError); + } else { + if (source) + source->stopUpdates(); + } + } + +private: + void setupNextExpiryTimeout() + { + nextExpiryTimer->stop(); + activeExpiry.first = QDateTime(); + activeExpiry.second = QString(); + + foreach (const QGeoAreaMonitorInfo &info, activeMonitors()) { + if (info.expiration().isValid()) { + if (!activeExpiry.first.isValid()) { + activeExpiry.first = info.expiration(); + activeExpiry.second = info.identifier(); + continue; + } + if (info.expiration() < activeExpiry.first) { + activeExpiry.first = info.expiration(); + activeExpiry.second = info.identifier(); + } + } + } + + if (activeExpiry.first.isValid()) + nextExpiryTimer->start(QDateTime::currentDateTime().msecsTo(activeExpiry.first)); + } + + + //returns true if areaEntered should be emitted + bool processInsideArea(const QString &monitorIdent) + { + if (!insideArea.contains(monitorIdent)) { + if (singleShotTrigger.value(monitorIdent, -1) == areaEnteredSignal().methodIndex()) { + //this is the finishing singleshot event + singleShotTrigger.remove(monitorIdent); + activeMonitorAreas.remove(monitorIdent); + setupNextExpiryTimeout(); + } else { + insideArea.insert(monitorIdent); + } + return true; + } + + return false; + } + + //returns true if areaExited should be emitted + bool processOutsideArea(const QString &monitorIdent) + { + if (insideArea.contains(monitorIdent)) { + if (singleShotTrigger.value(monitorIdent, -1) == areaExitedSignal().methodIndex()) { + //this is the finishing singleShot event + singleShotTrigger.remove(monitorIdent); + activeMonitorAreas.remove(monitorIdent); + setupNextExpiryTimeout(); + } else { + insideArea.remove(monitorIdent); + } + return true; + } + return false; + } + + + +Q_SIGNALS: + void timeout(const QGeoAreaMonitorInfo &info); + void positionError(const QGeoPositionInfoSource::Error error); + void areaEventDetected(const QGeoAreaMonitorInfo &minfo, + const QGeoPositionInfo &pinfo, bool isEnteredEvent); +private Q_SLOTS: + void timeout() + { + /* + * Don't block timer firing even if monitorExpiredSignal is not connected. + * This allows us to continue to remove the existing monitors as they expire. + **/ + const QGeoAreaMonitorInfo info = activeMonitorAreas.take(activeExpiry.second); + setupNextExpiryTimeout(); + emit timeout(info); + + } + + void positionUpdated(const QGeoPositionInfo &info) + { + foreach (const QGeoAreaMonitorInfo &monInfo, activeMonitors()) { + const QString identifier = monInfo.identifier(); + if (monInfo.area().contains(info.coordinate())) { + if (processInsideArea(identifier)) + emit areaEventDetected(monInfo, info, true); + } else { + if (processOutsideArea(identifier)) + emit areaEventDetected(monInfo, info, false); + } + } + } + +private: + QPair activeExpiry; + QHash singleShotTrigger; + QTimer* nextExpiryTimer; + QSet insideArea; + + MonitorTable activeMonitorAreas; + + QGeoPositionInfoSource* source; + QList registeredClients; + mutable QMutex mutex; +}; + +Q_GLOBAL_STATIC(QGeoAreaMonitorPollingPrivate, pollingPrivate) + + +QGeoAreaMonitorPolling::QGeoAreaMonitorPolling(QObject *parent) + : QGeoAreaMonitorSource(parent), signalsAreConnected(false) +{ + d = pollingPrivate(); + lastError = QGeoAreaMonitorSource::NoError; + d->registerClient(this); + //hookup to default source if existing + if (!positionInfoSource()) + setPositionInfoSource(QGeoPositionInfoSource::createDefaultSource(this)); +} + +QGeoAreaMonitorPolling::~QGeoAreaMonitorPolling() +{ + d->deregisterClient(this); +} + +QGeoPositionInfoSource* QGeoAreaMonitorPolling::positionInfoSource() const +{ + return d->positionSource(); +} + +void QGeoAreaMonitorPolling::setPositionInfoSource(QGeoPositionInfoSource *source) +{ + d->setPositionSource(source); +} + +QGeoAreaMonitorSource::Error QGeoAreaMonitorPolling::error() const +{ + return lastError; +} + +bool QGeoAreaMonitorPolling::startMonitoring(const QGeoAreaMonitorInfo &monitor) +{ + if (!monitor.isValid()) + return false; + + //reject an expiry in the past + if (monitor.expiration().isValid() && + (monitor.expiration() < QDateTime::currentDateTime())) + return false; + + //don't accept persistent monitor since we don't support it + if (monitor.isPersistent()) + return false; + + //update or insert + d->startMonitoring(monitor); + + return true; +} + +int QGeoAreaMonitorPolling::idForSignal(const char *signal) +{ + const QByteArray sig = QMetaObject::normalizedSignature(signal + 1); + const QMetaObject * const mo = metaObject(); + + return mo->indexOfSignal(sig.constData()); +} + +bool QGeoAreaMonitorPolling::requestUpdate(const QGeoAreaMonitorInfo &monitor, const char *signal) +{ + if (!monitor.isValid()) + return false; + //reject an expiry in the past + if (monitor.expiration().isValid() && + (monitor.expiration() < QDateTime::currentDateTime())) + return false; + + //don't accept persistent monitor since we don't support it + if (monitor.isPersistent()) + return false; + + if (!signal) + return false; + + const int signalId = idForSignal(signal); + if (signalId < 0) + return false; + + //only accept area entered or exit signal + if (signalId != areaEnteredSignal().methodIndex() && + signalId != areaExitedSignal().methodIndex()) + { + return false; + } + + d->requestUpdate(monitor, signalId); + + return true; +} + +bool QGeoAreaMonitorPolling::stopMonitoring(const QGeoAreaMonitorInfo &monitor) +{ + QGeoAreaMonitorInfo info = d->stopMonitoring(monitor); + + return info.isValid(); +} + +QList QGeoAreaMonitorPolling::activeMonitors() const +{ + return d->activeMonitors().values(); +} + +QList QGeoAreaMonitorPolling::activeMonitors(const QGeoShape ®ion) const +{ + QList results; + if (region.isEmpty()) + return results; + + const MonitorTable list = d->activeMonitors(); + foreach (const QGeoAreaMonitorInfo &monitor, list) { + if (region.contains(monitor.area().center())) + results.append(monitor); + } + + return results; +} + +QGeoAreaMonitorSource::AreaMonitorFeatures QGeoAreaMonitorPolling::supportedAreaMonitorFeatures() const +{ + return 0; +} + +void QGeoAreaMonitorPolling::connectNotify(const QMetaMethod &/*signal*/) +{ + if (!signalsAreConnected && + (isSignalConnected(areaEnteredSignal()) || + isSignalConnected(areaExitedSignal())) ) + { + signalsAreConnected = true; + d->checkStartStop(); + } +} + +void QGeoAreaMonitorPolling::disconnectNotify(const QMetaMethod &/*signal*/) +{ + if (!isSignalConnected(areaEnteredSignal()) && + !isSignalConnected(areaExitedSignal())) + { + signalsAreConnected = false; + d->checkStartStop(); + } +} + +void QGeoAreaMonitorPolling::positionError(const QGeoPositionInfoSource::Error error) +{ + switch (error) { + case QGeoPositionInfoSource::AccessError: + lastError = QGeoAreaMonitorSource::AccessError; + break; + case QGeoPositionInfoSource::UnknownSourceError: + lastError = QGeoAreaMonitorSource::UnknownSourceError; + break; + case QGeoPositionInfoSource::ClosedError: + lastError = QGeoAreaMonitorSource::InsufficientPositionInfo; + break; + case QGeoPositionInfoSource::NoError: + return; + } + + emit QGeoAreaMonitorSource::error(lastError); +} + +void QGeoAreaMonitorPolling::timeout(const QGeoAreaMonitorInfo& monitor) +{ + if (isSignalConnected(monitorExpiredSignal())) + emit monitorExpired(monitor); +} + +void QGeoAreaMonitorPolling::processAreaEvent(const QGeoAreaMonitorInfo &minfo, + const QGeoPositionInfo &pinfo, bool isEnteredEvent) +{ + if (isEnteredEvent) + emit areaEntered(minfo, pinfo); + else + emit areaExited(minfo, pinfo); +} + +#include "qgeoareamonitor_polling.moc" +#include "moc_qgeoareamonitor_polling.cpp" diff --git a/src/plugins/position/positionpoll/qgeoareamonitor_polling.h b/src/plugins/position/positionpoll/qgeoareamonitor_polling.h new file mode 100644 index 0000000..1116186 --- /dev/null +++ b/src/plugins/position/positionpoll/qgeoareamonitor_polling.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOAREAMONITORPOLLING_H +#define QGEOAREAMONITORPOLLING_H + +#include +#include + + +/** + * QGeoAreaMonitorPolling + * + */ + +class QGeoAreaMonitorPollingPrivate; +class QGeoAreaMonitorPolling : public QGeoAreaMonitorSource +{ + Q_OBJECT +public : + explicit QGeoAreaMonitorPolling(QObject *parent = 0); + ~QGeoAreaMonitorPolling(); + + void setPositionInfoSource(QGeoPositionInfoSource *source) override; + QGeoPositionInfoSource* positionInfoSource() const override; + + Error error() const override; + + bool startMonitoring(const QGeoAreaMonitorInfo &monitor) override; + bool requestUpdate(const QGeoAreaMonitorInfo &monitor, + const char *signal) override; + bool stopMonitoring(const QGeoAreaMonitorInfo &monitor) override; + + QList activeMonitors() const override; + QList activeMonitors(const QGeoShape ®ion) const override; + + QGeoAreaMonitorSource::AreaMonitorFeatures supportedAreaMonitorFeatures() const override; + + inline bool isValid() { return positionInfoSource(); } + + bool signalsAreConnected; + +private Q_SLOTS: + void positionError(QGeoPositionInfoSource::Error error); + void timeout(const QGeoAreaMonitorInfo &monitor); + void processAreaEvent(const QGeoAreaMonitorInfo &minfo, const QGeoPositionInfo &pinfo, bool isEnteredEvent); + +private: + QGeoAreaMonitorPollingPrivate* d; + QGeoAreaMonitorSource::Error lastError; + + void connectNotify(const QMetaMethod &signal) override; + void disconnectNotify(const QMetaMethod &signal) override; + + int idForSignal(const char *signal); +}; + +#endif // QGEOAREAMONITORPOLLING_H diff --git a/src/plugins/position/serialnmea/plugin.json b/src/plugins/position/serialnmea/plugin.json new file mode 100644 index 0000000..826836c --- /dev/null +++ b/src/plugins/position/serialnmea/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["serialnmea"], + "Provider": "serialnmea", + "Position": true, + "Satellite": false, + "Monitor" : false, + "Priority": 1000, + "Testable": false +} diff --git a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp b/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp new file mode 100644 index 0000000..58092ea --- /dev/null +++ b/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosourcefactory_serialnmea.h" +#include +#include +#include +#include +#include + +Q_LOGGING_CATEGORY(lcSerial, "qt.positioning.serialnmea") + +class NmeaSource : public QNmeaPositionInfoSource +{ +public: + NmeaSource(QObject *parent); + bool isValid() const { return !m_port.isNull(); } + +private: + QScopedPointer m_port; +}; + +NmeaSource::NmeaSource(QObject *parent) + : QNmeaPositionInfoSource(RealTimeMode, parent), + m_port(new QSerialPort) +{ + QByteArray requestedPort = qgetenv("QT_NMEA_SERIAL_PORT"); + if (requestedPort.isEmpty()) { + const QList ports = QSerialPortInfo::availablePorts(); + qCDebug(lcSerial) << "Found" << ports.count() << "serial ports"; + if (ports.isEmpty()) { + qWarning("serialnmea: No serial ports found"); + m_port.reset(); + return; + } + + // Try to find a well-known device. + QSet supportedDevices; + supportedDevices << 0x67b; // GlobalSat (BU-353S4 and probably others) + supportedDevices << 0xe8d; // Qstarz MTK II + QString portName; + foreach (const QSerialPortInfo& port, ports) { + if (port.hasVendorIdentifier() && supportedDevices.contains(port.vendorIdentifier())) { + portName = port.portName(); + break; + } + } + + if (portName.isEmpty()) { + qWarning("serialnmea: No known GPS device found. Specify the COM port via QT_NMEA_SERIAL_PORT."); + m_port.reset(); + return; + } + + m_port->setPortName(portName); + } else { + m_port->setPortName(QString::fromUtf8(requestedPort)); + } + + m_port->setBaudRate(4800); + + qCDebug(lcSerial) << "Opening serial port" << m_port->portName(); + + if (!m_port->open(QIODevice::ReadOnly)) { + qWarning("serialnmea: Failed to open %s", qPrintable(m_port->portName())); + m_port.reset(); + return; + } + + setDevice(m_port.data()); + + qCDebug(lcSerial) << "Opened successfully"; +} + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactorySerialNmea::positionInfoSource(QObject *parent) +{ + QScopedPointer src(new NmeaSource(parent)); + return src->isValid() ? src.take() : nullptr; +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactorySerialNmea::satelliteInfoSource(QObject *parent) +{ + Q_UNUSED(parent); + return nullptr; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactorySerialNmea::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent); + return nullptr; +} diff --git a/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.h b/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.h new file mode 100644 index 0000000..e372d56 --- /dev/null +++ b/src/plugins/position/serialnmea/qgeopositioninfosourcefactory_serialnmea.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_SERIALNMEA_H +#define QGEOPOSITIONINFOSOURCEFACTORY_SERIALNMEA_H + +#include +#include + +class QGeoPositionInfoSourceFactorySerialNmea : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) + +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +#endif diff --git a/src/plugins/position/serialnmea/serialnmea.pro b/src/plugins/position/serialnmea/serialnmea.pro new file mode 100644 index 0000000..bdeb3f1 --- /dev/null +++ b/src/plugins/position/serialnmea/serialnmea.pro @@ -0,0 +1,16 @@ +TARGET = qtposition_serialnmea + +QT = core positioning serialport + +HEADERS += \ + qgeopositioninfosourcefactory_serialnmea.h + +SOURCES += \ + qgeopositioninfosourcefactory_serialnmea.cpp + +OTHER_FILES += \ + plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactorySerialNmea +load(qt_plugin) diff --git a/src/plugins/position/simulator/plugin.json b/src/plugins/position/simulator/plugin.json new file mode 100644 index 0000000..0935f4d --- /dev/null +++ b/src/plugins/position/simulator/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["simulator"], + "Provider": "simulator", + "Position": true, + "Satellite": true, + "Monitor" : false, + "Priority": 1000, + "Testable": true +} diff --git a/src/plugins/position/simulator/qgeopositioninfosource_simulator.cpp b/src/plugins/position/simulator/qgeopositioninfosource_simulator.cpp new file mode 100644 index 0000000..39651b8 --- /dev/null +++ b/src/plugins/position/simulator/qgeopositioninfosource_simulator.cpp @@ -0,0 +1,170 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosource_simulator_p.h" +#include "qlocationdata_simulator_p.h" +#include "qlocationconnection_simulator_p.h" + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +namespace Simulator +{ + QGeoPositionInfo toPositionInfo(const QGeoPositionInfoData &data) + { + QDateTime timestamp; + if (data.dateTime.isValid()) + timestamp = data.dateTime; + else + timestamp = QDateTime::currentDateTime(); + QGeoCoordinate coord(data.latitude, data.longitude, data.altitude); + QGeoPositionInfo info(coord, timestamp); + info.setAttribute(QGeoPositionInfo::Direction, data.direction); + info.setAttribute(QGeoPositionInfo::GroundSpeed, data.groundSpeed); + info.setAttribute(QGeoPositionInfo::VerticalSpeed, data.verticalSpeed); + info.setAttribute(QGeoPositionInfo::MagneticVariation, data.magneticVariation); + info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, data.horizontalAccuracy); + info.setAttribute(QGeoPositionInfo::VerticalAccuracy, data.verticalAccuracy); + return info; + } +} //namespace + +// Location API + +QGeoPositionInfoSourceSimulator::QGeoPositionInfoSourceSimulator(QObject *parent) + : QGeoPositionInfoSource(parent) + , timer(new QTimer(this)) + , requestTimer(new QTimer(this)) + , m_positionError(QGeoPositionInfoSource::NoError) +{ + Simulator::LocationConnection::ensureSimulatorConnection(); + + connect(timer, SIGNAL(timeout()), this, SLOT(updatePosition())); + requestTimer->setSingleShot(true); + connect(requestTimer, SIGNAL(timeout()), this, SLOT(updatePosition())); +} + +QGeoPositionInfoSourceSimulator::~QGeoPositionInfoSourceSimulator() +{ +} + +QGeoPositionInfo QGeoPositionInfoSourceSimulator::lastKnownPosition(bool /*fromSatellitePositioningMethodsOnly*/) const +{ + return lastPosition; +} + +QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceSimulator::supportedPositioningMethods() const +{ + // Is GPS now Satelite or not? Guessing so... + return QGeoPositionInfoSource::SatellitePositioningMethods; +} + +void QGeoPositionInfoSourceSimulator::setUpdateInterval(int msec) +{ + // If msec is 0 we send updates as data becomes available, otherwise we force msec to be equal + // to or larger than the minimum update interval. + if (msec != 0 && msec < minimumUpdateInterval()) + msec = minimumUpdateInterval(); + + QGeoPositionInfoSource::setUpdateInterval(msec); + if (timer->isActive()) { + timer->setInterval(msec); + timer->start(); + } +} + +int QGeoPositionInfoSourceSimulator::minimumUpdateInterval() const +{ + return qtPositionInfo()->minimumInterval; +} + +void QGeoPositionInfoSourceSimulator::startUpdates() +{ + int interval = updateInterval(); + if (interval < minimumUpdateInterval()) + interval = minimumUpdateInterval(); + timer->setInterval(interval); + timer->start(); +} + +void QGeoPositionInfoSourceSimulator::stopUpdates() +{ + timer->stop(); +} + +void QGeoPositionInfoSourceSimulator::requestUpdate(int timeout) +{ + if (!requestTimer->isActive()) { + // Get a single update within timeframe + if (timeout < minimumUpdateInterval() && timeout != 0) + emit updateTimeout(); + else { + requestTimer->start(timeout * qreal(0.75)); + } + } +} + +void QGeoPositionInfoSourceSimulator::updatePosition() +{ + if (qtPositionInfo()->enabled) { + lastPosition = Simulator::toPositionInfo(*qtPositionInfo()); + emit positionUpdated(lastPosition); + } else { + emit updateTimeout(); + } +} + +QGeoPositionInfoSource::Error QGeoPositionInfoSourceSimulator::error() const +{ + return m_positionError; +} + + +void QGeoPositionInfoSourceSimulator::setError(QGeoPositionInfoSource::Error positionError) +{ + m_positionError = positionError; + emit QGeoPositionInfoSource::error(positionError); +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/simulator/qgeopositioninfosource_simulator_p.h b/src/plugins/position/simulator/qgeopositioninfosource_simulator_p.h new file mode 100644 index 0000000..6b1acfc --- /dev/null +++ b/src/plugins/position/simulator/qgeopositioninfosource_simulator_p.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCESIMULATOR_H +#define QGEOPOSITIONINFOSOURCESIMULATOR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeopositioninfosource.h" +#include "qgeopositioninfo.h" +#include "qlocationdata_simulator_p.h" + +QT_BEGIN_NAMESPACE + +class QTimer; + +class QGeoPositionInfoSourceSimulator : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + QGeoPositionInfoSourceSimulator(QObject *parent = 0); + ~QGeoPositionInfoSourceSimulator(); + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + PositioningMethods supportedPositioningMethods() const; + + void setUpdateInterval(int msec); + int minimumUpdateInterval() const; + Error error() const; + +public Q_SLOTS: + void startUpdates(); + void stopUpdates(); + + void requestUpdate(int timeout = 0); + +private slots: + void updatePosition(); +private: + Q_DISABLE_COPY(QGeoPositionInfoSourceSimulator); + QTimer *timer; + QTimer *requestTimer; + QGeoPositionInfo lastPosition; + QGeoPositionInfoSource::Error m_positionError; + void setError(QGeoPositionInfoSource::Error positionError); +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCESIMULATOR_H diff --git a/src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.cpp b/src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.cpp new file mode 100644 index 0000000..9ddc2b9 --- /dev/null +++ b/src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosourcefactory_simulator.h" + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactorySimulator::positionInfoSource(QObject *parent) +{ + return new QGeoPositionInfoSourceSimulator(parent); +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactorySimulator::satelliteInfoSource(QObject *parent) +{ + QGeoSatelliteInfoSourceSimulator *src = new QGeoSatelliteInfoSourceSimulator(parent); + if (!src->isConnected()) { + delete src; + src = 0; + } + return src; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactorySimulator::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} diff --git a/src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.h b/src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.h new file mode 100644 index 0000000..2d3146e --- /dev/null +++ b/src/plugins/position/simulator/qgeopositioninfosourcefactory_simulator.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_SIMULATOR_H +#define QGEOPOSITIONINFOSOURCEFACTORY_SIMULATOR_H + +#include +#include + +#include "qgeopositioninfosource_simulator_p.h" +#include "qgeosatelliteinfosource_simulator_p.h" + +class QGeoPositionInfoSourceFactorySimulator : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +#endif // QGEOPOSITIONINFOSOURCEFACTORY_SIMULATOR_H diff --git a/src/plugins/position/simulator/qgeosatelliteinfosource_simulator.cpp b/src/plugins/position/simulator/qgeosatelliteinfosource_simulator.cpp new file mode 100644 index 0000000..25f3842 --- /dev/null +++ b/src/plugins/position/simulator/qgeosatelliteinfosource_simulator.cpp @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeosatelliteinfosource_simulator_p.h" +#include "qlocationconnection_simulator_p.h" +#include "qlocationdata_simulator_p.h" + +QT_BEGIN_NAMESPACE + +QGeoSatelliteInfoSourceSimulator::QGeoSatelliteInfoSourceSimulator(QObject *parent) + : QGeoSatelliteInfoSource(parent) + , timer(new QTimer(this)) + , requestTimer(new QTimer(this)) +{ + Simulator::LocationConnection::ensureSimulatorConnection(); + + connect(timer, SIGNAL(timeout()), this, SLOT(updateData())); + requestTimer->setSingleShot(true); + connect(requestTimer, SIGNAL(timeout()), this, SLOT(updateData())); +} + +bool QGeoSatelliteInfoSourceSimulator::isConnected() const +{ + return Simulator::LocationConnection::ensureSimulatorConnection(); +} + +void QGeoSatelliteInfoSourceSimulator::startUpdates() +{ + int interval = updateInterval(); + if (interval < minimumUpdateInterval()) + interval = minimumUpdateInterval(); + timer->setInterval(interval); + timer->start(); +} + +void QGeoSatelliteInfoSourceSimulator::stopUpdates() +{ + timer->stop(); +} + +void QGeoSatelliteInfoSourceSimulator::requestUpdate(int timeout) +{ + if (!requestTimer->isActive()) { + // Get a single update within timeframe + if (timeout == 0) + timeout = minimumUpdateInterval(); + + if (timeout < minimumUpdateInterval()) + emit requestTimeout(); + else + requestTimer->start(timeout); + } +} + +void QGeoSatelliteInfoSourceSimulator::setUpdateInterval(int msec) +{ + // msec should be equal to or larger than the minimum update interval; 0 is a special case + // that currently behaves as if the interval is set to the minimum update interval + if (msec != 0 && msec < minimumUpdateInterval()) + msec = minimumUpdateInterval(); + + QGeoSatelliteInfoSource::setUpdateInterval(msec); + if (timer->isActive()) { + timer->setInterval(msec); + timer->start(); + } +} + +int QGeoSatelliteInfoSourceSimulator::minimumUpdateInterval() const +{ + return qtPositionInfo()->minimumInterval; +} + +void QGeoSatelliteInfoSourceSimulator::updateData() +{ + QList satellitesInUse; + QList satellitesInView; + + QGeoSatelliteInfoData *data = qtSatelliteInfo(); + for(int i = 0; i < data->satellites.count(); i++) { + QGeoSatelliteInfoData::SatelliteInfo info = data->satellites.at(i); + QGeoSatelliteInfo satInfo; + satInfo.setAttribute(QGeoSatelliteInfo::Azimuth, info.azimuth); + satInfo.setAttribute(QGeoSatelliteInfo::Elevation, info.elevation); + satInfo.setSignalStrength(info.signalStrength); + satInfo.setSatelliteSystem(static_cast(info.satelliteSystem)); + satInfo.setSatelliteIdentifier(info.satelliteIdentifier); + satellitesInView.append(satInfo); + if (info.inUse) + satellitesInUse.append(satInfo); + } + emit satellitesInViewUpdated(satellitesInView); + emit satellitesInUseUpdated(satellitesInUse); +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/simulator/qgeosatelliteinfosource_simulator_p.h b/src/plugins/position/simulator/qgeosatelliteinfosource_simulator_p.h new file mode 100644 index 0000000..c2fb0d0 --- /dev/null +++ b/src/plugins/position/simulator/qgeosatelliteinfosource_simulator_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSATELLITEINFOSOURCE_SIMULATOR_H +#define QGEOSATELLITEINFOSOURCE_SIMULATOR_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qgeosatelliteinfosource.h" +#include "qgeosatelliteinfo.h" + +#define MINIMUM_UPDATE_INTERVAL 1000 +#define DEFAULT_UPDATE_INTERVAL 5000 + +QT_BEGIN_NAMESPACE + +class QGeoSatelliteInfoSourceSimulator : public QGeoSatelliteInfoSource +{ + Q_OBJECT + +public: + explicit QGeoSatelliteInfoSourceSimulator(QObject *parent = 0); + + bool isConnected() const; + + virtual void setUpdateInterval(int msec); + virtual int minimumUpdateInterval() const; + + // Default implementation for error() + Error error() const { return QGeoSatelliteInfoSource::NoError; } +public slots: + virtual void startUpdates(); + virtual void stopUpdates(); + virtual void requestUpdate(int timeout = 5000); + +private slots: + void updateData(); + +private: + Q_DISABLE_COPY(QGeoSatelliteInfoSourceSimulator) + QTimer *timer; + QTimer *requestTimer; +}; + +QT_END_NAMESPACE + +#endif // QGEOSATELLITEINFOSOURCE_SIMULATOR_H + diff --git a/src/plugins/position/simulator/qlocationconnection_simulator.cpp b/src/plugins/position/simulator/qlocationconnection_simulator.cpp new file mode 100644 index 0000000..4835a9a --- /dev/null +++ b/src/plugins/position/simulator/qlocationconnection_simulator.cpp @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocationconnection_simulator_p.h" +#include "qgeopositioninfosource_simulator_p.h" +#include "qlocationdata_simulator_p.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +const QString simulatorName(QString::fromLatin1("QtSimulator_Mobility_ServerName1.3.0.0")); +const quint16 simulatorPort = 0xbeef + 1; + +namespace Simulator +{ + LocationConnection::LocationConnection() + : mConnection(new Connection(Connection::Client, simulatorName, simulatorPort, Version(1,3,0,0))) + { + qt_registerLocationTypes(); + mWorker = mConnection->connectToServer(Connection::simulatorHostName(true), simulatorPort); + if (!mWorker) + return; + + mWorker->addReceiver(this); + + // register for location notifications + mWorker->call("setRequestsLocationInfo"); + } + + LocationConnection::~LocationConnection() + { + delete mWorker; + delete mConnection; + } + + bool LocationConnection::ensureSimulatorConnection() + { + static LocationConnection locationConnection; + return locationConnection.mWorker; + } + + void LocationConnection::initialLocationDataSent() + { + emit initialDataReceived(); + } + + void LocationConnection::setLocationData(const QGeoPositionInfoData &data) + { + *qtPositionInfo() = data; + } + + void LocationConnection::setSatelliteData(const QGeoSatelliteInfoData &data) + { + *qtSatelliteInfo() = data; + } + +} // namespace + +QGeoPositionInfoData *qtPositionInfo() +{ + static QGeoPositionInfoData *positionInfo = 0; + if (!positionInfo) { + positionInfo = new QGeoPositionInfoData; + } + + return positionInfo; +} + +QGeoSatelliteInfoData *qtSatelliteInfo() +{ + static QGeoSatelliteInfoData *satelliteInfo = 0; + if (!satelliteInfo) { + satelliteInfo = new QGeoSatelliteInfoData; + } + + return satelliteInfo; +} + + +QT_END_NAMESPACE diff --git a/src/plugins/position/simulator/qlocationconnection_simulator_p.h b/src/plugins/position/simulator/qlocationconnection_simulator_p.h new file mode 100644 index 0000000..a13c9e8 --- /dev/null +++ b/src/plugins/position/simulator/qlocationconnection_simulator_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOCATIONCONNECTION_H +#define QLOCATIONCONNECTION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qlocationdata_simulator_p.h" + +#include + +QT_BEGIN_NAMESPACE + +namespace Simulator +{ + class Connection; + class ConnectionWorker; + + class LocationConnection : public QObject + { + Q_OBJECT + + public: + static bool ensureSimulatorConnection(); + virtual ~LocationConnection(); + + private: + LocationConnection(); + Q_DISABLE_COPY(LocationConnection) + + private slots: + // these will be called by the simulator + void setLocationData(const QGeoPositionInfoData &); + void setSatelliteData(const QGeoSatelliteInfoData &); + void initialLocationDataSent(); + + signals: + void initialDataReceived(); + + private: + Connection *mConnection; + ConnectionWorker *mWorker; + }; +} // end namespace Simulator + +QGeoPositionInfoData *qtPositionInfo(); +QGeoSatelliteInfoData *qtSatelliteInfo(); + +QT_END_NAMESPACE + +#endif // QLOCATIONCONNECTION_H diff --git a/src/plugins/position/simulator/simulator.pro b/src/plugins/position/simulator/simulator.pro new file mode 100644 index 0000000..c3e6ea3 --- /dev/null +++ b/src/plugins/position/simulator/simulator.pro @@ -0,0 +1,22 @@ +TARGET = qtposition_simulator + +QT = core network positioning simulator + +INCLUDEPATH += ../../../positioning + +DEFINES += QT_SIMULATOR +SOURCES += qgeopositioninfosource_simulator.cpp \ + qgeosatelliteinfosource_simulator.cpp \ + qlocationconnection_simulator.cpp \ + qgeopositioninfosourcefactory_simulator.cpp +HEADERS += qgeopositioninfosource_simulator_p.h \ + qgeosatelliteinfosource_simulator_p.h \ + qlocationconnection_simulator_p.h \ + qgeopositioninfosourcefactory_simulator.h + +OTHER_FILES += \ + plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactorySimulator +load(qt_plugin) diff --git a/src/plugins/position/winrt/plugin.json b/src/plugins/position/winrt/plugin.json new file mode 100644 index 0000000..0696cb0 --- /dev/null +++ b/src/plugins/position/winrt/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["winrt"], + "Provider": "winrt", + "Position": true, + "Satellite": false, + "Monitor" : false, + "Priority": 1000, + "Testable": false +} diff --git a/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp new file mode 100644 index 0000000..046d862 --- /dev/null +++ b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp @@ -0,0 +1,567 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosource_winrt_p.h" + +#include +#include +#include +#ifdef Q_OS_WINRT +#include +#endif + +#include +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Devices::Geolocation; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; + +typedef ITypedEventHandler GeoLocatorPositionHandler; +typedef ITypedEventHandler GeoLocatorStatusHandler; +typedef IAsyncOperationCompletedHandler PositionHandler; +typedef IAsyncOperationCompletedHandler AccessHandler; + +QT_BEGIN_NAMESPACE + +#ifndef Q_OS_WINRT +namespace QEventDispatcherWinRT { +HRESULT runOnXamlThread(const std::function &delegate, bool waitForRun = true) +{ + Q_UNUSED(waitForRun); + return delegate(); +} +} +#endif + +class QGeoPositionInfoSourceWinRTPrivate { +public: + ComPtr locator; + QTimer periodicTimer; + QTimer singleUpdateTimer; + QGeoPositionInfo lastPosition; + QGeoPositionInfoSource::Error positionError; + EventRegistrationToken statusToken; + EventRegistrationToken positionToken; + QMutex mutex; + bool updatesOngoing; +}; + + +QGeoPositionInfoSourceWinRT::QGeoPositionInfoSourceWinRT(QObject *parent) + : QGeoPositionInfoSource(parent) + , d_ptr(new QGeoPositionInfoSourceWinRTPrivate) +{ + Q_D(QGeoPositionInfoSourceWinRT); + d->positionError = QGeoPositionInfoSource::NoError; + d->updatesOngoing = false; +} + +QGeoPositionInfoSourceWinRT::~QGeoPositionInfoSourceWinRT() +{ +} + +int QGeoPositionInfoSourceWinRT::init() +{ + Q_D(QGeoPositionInfoSourceWinRT); + if (!requestAccess()) { + qWarning ("Location access failed."); + return -1; + } + HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() { + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(), + &d->locator); + RETURN_HR_IF_FAILED("Could not initialize native location services."); + + hr = d->locator->add_StatusChanged(Callback(this, + &QGeoPositionInfoSourceWinRT::onStatusChanged).Get(), + &d->statusToken); + RETURN_HR_IF_FAILED("Could not add status callback."); + + hr = d->locator->put_ReportInterval(1000); + RETURN_HR_IF_FAILED("Could not initialize report interval."); + + return hr; + }); + if (FAILED(hr)) { + setError(QGeoPositionInfoSource::UnknownSourceError); + qErrnoWarning(hr, "Could not register status changed callback"); + return -1; + } + + hr = d->locator->put_DesiredAccuracy(PositionAccuracy::PositionAccuracy_Default); + if (FAILED(hr)) { + setError(QGeoPositionInfoSource::UnknownSourceError); + qErrnoWarning(hr, "Could not initialize desired accuracy."); + return -1; + } + + d->positionToken.value = 0; + + d->periodicTimer.setSingleShot(true); + d->periodicTimer.setInterval(minimumUpdateInterval()); + connect(&d->periodicTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate); + + d->singleUpdateTimer.setSingleShot(true); + connect(&d->singleUpdateTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut); + + setPreferredPositioningMethods(QGeoPositionInfoSource::AllPositioningMethods); + + connect(this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate, this, &QGeoPositionInfoSourceWinRT::updateSynchronized); + return 0; +} + +QGeoPositionInfo QGeoPositionInfoSourceWinRT::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const +{ + Q_D(const QGeoPositionInfoSourceWinRT); + Q_UNUSED(fromSatellitePositioningMethodsOnly) + return d->lastPosition; +} + +QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceWinRT::supportedPositioningMethods() const +{ + Q_D(const QGeoPositionInfoSourceWinRT); + + PositionStatus status; + HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, &status]() { + HRESULT hr = d->locator->get_LocationStatus(&status); + return hr; + }); + if (FAILED(hr)) + return QGeoPositionInfoSource::NoPositioningMethods; + + switch (status) { + case PositionStatus::PositionStatus_NoData: + case PositionStatus::PositionStatus_Disabled: + case PositionStatus::PositionStatus_NotAvailable: + return QGeoPositionInfoSource::NoPositioningMethods; + } + + return QGeoPositionInfoSource::AllPositioningMethods; +} + +void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods) +{ + Q_D(QGeoPositionInfoSourceWinRT); + + PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); + QGeoPositionInfoSource::setPreferredPositioningMethods(methods); + if (previousPreferredPositioningMethods == preferredPositioningMethods()) + return; + + bool needsRestart = d->positionToken.value != 0; + + if (needsRestart) + stopHandler(); + + PositionAccuracy acc = methods & PositioningMethod::SatellitePositioningMethods ? + PositionAccuracy::PositionAccuracy_High : + PositionAccuracy::PositionAccuracy_Default; + HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, acc]() { + HRESULT hr = d->locator->put_DesiredAccuracy(acc); + return hr; + }); + RETURN_VOID_IF_FAILED("Could not set positioning accuracy."); + + if (needsRestart) + startHandler(); +} + +void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec) +{ + Q_D(QGeoPositionInfoSourceWinRT); + // Windows Phone 8.1 and Windows 10 do not support 0 interval +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP) + if (msec == 0) + msec = minimumUpdateInterval(); +#endif + + // If msec is 0 we send updates as data becomes available, otherwise we force msec to be equal + // to or larger than the minimum update interval. + if (msec != 0 && msec < minimumUpdateInterval()) + msec = minimumUpdateInterval(); + + HRESULT hr = d->locator->put_ReportInterval(msec); + if (FAILED(hr)) { + setError(QGeoPositionInfoSource::UnknownSourceError); + qErrnoWarning(hr, "Failed to set update interval"); + return; + } + + d->periodicTimer.setInterval(qMax(msec, minimumUpdateInterval())); + + QGeoPositionInfoSource::setUpdateInterval(msec); +} + +int QGeoPositionInfoSourceWinRT::minimumUpdateInterval() const +{ + // We use one second to reduce potential timer events + // in case the platform itself stops reporting + return 1000; +} + +void QGeoPositionInfoSourceWinRT::startUpdates() +{ + Q_D(QGeoPositionInfoSourceWinRT); + + if (d->updatesOngoing) + return; + + if (!startHandler()) + return; + d->updatesOngoing = true; + d->periodicTimer.start(); +} + +void QGeoPositionInfoSourceWinRT::stopUpdates() +{ + Q_D(QGeoPositionInfoSourceWinRT); + + stopHandler(); + d->updatesOngoing = false; + d->periodicTimer.stop(); +} + +bool QGeoPositionInfoSourceWinRT::startHandler() +{ + Q_D(QGeoPositionInfoSourceWinRT); + + // Check if already attached + if (d->positionToken.value != 0) + return true; + + if (preferredPositioningMethods() == QGeoPositionInfoSource::NoPositioningMethods) { + setError(QGeoPositionInfoSource::UnknownSourceError); + return false; + } + + if (!requestAccess() || !checkNativeState()) + return false; + + HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() { + HRESULT hr; + + // We need to call this at least once on Windows 10 Mobile. + // Unfortunately this operation does not have a completion handler + // registered. That could have helped in the single update case + ComPtr> op; + hr = d->locator->GetGeopositionAsync(&op); + + hr = d->locator->add_PositionChanged(Callback(this, + &QGeoPositionInfoSourceWinRT::onPositionChanged).Get(), + &d->positionToken); + return hr; + }); + if (FAILED(hr)) { + setError(QGeoPositionInfoSource::UnknownSourceError); + qErrnoWarning(hr, "Could not add position handler"); + return false; + } + + return true; +} + +void QGeoPositionInfoSourceWinRT::stopHandler() +{ + Q_D(QGeoPositionInfoSourceWinRT); + + if (!d->positionToken.value) + return; + QEventDispatcherWinRT::runOnXamlThread([d]() { + d->locator->remove_PositionChanged(d->positionToken); + return S_OK; + }); + d->positionToken.value = 0; +} + +void QGeoPositionInfoSourceWinRT::requestUpdate(int timeout) +{ + Q_D(QGeoPositionInfoSourceWinRT); + + if (timeout != 0 && timeout < minimumUpdateInterval()) { + emit updateTimeout(); + return; + } + + if (timeout == 0) + timeout = 2*60*1000; // Maximum time for cold start (see Android) + + startHandler(); + d->singleUpdateTimer.start(timeout); +} + +void QGeoPositionInfoSourceWinRT::virtualPositionUpdate() +{ + Q_D(QGeoPositionInfoSourceWinRT); + QMutexLocker locker(&d->mutex); + + // Need to check if services are still running and ok + if (!checkNativeState()) { + stopUpdates(); + return; + } + + // The operating system did not provide information in time + // Hence we send a virtual position update to keep same behavior + // between backends. + // This only applies to the periodic timer, not for single requests + // We can only do this if we received a valid position before + if (d->lastPosition.isValid()) { + QGeoPositionInfo sent = d->lastPosition; + sent.setTimestamp(sent.timestamp().addMSecs(updateInterval())); + d->lastPosition = sent; + emit positionUpdated(sent); + } + d->periodicTimer.start(); +} + +void QGeoPositionInfoSourceWinRT::singleUpdateTimeOut() +{ + Q_D(QGeoPositionInfoSourceWinRT); + QMutexLocker locker(&d->mutex); + + if (d->singleUpdateTimer.isActive()) { + emit updateTimeout(); + if (!d->updatesOngoing) + stopHandler(); + } +} + +void QGeoPositionInfoSourceWinRT::updateSynchronized(QGeoPositionInfo currentInfo) +{ + Q_D(QGeoPositionInfoSourceWinRT); + QMutexLocker locker(&d->mutex); + + d->periodicTimer.stop(); + d->lastPosition = currentInfo; + + if (d->updatesOngoing) + d->periodicTimer.start(); + + if (d->singleUpdateTimer.isActive()) { + d->singleUpdateTimer.stop(); + if (!d->updatesOngoing) + stopHandler(); + } + + emit positionUpdated(currentInfo); +} + +QGeoPositionInfoSource::Error QGeoPositionInfoSourceWinRT::error() const +{ + Q_D(const QGeoPositionInfoSourceWinRT); + return d->positionError; +} + +void QGeoPositionInfoSourceWinRT::setError(QGeoPositionInfoSource::Error positionError) +{ + Q_D(QGeoPositionInfoSourceWinRT); + + if (positionError == d->positionError) + return; + d->positionError = positionError; + emit QGeoPositionInfoSource::error(positionError); +} + +bool QGeoPositionInfoSourceWinRT::checkNativeState() +{ + Q_D(QGeoPositionInfoSourceWinRT); + + PositionStatus status; + HRESULT hr = d->locator->get_LocationStatus(&status); + if (FAILED(hr)) { + setError(QGeoPositionInfoSource::UnknownSourceError); + qErrnoWarning(hr, "Could not query status"); + return false; + } + + bool result = false; + switch (status) { + case PositionStatus::PositionStatus_NotAvailable: + setError(QGeoPositionInfoSource::UnknownSourceError); + break; + case PositionStatus::PositionStatus_Disabled: + case PositionStatus::PositionStatus_NoData: + setError(QGeoPositionInfoSource::ClosedError); + break; + default: + setError(QGeoPositionInfoSource::NoError); + result = true; + break; + } + return result; +} + +HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPositionChangedEventArgs *args) +{ + Q_UNUSED(locator); + + HRESULT hr; + ComPtr pos; + hr = args->get_Position(&pos); + RETURN_HR_IF_FAILED("Could not access position object."); + + QGeoPositionInfo currentInfo; + + ComPtr coord; + hr = pos->get_Coordinate(&coord); + if (FAILED(hr)) + qErrnoWarning(hr, "Could not access coordinate"); + + DOUBLE lat; + hr = coord->get_Latitude(&lat); + if (FAILED(hr)) + qErrnoWarning(hr, "Could not access latitude"); + + DOUBLE lon; + hr = coord->get_Longitude(&lon); + if (FAILED(hr)) + qErrnoWarning(hr, "Could not access longitude"); + + // Depending on data source altitude can + // be identified or not + IReference *alt; + hr = coord->get_Altitude(&alt); + if (SUCCEEDED(hr) && alt) { + double altd; + hr = alt->get_Value(&altd); + currentInfo.setCoordinate(QGeoCoordinate(lat, lon, altd)); + } else { + currentInfo.setCoordinate(QGeoCoordinate(lat, lon)); + } + + DOUBLE accuracy; + hr = coord->get_Accuracy(&accuracy); + if (SUCCEEDED(hr)) + currentInfo.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy); + + IReference *altAccuracy; + hr = coord->get_AltitudeAccuracy(&altAccuracy); + if (SUCCEEDED(hr) && altAccuracy) { + double value; + hr = alt->get_Value(&value); + currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value); + } + + IReference *speed; + hr = coord->get_Speed(&speed); + if (SUCCEEDED(hr) && speed) { + double value; + hr = speed->get_Value(&value); + currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value); + } + + IReference *heading; + hr = coord->get_Heading(&heading); + if (SUCCEEDED(hr) && heading) { + double value; + hr = heading->get_Value(&value); + double mod = 0; + value = modf(value, &mod); + value += static_cast(mod) % 360; + if (value >=0 && value <= 359) // get_Value might return nan/-nan + currentInfo.setAttribute(QGeoPositionInfo::Direction, value); + } + + DateTime dateTime; + hr = coord->get_Timestamp(&dateTime); + + if (dateTime.UniversalTime > 0) { + ULARGE_INTEGER uLarge; + uLarge.QuadPart = dateTime.UniversalTime; + FILETIME fileTime; + fileTime.dwHighDateTime = uLarge.HighPart; + fileTime.dwLowDateTime = uLarge.LowPart; + SYSTEMTIME systemTime; + if (FileTimeToSystemTime(&fileTime, &systemTime)) { + currentInfo.setTimestamp(QDateTime(QDate(systemTime.wYear, systemTime.wMonth, + systemTime.wDay), + QTime(systemTime.wHour, systemTime.wMinute, + systemTime.wSecond, systemTime.wMilliseconds), + Qt::UTC)); + } + } + + emit nativePositionUpdate(currentInfo); + + return S_OK; +} + +HRESULT QGeoPositionInfoSourceWinRT::onStatusChanged(IGeolocator*, IStatusChangedEventArgs *args) +{ + PositionStatus st; + args->get_Status(&st); + return S_OK; +} + +bool QGeoPositionInfoSourceWinRT::requestAccess() const +{ +#ifdef Q_OS_WINRT + static GeolocationAccessStatus accessStatus = GeolocationAccessStatus_Unspecified; + static ComPtr statics; + + if (accessStatus == GeolocationAccessStatus_Allowed) + return true; + else if (accessStatus == GeolocationAccessStatus_Denied) + return false; + + ComPtr> op; + HRESULT hr; + hr = QEventDispatcherWinRT::runOnXamlThread([&op]() { + HRESULT hr; + hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(), + IID_PPV_ARGS(&statics)); + RETURN_HR_IF_FAILED("Could not access Geolocation Statics."); + + hr = statics->RequestAccessAsync(&op); + return hr; + }); + Q_ASSERT_SUCCEEDED(hr); + + // We cannot wait inside the XamlThread as that would deadlock + QWinRTFunctions::await(op, &accessStatus); + return accessStatus == GeolocationAccessStatus_Allowed; +#else // Q_OS_WINRT + return true; +#endif // Q_OS_WINRT +} + +QT_END_NAMESPACE diff --git a/src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h b/src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h new file mode 100644 index 0000000..9f3a1c7 --- /dev/null +++ b/src/plugins/position/winrt/qgeopositioninfosource_winrt_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEWINRT_H +#define QGEOPOSITIONINFOSOURCEWINRT_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeopositioninfosource.h" +#include "qgeopositioninfo.h" + +#include + +#include +#include + +namespace ABI { + namespace Windows { + namespace Devices { + namespace Geolocation{ + struct IGeolocator; + struct IPositionChangedEventArgs; + struct IStatusChangedEventArgs; + } + } + } +} + +QT_BEGIN_NAMESPACE + +class QGeoPositionInfoSourceWinRTPrivate; + +class QGeoPositionInfoSourceWinRT : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + QGeoPositionInfoSourceWinRT(QObject *parent = 0); + ~QGeoPositionInfoSourceWinRT(); + int init(); + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + PositioningMethods supportedPositioningMethods() const; + + void setPreferredPositioningMethods(PositioningMethods methods); + + void setUpdateInterval(int msec); + int minimumUpdateInterval() const; + Error error() const; + + HRESULT onPositionChanged(ABI::Windows::Devices::Geolocation::IGeolocator *locator, + ABI::Windows::Devices::Geolocation::IPositionChangedEventArgs *args); + + HRESULT onStatusChanged(ABI::Windows::Devices::Geolocation::IGeolocator*, + ABI::Windows::Devices::Geolocation::IStatusChangedEventArgs *args); + + bool requestAccess() const; +Q_SIGNALS: + void nativePositionUpdate(const QGeoPositionInfo); +public slots: + void startUpdates(); + void stopUpdates(); + + void requestUpdate(int timeout = 0); + +private slots: + void stopHandler(); + void virtualPositionUpdate(); + void singleUpdateTimeOut(); + void updateSynchronized(const QGeoPositionInfo info); +private: + bool startHandler(); + + Q_DISABLE_COPY(QGeoPositionInfoSourceWinRT) + void setError(QGeoPositionInfoSource::Error positionError); + bool checkNativeState(); + + QScopedPointer d_ptr; + Q_DECLARE_PRIVATE(QGeoPositionInfoSourceWinRT) +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCEWINRT_H diff --git a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp new file mode 100644 index 0000000..e58744a --- /dev/null +++ b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosourcefactory_winrt.h" +#include "qgeopositioninfosource_winrt_p.h" + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryWinRT::positionInfoSource(QObject *parent) +{ + QGeoPositionInfoSourceWinRT *src = new QGeoPositionInfoSourceWinRT(parent); + if (src->init() < 0) { + delete src; + src = 0; + } + return src; +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryWinRT::satelliteInfoSource(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryWinRT::areaMonitor(QObject *parent) +{ + Q_UNUSED(parent); + return 0; +} diff --git a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h new file mode 100644 index 0000000..46cd385 --- /dev/null +++ b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_WINRT_H +#define QGEOPOSITIONINFOSOURCEFACTORY_WINRT_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoPositionInfoSourceFactoryWinRT : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCEFACTORY_WINRT_H diff --git a/src/plugins/position/winrt/winrt.pro b/src/plugins/position/winrt/winrt.pro new file mode 100644 index 0000000..446cb34 --- /dev/null +++ b/src/plugins/position/winrt/winrt.pro @@ -0,0 +1,16 @@ +TARGET = qtposition_winrt + +QT = core core-private positioning + +SOURCES += qgeopositioninfosource_winrt.cpp \ + qgeopositioninfosourcefactory_winrt.cpp +HEADERS += qgeopositioninfosource_winrt_p.h \ + qgeopositioninfosourcefactory_winrt.h + +OTHER_FILES += \ + plugin.json + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = QGeoPositionInfoSourceFactoryWinRT +msvc:!winrt: LIBS += runtimeobject.lib +load(qt_plugin) diff --git a/src/positioning/configure.json b/src/positioning/configure.json new file mode 100644 index 0000000..49e32be --- /dev/null +++ b/src/positioning/configure.json @@ -0,0 +1,48 @@ +{ + "module": "positioning", + "testDir": "../../config.tests", + + "libraries": { + "gypsy": { + "label": "Gypsy", + "test": "gypsy", + "sources": [ + { "type": "pkgConfig", "args": "gypsy gconf-2.0" } + ] + } + }, + + "tests": { + "winrt_geolocation": { + "label": "WinRT Geolocation API", + "type": "compile", + "test": "winrt" + } + }, + + "features": { + "gypsy": { + "label": "Gypsy GPS Daemon", + "condition": "libs.gypsy", + "output": [ "privateFeature" ] + }, + "winrt_geolocation": { + "label": "WinRT Geolocation API", + "condition": "tests.winrt_geolocation", + "output": [ "privateFeature" ] + } + }, + + "report": [ + ], + + "summary": [ + { + "section": "Qt Positioning", + "entries": [ + "gypsy", + "winrt_geolocation" + ] + } + ] +} diff --git a/src/positioning/doc/qtpositioning.qdocconf b/src/positioning/doc/qtpositioning.qdocconf new file mode 100644 index 0000000..3f6438e --- /dev/null +++ b/src/positioning/doc/qtpositioning.qdocconf @@ -0,0 +1,57 @@ +include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) + +project = QtPositioning +description = Qt Positioning Reference Documentation +version = $QT_VERSION + + + +qhp.projects = QtPositioning + +qhp.QtPositioning.file = qtpositioning.qhp +qhp.QtPositioning.namespace = org.qt-project.qtpositioning.$QT_VERSION_TAG +qhp.QtPositioning.virtualFolder = qtpositioning +qhp.QtPositioning.indexTitle = Qt Positioning +qhp.QtPositioning.indexRoot = + +qhp.QtPositioning.filterAttributes = qtpositioning $QT_VERSION qtrefdoc +qhp.QtPositioning.customFilters.Qt.name = QtPositioning $QT_VERSION +qhp.QtPositioning.customFilters.Qt.filterAttributes = qtpositioning $QT_VERSION +qhp.QtPositioning.subprojects = classes qml examples +qhp.QtPositioning.subprojects.classes.title = C++ Classes +qhp.QtPositioning.subprojects.classes.indexTitle = Qt Positioning C++ Classes +qhp.QtPositioning.subprojects.classes.selectors = class fake:headerfile +qhp.QtPositioning.subprojects.classes.sortPages = true +qhp.QtPositioning.subprojects.qml.title = QML Types +qhp.QtPositioning.subprojects.qml.indexTitle = Qt Positioning QML Types +qhp.QtPositioning.subprojects.qml.selectors = qmlclass +qhp.QtPositioning.subprojects.qml.sortPages = true +qhp.QtPositioning.subprojects.examples.title = Qt Positioning Examples +qhp.QtPositioning.subprojects.examples.indexTitle = Qt Positioning Examples +qhp.QtPositioning.subprojects.examples.selectors = fake:example + +tagfile = ../../../doc/qtpositioning/qtpositioning.tags + +depends += qtcore qtdoc qtquick qtqml qtnetwork qtlocation + +headerdirs += .. \ + ../../imports/positioning \ + ../../positioningquick + +sourcedirs += .. \ + ../../imports/positioning \ + ../../positioningquick + +examplesinstallpath = positioning + +exampledirs += ../../../examples/positioning \ + snippets/ + + +imagedirs += images + +navigation.landingpage = "Qt Positioning" +navigation.cppclassespage = "Qt Positioning C++ Classes" +navigation.qmltypespage = "Qt Positioning QML Types" + +manifestmeta.thumbnail.names += "QtPositioning/Log File*" diff --git a/src/positioning/doc/snippets/cpp/cpp.pro b/src/positioning/doc/snippets/cpp/cpp.pro new file mode 100644 index 0000000..47401e9 --- /dev/null +++ b/src/positioning/doc/snippets/cpp/cpp.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +TARGET = positioning_cppsnippet +QT = core positioning + +SOURCES += \ + main.cpp \ + cppqml.cpp + diff --git a/src/positioning/doc/snippets/cpp/cppqml.cpp b/src/positioning/doc/snippets/cpp/cppqml.cpp new file mode 100644 index 0000000..3db5571 --- /dev/null +++ b/src/positioning/doc/snippets/cpp/cppqml.cpp @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +void cppQmlInterface(QObject *qmlObject) +{ + //! [Address get] + QGeoAddress geoAddress = qmlObject->property("address").value(); + //! [Address get] + + //! [Address set] + qmlObject->setProperty("address", QVariant::fromValue(geoAddress)); + //! [Address set] + + //! [Location get] + QGeoLocation geoLocation = qmlObject->property("location").value(); + //! [Location get] + + //! [Location set] + qmlObject->setProperty("location", QVariant::fromValue(geoLocation)); + //! [Location set] +} + +class MyClass : public QObject +{ + Q_OBJECT +//! [BigBen] +public: + MyClass() : QObject() + { + QGeoAreaMonitorSource *monitor = QGeoAreaMonitorSource::createDefaultSource(this); + if (monitor) { + connect(monitor, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo)), + this, SLOT(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo))); + connect(monitor, SIGNAL(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo)), + this, SLOT(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo))); + + QGeoAreaMonitorInfo bigBen("Big Ben"); + QGeoCoordinate position(51.50104, -0.124632); + bigBen.setArea(QGeoCircle(position, 100)); + + monitor->startMonitoring(bigBen); + + } else { + qDebug() << "Could not create default area monitor"; + } + } + +public Q_SLOTS: + void areaEntered(const QGeoAreaMonitorInfo &mon, const QGeoPositionInfo &update) + { + Q_UNUSED(mon) + + qDebug() << "Now within 100 meters, current position is" << update.coordinate(); + } + + void areaExited(const QGeoAreaMonitorInfo &mon, const QGeoPositionInfo &update) + { + Q_UNUSED(mon) + + qDebug() << "No longer within 100 meters, current position is" << update.coordinate(); + } +//! [BigBen] +}; diff --git a/src/positioning/doc/snippets/cpp/main.cpp b/src/positioning/doc/snippets/cpp/main.cpp new file mode 100644 index 0000000..285bf6a --- /dev/null +++ b/src/positioning/doc/snippets/cpp/main.cpp @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +int main(int /*argc*/, char ** /*argv*/) +{ + return 0; +} + diff --git a/src/positioning/doc/snippets/doc_src_qtpositioning.qml b/src/positioning/doc/snippets/doc_src_qtpositioning.qml new file mode 100644 index 0000000..ee6e832 --- /dev/null +++ b/src/positioning/doc/snippets/doc_src_qtpositioning.qml @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//! [import] +import QtPositioning 5.11 +//! [import] + +Item { +} + diff --git a/src/positioning/doc/snippets/snippets.pro b/src/positioning/doc/snippets/snippets.pro new file mode 100644 index 0000000..451d1c3 --- /dev/null +++ b/src/positioning/doc/snippets/snippets.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += cpp diff --git a/src/positioning/doc/src/cpp-position.qdoc b/src/positioning/doc/src/cpp-position.qdoc new file mode 100644 index 0000000..085aecd --- /dev/null +++ b/src/positioning/doc/src/cpp-position.qdoc @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-positioning-cpp.html + +\title Positioning (C++) + +\brief The Location Positioning API enables location positioning by means of +GPS or an NMEA data source. + +\section1 Positioning + +The Positioning component of the Qt Location API is about the geographical position, size +and address of some place. Positioning contains a QGeoCoordinate class, containing latitude, longitude and altitude in meters. QGeoLocation contains a QGeoCoordinate plus address +and size information (a bounding box) so that positions can be more than mathematical points. +Movement into or out of the defined bounding box areas can be monitored. The API +also allows the developer to control the source of the positional information +as well. + +Location data involves a precisely specified position on the Earth's +surface \unicode {0x2014} as provided by a latitude-longitude coordinate +\unicode {0x2014} along with associated data, such as: + + \list + \li The date and time at which the position was reported + \li The velocity of the device that reported the position + \li The altitude of the reported position (height above sea level) + \li The bearing of the device in degrees, relative to true north + \endlist + +This data can be extracted through a variety of methods. One of the most +well known methods of positioning is GPS (Global Positioning System), a +publicly available system that uses radiowave signals received from +Earth-orbiting satellites to calculate the precise position and time of +the receiver. Another popular method is 'Cell Identifier Positioning', which uses +the cell identifier of the cell site that is currently serving the receiving +device to calculate its approximate location. These and other positioning +methods can all be used with the Location API; the only requirement for a +location data source within the API is that it provides a +latitude-longitude coordinate with a date/time value, with the option of +providing the other attributes listed above. + + +Location data sources are created by subclassing QGeoPositionInfoSource and +providing QGeoPositionInfo objects through the +QGeoPositionInfoSource::positionUpdated() signal. Clients that require +location data can connect to the +\l{QGeoPositionInfoSource::positionUpdated()}{positionUpdated()} signal and +call \l{QGeoPositionInfoSource::startUpdates()}{startUpdates()} or +\l{QGeoPositionInfoSource::requestUpdate()}{requestUpdate()} to trigger the +distribution of location data. The location data distribution can be stopped by +calling the \l {QGeoPositionInfoSource::stopUpdates()}{stopUpdates()} function. + +A default position source may be available on some platforms. Call +QGeoPositionInfoSource::createDefaultSource() to create an instance of the default +position source; the method returns 0 if no default source is available for +the platform. + +If a problem occurs with access to the information source then an +\l {QGeoPositionInfoSource::error()}{error()} signal is emitted. + +The QGeoAreaMonitorSource class enables client applications to be notified when +the receiving device has moved in or out of a particular area, as specified +by a coordinate and radius. If the platform provides built-in support for +area monitoring, QGeoAreaMonitorSource::createDefaultMonitor() returns an instance of +the default area monitor. + +Satellite information can also be distributed through the +QGeoSatelliteInfoSource class. Call QGeoSatelliteInfoSource::createDefaultSource() to +create an instance of the default satellite data source for the platform, +if one is available. Alternatively, clients can subclass it to provide a +custom satellite data source. + + + +\section2 Requesting Location Data from Data Sources + +To receive data from a source, connect to its +\l{QGeoPositionInfoSource::positionUpdated()}{positionUpdated()} signal, +then call either \l{QGeoPositionInfoSource::startUpdates()}{startUpdates()} +or \l{QGeoPositionInfoSource::requestUpdate()}{requestUpdate()} to begin. + +Here is an example of a client that receives data from the default location +data source, as returned by QGeoPositionInfoSource::createDefaultSource(): + +\code +class MyClass : public QObject +{ + Q_OBJECT +public: + MyClass(QObject *parent = 0) + : QObject(parent) + { + QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource(this); + if (source) { + connect(source, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdated(QGeoPositionInfo))); + source->startUpdates(); + } + } + +private slots: + void positionUpdated(const QGeoPositionInfo &info) + { + qDebug() << "Position updated:" << info; + } +}; + +\endcode + +\section2 Controlling Aspects of Data Sources + +The QGeoPositionInfoSource::setUpdateInterval() method can be used to +control the rate at which position updates are received. For example, if +the client application only requires updates once every 30 seconds, it can +call \c setUpdateInterval(30000). (If no update interval is set, or +\l {QGeoPositionInfoSource::}{setUpdateInterval()} is called with a value of 0, the source uses a default +interval or some other internal logic to determine when updates should be +provided.) + +QGeoPositionInfoSource::setPreferredPositioningMethods() enables client +applications to request that a certain type of positioning method be used. +For example, if the application prefers to use only satellite positioning, +which offers fairly precise outdoor positioning but can be a heavy user of +power resources, it can call this method with the +QGeoPositionInfoSource::SatellitePositioningMethods value. However, this +method should only be used in specialized client applications; in most +cases, the default positioning methods should not be changed, as a source +may internally use a variety of positioning methods that can be useful to +the application. + +\section2 NMEA Data + +\l {http://en.wikipedia.org/wiki/NMEA_0183}{NMEA} is a common text-based +protocol for specifying navigational data. For convenience, the +QNmeaPositionInfoSource is provided to enable client applications to read +and distribute NMEA data in either real-time mode (for example, when +streaming from a GPS device) or simulation mode (for example, when reading +from a NMEA log file). In simulation mode, the source will emit updates +according to the time stamp of each NMEA sentence to produce a "replay" +of the recorded data. + +Generally, the capabilities provided by the default position source as +returned by QGeoPositionInfoSource::createDefaultSource(), along with the +QNmeaPositionInfoSource class, are sufficient for retrieving location +data. However, in some cases developers may wish to write their own custom +location data source. + +The \l {Log File Position Source (C++)} example demonstrates how to subclass QGeoPositionInfoSource +to create a custom positioning source. + + +\section1 Examples + +\section3 \b{Flickr Example} + +The \l{GeoFlickr QML}{Flickr Example} uses the current location to download thumbnail +images from Flickr relevant to the current location. + + + +\section1 Positioning Classes + +\annotatedlist QtPositioning-positioning + +*/ diff --git a/src/positioning/doc/src/cpp-qml-positioning.qdoc b/src/positioning/doc/src/cpp-qml-positioning.qdoc new file mode 100644 index 0000000..6ae3f26 --- /dev/null +++ b/src/positioning/doc/src/cpp-qml-positioning.qdoc @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page positioning-cpp-qml.html +\title Interfaces between C++ and QML Code in Qt Positioning + +\brief Some of the providing QtPositioning QML types providing interfaces to access and modify properties in C++. + +\section1 Overview + +Depending on the type of C++ class QtPositioning utilizes two methods to simplify +exchange of position data between C++ and QML code. + +\target Cpp_value_integration_positioning +\section1 Direct C++ Value Integration in QtPositioning + +Starting with Qt 5.5, it has become much easier to integrate non-QObject based +data types into QML. This is achieved by adding \l Q_GADGET support to QtQml. +The macro converts classes into a light-weight +version of a QObject without the required \l QObject inheritance. At the same time +it retains the reflection capabilities of \l QMetaObject. As a result they can be directly +exposed to QML and do not require any further wrapper classes. + +A significant number of Position and Location +related data types were converted to Q_GADGETs. They retain their API and value +type character but have become introspectable via \l QMetaObject. This conversion +was done to the following classes: + +\list +\li \l QGeoCircle +\li \l QGeoCoordinate +\li \l QGeoRectangle +\li \l QGeoShape +\endlist + +Using \l QGeoCoordinate as an example, the C++ types are directly exposed to the +QML environment via its meta type: + +\code + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); +\endcode + +The above registration of \l QGeoCoordinate is automatically done once by the +QtPositioning QML plugin. The \l{Plane Spotter (QML)}{Plane Spotter} example demonstrates +this feature. + +\section1 QVariant Based integration + +This section provides information on how to integrate QGeoAddress and QGeoLocation. + +\section2 Address - QGeoAddress +The \l {QtPositioning::Address::address} {Address.address} property is used to provide an interface between C++ and QML code. First a pointer to a +Address object must be obtained from C++, then use the \l {QObject::property()}{property()} and +\l {QObject::setProperty()}{setProperty()} functions to get and set the \c address property. +The following gets the QGeoAddress representing this object from C++: +\snippet cpp/cppqml.cpp Address get +The following sets the properties of this object based on a QGeoAddress object from C++: +\snippet cpp/cppqml.cpp Address set + + +\section2 Location - QGeoLocation +The \l {Location::location} {Location.location} property is used to provide an interface between C++ and QML code. First a pointer to a +Location object must be obtained from C++, then use the \l {QObject::property()}{property()} and +\l {QObject::setProperty()}{setProperty()} functions to get and set the \c location property. +The following gets the QGeoLocation representing this object from C++: +\snippet cpp/cppqml.cpp Location get +The following sets the properties of this object based on a QGeoLocation object from C++: +\snippet cpp/cppqml.cpp Location set + +*/ diff --git a/src/positioning/doc/src/qml-position.qdoc b/src/positioning/doc/src/qml-position.qdoc new file mode 100644 index 0000000..dd3b5e4 --- /dev/null +++ b/src/positioning/doc/src/qml-position.qdoc @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page location-positioning-qml.html + +\title Positioning (QML) + +\brief The Location Positioning API enables location positioning by means of +GPS or an NMEA data source. + +\section1 Location Positioning + +Location data involves a precisely specified position on the Earth's +surface \unicode {0x2014} as provided by a latitude-longitude coordinate +\unicode {0x2014} along with associated data, such as: + + \list + \li The date and time at which the position was reported + \li The velocity of the device that reported the position + \li The altitude of the reported position (height above sea level) + \li The bearing of the device in degrees, relative to true north + \endlist + +For more information see +\l {http://en.wikipedia.org/wiki/Geographic_coordinate}{Geographic Coordinate}. + +This data can be extracted through a variety of methods. One of the most +well known methods of positioning is GPS (Global Positioning System), a +publicly available system that uses radiowave signals received from +Earth-orbiting satellites to calculate the precise position and time of +the receiver. Another popular method is 'Cell Identifier Positioning', which uses +the cell identifier of the cell site that is currently serving the receiving +device to calculate its approximate location. These and other positioning +methods can all be used with the Location API; the only requirement for a +location data source within the API is that it provides a +latitude-longitude coordinate with a date/time value, with the option of +providing the other attributes listed above. + +\section2 Coordinates + +The \l {coordinate} is a basic unit of geographical information. The +\l {coordinate} type has attributes to hold the \c {latitude}, +\c longitude and \c altitude. + +\section2 Positions + +The three dimensional position of an object such as a mobile device can be specified by giving +the latitude, longitude and altitude. That is the values held in the +l\ {coordinate} type. Additionally for computation of future +positions we would like to know if the object is moving, what \l [QML] {Position::}{speed} it is +doing and what is the \l {Position::timestamp}{timestamp} of the last position data. Position +therefore includes values for the \l {Position::coordinate}{coordinate}, +\l {Position::speed}{speed} and a \l {Position::timestamp}{timestamp}. \l Position also takes +responsibility for validation of sensible values for these properties. These are exposed as +the \l {Position::latitudeValid}{latitudeValid}, \l {Position::longitudeValid}{longitudeValid}, +\l {Position::altitudeValid}{altitudeValid}, \l {Position::speedValid}{speedValid}, +\l {Position::horizontalAccuracyValid}{horizontalAccuracyValid}, and +\l {Position::verticalAccuracyValid}{verticalAccuracyValid} properties. + + +\section2 PositionSource Type + +We have a Position type, a \l {coordinate} type but where does the data come from? +Also it is a good idea to be able to indicate alternative sources. +Perhaps instead of directly picking up GPS satellites it might be desirable to do +some testing using a datafile. + +The \l PositionSource type provides the developer with control, +within the limits allowed by the platform, of the source of the +geographical data. Apart from tradtional sources such as GPS and cell data the positional data can be +sourced from a logfile which is in NMEA format. + +\l {http://en.wikipedia.org/wiki/NMEA}{NMEA} is a common text-based +protocol for specifying navigational data. For convenience, the \l +{PositionSource::nmeaSource}{nmeaSource} property is provided to enable +QML applications to read NMEA data from a log file or a TCP socket, the +source will emit updates according to the time stamp of each NMEA sentence +to produce a "replay" of the recorded data. To use a TCP socket set the +"socket" uri scheme. + +\code +PositionSource { + nmeaSource: "socket://127.0.0.1:12345" +} +\endcode + + + +\section2 GeoFlickr Example + +The \l{GeoFlickr (QML)}{GeoFlickr Example} uses the Location to download thumbnail +images from Flickr relevant to the current location. + +*/ diff --git a/src/positioning/doc/src/qtpositioning-examples.qdoc b/src/positioning/doc/src/qtpositioning-examples.qdoc new file mode 100644 index 0000000..74992d9 --- /dev/null +++ b/src/positioning/doc/src/qtpositioning-examples.qdoc @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \group qtpositioning-examples + \title Qt Positioning Examples + \brief Examples for the Qt Positioning module + \ingroup all-examples + \ingroup qtpositioning + + These are the \l{Qt Positioning} examples. + +*/ diff --git a/src/positioning/doc/src/qtpositioning-plugins.qdoc b/src/positioning/doc/src/qtpositioning-plugins.qdoc new file mode 100644 index 0000000..37d25fc --- /dev/null +++ b/src/positioning/doc/src/qtpositioning-plugins.qdoc @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! +\page qtpositioning-plugins.html +\title Qt Positioning service plugins +\brief Implementing Qt Positioning plugins + +Qt Positioning provides the majority of its functionality through plugins. This +document outlines how to develop a new position plugin. + +\section1 Plugin Description + +Each plugin is described by a json file. The json describes the plugins capabilities and +version. Below is an example of a json file used by the postionpoll plugin: + +\quotefile ../../../plugins/position/positionpoll/plugin.json + +The entries have the following meaning: + +\table + \header + \li Key + \li Description + \row + \li Keys + \li The unique name/key of the plugin. Each position plugin must have a unique name. + \row + \li Provider + \li The provider name of the services. Multiple plugins may have the same name. + In such cases the Version string will be used to further distinguish the plugins. + \row + \li Position + \li Set to \c true if the plugin implements a \l QGeoPositionInfoSource. + \row + \li Satellite + \li Set to \c true if the plugin implements a \l QGeoSatelliteInfoSource. + \row + \li Monitor + \li Set to \c true if the plugin implements a \l QGeoAreaMonitorSource. + \row + \li Priority + \li The plugin priority. If multiple plugins have the same provider name, the plugin + with the higest priority will be used. +\endtable + +\section1 Implementing Plugins + +A plugin implementer needs to subclass \l QGeoPositionInfoSourceFactory and override one or more of +its functions. If a plugin does not support a specific feature the function should return 0 or +utilize the default implementation. + +\list + \li \l QGeoPositionInfoSourceFactory::areaMonitor() + \li \l QGeoPositionInfoSourceFactory::positionInfoSource() + \li \l QGeoPositionInfoSourceFactory::satelliteInfoSource() +\endlist +*/ diff --git a/src/positioning/doc/src/qtpositioning-qml.qdoc b/src/positioning/doc/src/qtpositioning-qml.qdoc new file mode 100644 index 0000000..7c9567c --- /dev/null +++ b/src/positioning/doc/src/qtpositioning-qml.qdoc @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \qmlmodule QtPositioning 5.11 + \title Qt Positioning QML Types + \ingroup qmlmodules + \brief Provides QML types for position information + +\section1 Overview + +The identifying string for this module is \e QtPositioning. To use include the following import +statement in the QML file. + +\snippet doc_src_qtpositioning.qml import + +\section2 Positioning QML Concepts + +Position information can come from a variety of sources including satellites, +wifi, text files and so on. The position is described by the latitude, +the longitude, and the altitude in meters. For more information see +\l {http://en.wikipedia.org/wiki/Geographic_coordinate}{Geographic Coordinate}. + +The QML position is stored in a \l {coordinate} which contains the +latitude, longitude and altitude of the device. The \l {QtPositioning::Location}{Location} contains +this \l {coordinate} and adds an address, it also has a bounding box which +defines the recommended viewing region when displaying the location. + +Now that the device has a position, with regular updates the API can determine +the speed and heading of the device. It can also define a box or a circle that can +produce a notification when the device either leaves or enters that region. + +More detailed information retrieving the current position can be found under +\l {Positioning (QML)}{Location Positioning via QML} + +\section1 Basic Types + +\annotatedlist qml-QtPositioning5-basictypes + +\section1 Alphabetical Listing of All QML Types +*/ diff --git a/src/positioning/doc/src/qtpositioning.qdoc b/src/positioning/doc/src/qtpositioning.qdoc new file mode 100644 index 0000000..f0094ff --- /dev/null +++ b/src/positioning/doc/src/qtpositioning.qdoc @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \module QtPositioning + \title Qt Positioning C++ Classes + \ingroup modules + \qtvariable positioning + + \brief The Positioning module provides positioning information via QML and C++ interfaces. + + To load the Qt Positioning module, add the following statement to your .qml files + + \snippet doc_src_qtpositioning.qml import + + For C++ projects include the header appropriate for the current use case, + for example applications using routes may use + + \code #include \endcode + + The .pro file should have the \e positioning keyword added + + \code QT += positioning \endcode + + + See more in the \l{Qt Positioning}{Qt Positioning Overview}. + +*/ + + + +/*! +\page qtpositioning-index.html +\title Qt Positioning +\brief The Qt Positioning API provides positioning information via QML and C++ interfaces. +\ingroup technology-apis + +The Qt Positioning API provides positioning information via QML and C++ interfaces. + +Currently the API is supported on \l {Qt for Android}{Android}, \l {Qt for iOS}{iOS}, +\l {Qt for macOS}{\macos}, +\l {Qt for Linux/X11}{Linux} (using +\l {http://www.freedesktop.org/wiki/Software/GeoClue}{GeoClue version 0.12.99}), +\l {Qt for Windows}{Windows} (with GPS receivers exposed as a serial port providing NMEA sentences), +and \l {Qt for WinRT}{WinRT} (using Windows.Devices.Geolocation). Note that the \l {Qt for WinRT}{WinRT} +implementation can also be used in Win32 Desktop uses cases if the underlying platform is Windows 10 +or later. + +\section1 Overview + +The Qt Positioning API gives developers the ability to determine a position by +using a variety of possible sources, including satellite, or wifi, or text file, +and so on. That information can then be used to for example determine a position +on a map. In addition satellite information can be retrieved and area based monitoring +can be performed. + +\section1 Getting Started + +To load the Qt Positioning module, add the following statement to your .qml files + +\snippet doc_src_qtpositioning.qml import + +For C++ projects include the header appropriate for the current use case, +for example applications using routes may use + +\code #include \endcode + +The .pro file should have the \e positioning keyword added + +\code QT += positioning \endcode + +\section1 Licenses + +Qt Positioning is available under commercial licenses from \l{The Qt Company}. +In addition, it is available under free software licenses. Since Qt 5.4, +these free software licenses are +\l{GNU Lesser General Public License, version 3}, or +the \l{GNU General Public License, version 2}. +See \l{Qt Licensing} for further details. + +\section1 Related Information +\section2 Overview + +Positioning includes all the functionality necessary to find and work with geographic +coordinates. It can use a variety of external sources of information, including GPS. This +provides us with a coordinate and altitude for the device with additional features +such as speed and direction. This provides the fundamental location information used in the API. + +\section2 References +\table +\row + \li Positioning introduction: + \li \l{Positioning (QML)}{for QML} + \li \l{Positioning (C++)}{for C++} +\row + \li API references: + \li \l {Qt Positioning QML Types}{for QML} + \li \l {Qt Positioning C++ Classes}{for C++} +\row + \li Position plugins: + \li \l {Qt Positioning service plugins} +\endtable + +\section2 Examples + +\list + \li \l {GeoFlickr (QML)} + \li \l {Log File Position Source (C++)} + \li \l {SatelliteInfo (C++/QML)} + \li \l {Weather Info (C++/QML)} +\endlist +*/ diff --git a/src/positioning/positioning.pro b/src/positioning/positioning.pro new file mode 100644 index 0000000..44de23e --- /dev/null +++ b/src/positioning/positioning.pro @@ -0,0 +1,98 @@ +TARGET = QtPositioning +QT = core-private +CONFIG += simd optimize_full + +#INCLUDEPATH += ../3rdparty/poly2tri +INCLUDEPATH += ../3rdparty/clipper +INCLUDEPATH += ../3rdparty/clip2tri + +QMAKE_DOCS = $$PWD/doc/qtpositioning.qdocconf +OTHER_FILES += configure.json doc/src/*.qdoc # show .qdoc files in Qt Creator + +ANDROID_BUNDLED_JAR_DEPENDENCIES = \ + jar/QtPositioning.jar:org.qtproject.qt5.android.positioning.QtPositioning +ANDROID_PERMISSIONS = \ + android.permission.ACCESS_FINE_LOCATION +ANDROID_LIB_DEPENDENCIES = \ + plugins/position/libqtposition_android.so +MODULE_WINRT_CAPABILITIES_DEVICE += \ + location +MODULE_PLUGIN_TYPES = \ + position + +PUBLIC_HEADERS += \ + qgeoaddress.h \ + qgeoareamonitorinfo.h \ + qgeoareamonitorsource.h \ + qgeoshape.h \ + qgeorectangle.h \ + qgeocircle.h \ + qgeocoordinate.h \ + qgeolocation.h \ + qgeopositioninfo.h \ + qgeopositioninfosource.h \ + qgeosatelliteinfo.h \ + qgeosatelliteinfosource.h \ + qnmeapositioninfosource.h \ + qgeopositioninfosourcefactory.h \ + qpositioningglobal.h \ + qgeopolygon.h \ + qgeopath.h \ + +PRIVATE_HEADERS += \ + qgeoaddress_p.h \ + qgeoshape_p.h \ + qgeorectangle_p.h \ + qgeocircle_p.h \ + qgeolocation_p.h \ + qlocationutils_p.h \ + qnmeapositioninfosource_p.h \ + qgeocoordinate_p.h \ + qgeopositioninfosource_p.h \ + qdeclarativegeoaddress_p.h \ + qdeclarativegeolocation_p.h \ + qdoublevector2d_p.h \ + qdoublevector3d_p.h \ + qwebmercator_p.h \ + qpositioningglobal_p.h \ + qlocationdata_simulator_p.h \ + qdoublematrix4x4_p.h \ + qgeopath_p.h \ + qgeocoordinateobject_p.h \ + qgeopositioninfo_p.h \ + qclipperutils_p.h + +SOURCES += \ + qgeoaddress.cpp \ + qgeoareamonitorsource.cpp \ + qgeoareamonitorinfo.cpp \ + qgeoshape.cpp \ + qgeorectangle.cpp \ + qgeocircle.cpp \ + qgeocoordinate.cpp \ + qgeolocation.cpp \ + qgeopositioninfo.cpp \ + qgeopositioninfosource.cpp \ + qgeosatelliteinfo.cpp \ + qgeosatelliteinfosource.cpp \ + qlocationutils.cpp \ + qnmeapositioninfosource.cpp \ + qgeopositioninfosourcefactory.cpp \ + qdeclarativegeoaddress.cpp \ + qdeclarativegeolocation.cpp \ + qdoublevector2d.cpp \ + qdoublevector3d.cpp \ + qgeopath.cpp \ + qgeopolygon.cpp \ + qlocationdata_simulator.cpp \ + qwebmercator.cpp \ + qdoublematrix4x4.cpp \ + qclipperutils.cpp \ + qgeocoordinateobject.cpp + +HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS + + +load(qt_module) + +LIBS_PRIVATE += -L$$MODULE_BASE_OUTDIR/lib -lclip2tri$$qtPlatformTargetSuffix() diff --git a/src/positioning/qclipperutils.cpp b/src/positioning/qclipperutils.cpp new file mode 100644 index 0000000..2f69d98 --- /dev/null +++ b/src/positioning/qclipperutils.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qclipperutils_p.h" + +QT_BEGIN_NAMESPACE + +static const double kClipperScaleFactor = 281474976710656.0; // 48 bits of precision +static const double kClipperScaleFactorInv = 1.0 / kClipperScaleFactor; + +double QClipperUtils::clipperScaleFactor() +{ + return kClipperScaleFactor; +} + +QDoubleVector2D QClipperUtils::toVector2D(const IntPoint &p) +{ + return QDoubleVector2D(double(p.X) * kClipperScaleFactorInv, double(p.Y) * kClipperScaleFactorInv); +} + +IntPoint QClipperUtils::toIntPoint(const QDoubleVector2D &p) +{ + return IntPoint(cInt(p.x() * kClipperScaleFactor), cInt(p.y() * kClipperScaleFactor)); +} + +QList QClipperUtils::pathToQList(const Path &path) +{ + QList res; + res.reserve(int(path.size())); + for (const IntPoint &ip: path) + res.append(toVector2D(ip)); + return res; +} + +QList > QClipperUtils::pathsToQList(const Paths &paths) +{ + QList > res; + res.reserve(int(paths.size())); + for (const Path &p: paths) { + res.append(pathToQList(p)); + } + return res; +} + +Path QClipperUtils::qListToPath(const QList &list) +{ + Path res; + res.reserve(list.size()); + for (const QDoubleVector2D &p: list) + res.push_back(toIntPoint(p)); + return res; +} + +Paths QClipperUtils::qListToPaths(const QList > &lists) +{ + Paths res; + res.reserve(lists.size()); + for (const QList &l: lists) { + res.push_back(qListToPath(l)); + } + return res; +} + +QT_END_NAMESPACE diff --git a/src/positioning/qclipperutils_p.h b/src/positioning/qclipperutils_p.h new file mode 100644 index 0000000..f05d983 --- /dev/null +++ b/src/positioning/qclipperutils_p.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QCLIPPERUTILS_P_H +#define QCLIPPERUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +/* + * This file is intended to be include only in source files of + * QtPositioning/QtLocation. It is in QtPositioning to enable manipulation + * of geo polygons + */ + +#include +#include +#include +#include +/* clip2tri triangulator includes */ +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_PRIVATE_EXPORT QClipperUtils +{ +public: + static double clipperScaleFactor(); + + static QDoubleVector2D toVector2D(const IntPoint &p); + static IntPoint toIntPoint(const QDoubleVector2D &p); + + static QList pathToQList(const Path &path); + static QList > pathsToQList(const Paths &paths); + + static Path qListToPath(const QList &list); + static Paths qListToPaths(const QList > &lists); +}; + +QT_END_NAMESPACE + +#endif // QCLIPPERUTILS_P_H diff --git a/src/positioning/qdeclarativegeoaddress.cpp b/src/positioning/qdeclarativegeoaddress.cpp new file mode 100644 index 0000000..470a4c1 --- /dev/null +++ b/src/positioning/qdeclarativegeoaddress.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +***************************************************************************/ + +#include "qdeclarativegeoaddress_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Address + \instantiates QDeclarativeGeoAddress + \inqmlmodule QtPositioning + \since 5.2 + + \brief The Address QML type represents a specific location as a street address. + + An Address is used as a unit of data for queries such as (Reverse) Geocoding + or Places searches -- many of these operations either accept an Address + or return one. + + Not all properties of an Address are necessarily available or relevant + in all parts of the world and all locales. The \l district, \l state and + \l county properties are particularly area-specific for many data sources, + and often only one or two of these are available or useful. + + The Address has a \l text property which holds a formatted string. It + is the recommended way to display an address to the user and typically + takes the format of an address as found on an envelope, but this is not always + the case. The \l text may be automatically generated from constituent + address properties such as \l street, \l city and and so on, but can also + be explicitly assigned. See \l text for details. + + \section2 Example Usage + + The following code snippet shows the declaration of an Address object. + + \code + Address { + id: address + street: "53 Brandl St" + city: "Eight Mile Plains" + country: "Australia" + countryCode: "AUS" + } + \endcode + + This could then be used, for example, as the value of a geocoding query, + to get an exact longitude and latitude for the address. + + \sa {QGeoAddress} +*/ + +QDeclarativeGeoAddress::QDeclarativeGeoAddress(QObject *parent) : + QObject(parent) +{ +} + +QDeclarativeGeoAddress::QDeclarativeGeoAddress(const QGeoAddress &address, QObject *parent) : + QObject(parent), m_address(address) +{ +} + +/*! + \qmlproperty QGeoAddress QtPositioning::Address::address + + For details on how to use this property to interface between C++ and QML see + "\l {Address - QGeoAddress} {Interfaces between C++ and QML Code}". +*/ +QGeoAddress QDeclarativeGeoAddress::address() const +{ + return m_address; +} + +void QDeclarativeGeoAddress::setAddress(const QGeoAddress &address) +{ + // Elaborate but takes care of emiting needed signals + setText(address.text()); + setCountry(address.country()); + setCountryCode(address.countryCode()); + setState(address.state()); + setCounty(address.county()); + setCity(address.city()); + setDistrict(address.district()); + setStreet(address.street()); + setPostalCode(address.postalCode()); + m_address = address; +} + +/*! + \qmlproperty string QtPositioning::Address::text + + This property holds the address as a single formatted string. It is the recommended + string to use to display the address to the user. It typically takes the format of + an address as found on an envelope, but this is not always necessarily the case. + + The address \c text is either automatically generated or explicitly assigned, + this can be determined by checking \l isTextGenerated. + + If an empty string is assigned to \c text, then \l isTextGenerated will be set + to true and \c text will return a string which is locally formatted according to + \l countryCode and based on the properties of the address. Modifying the address + properties such as \l street, \l city and so on may cause the contents of \c text to + change. + + If a non-empty string is assigned to \c text, then \l isTextGenerated will be + set to false and \c text will always return the explicitly assigned string. + Modifying address properties will not affect the \c text property. +*/ +QString QDeclarativeGeoAddress::text() const +{ + return m_address.text(); +} + +void QDeclarativeGeoAddress::setText(const QString &address) +{ + QString oldText = m_address.text(); + bool oldIsTextGenerated = m_address.isTextGenerated(); + m_address.setText(address); + + if (oldText != m_address.text()) + emit textChanged(); + if (oldIsTextGenerated != m_address.isTextGenerated()) + emit isTextGeneratedChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::country + + This property holds the country of the address as a single formatted string. +*/ +QString QDeclarativeGeoAddress::country() const +{ + return m_address.country(); +} + +void QDeclarativeGeoAddress::setCountry(const QString &country) +{ + if (m_address.country() == country) + return; + QString oldText = m_address.text(); + m_address.setCountry(country); + emit countryChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::countryCode + + This property holds the country code of the address as a single formatted string. +*/ +QString QDeclarativeGeoAddress::countryCode() const +{ + return m_address.countryCode(); +} + +void QDeclarativeGeoAddress::setCountryCode(const QString &countryCode) +{ + if (m_address.countryCode() == countryCode) + return; + QString oldText = m_address.text(); + m_address.setCountryCode(countryCode); + emit countryCodeChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::state + + This property holds the state of the address as a single formatted string. +*/ +QString QDeclarativeGeoAddress::state() const +{ + return m_address.state(); +} + +void QDeclarativeGeoAddress::setState(const QString &state) +{ + if (m_address.state() == state) + return; + QString oldText = m_address.text(); + m_address.setState(state); + emit stateChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::county + + This property holds the county of the address as a single formatted string. +*/ +QString QDeclarativeGeoAddress::county() const +{ + return m_address.county(); +} + +void QDeclarativeGeoAddress::setCounty(const QString &county) +{ + if (m_address.county() == county) + return; + QString oldText = m_address.text(); + m_address.setCounty(county); + emit countyChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::city + + This property holds the city of the address as a single formatted string. +*/ +QString QDeclarativeGeoAddress::city() const +{ + return m_address.city(); +} + +void QDeclarativeGeoAddress::setCity(const QString &city) +{ + if (m_address.city() == city) + return; + QString oldText = m_address.text(); + m_address.setCity(city); + emit cityChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::district + + This property holds the district of the address as a single formatted string. +*/ +QString QDeclarativeGeoAddress::district() const +{ + return m_address.district(); +} + +void QDeclarativeGeoAddress::setDistrict(const QString &district) +{ + if (m_address.district() == district) + return; + QString oldText = m_address.text(); + m_address.setDistrict(district); + emit districtChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::street + + This property holds the street of the address but + may also contain things like a unit number, a building + name, or anything else that might be used to + distinguish one address from another. +*/ +QString QDeclarativeGeoAddress::street() const +{ + return m_address.street(); +} + +void QDeclarativeGeoAddress::setStreet(const QString &street) +{ + if (m_address.street() == street) + return; + QString oldText = m_address.text(); + m_address.setStreet(street); + emit streetChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty string QtPositioning::Address::postalCode + + This property holds the postal code of the address as a single formatted string. +*/ +QString QDeclarativeGeoAddress::postalCode() const +{ + return m_address.postalCode(); +} + +void QDeclarativeGeoAddress::setPostalCode(const QString &postalCode) +{ + if (m_address.postalCode() == postalCode) + return; + QString oldText = m_address.text(); + m_address.setPostalCode(postalCode); + emit postalCodeChanged(); + + if (m_address.isTextGenerated() && oldText != m_address.text()) + emit textChanged(); +} + +/*! + \qmlproperty bool QtPositioning::Address::isTextGenerated + + This property holds a boolean that if true, indicates that \l text is automatically + generated from address properties. If false, it indicates that the \l text has been + explicitly assigned. + +*/ +bool QDeclarativeGeoAddress::isTextGenerated() const +{ + return m_address.isTextGenerated(); +} + +QT_END_NAMESPACE diff --git a/src/positioning/qdeclarativegeoaddress_p.h b/src/positioning/qdeclarativegeoaddress_p.h new file mode 100644 index 0000000..3285cea --- /dev/null +++ b/src/positioning/qdeclarativegeoaddress_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +***************************************************************************/ + +#ifndef QDECLARATIVEGEOADDRESS_P_H +#define QDECLARATIVEGEOADDRESS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_EXPORT QDeclarativeGeoAddress : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QGeoAddress address READ address WRITE setAddress) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QString country READ country WRITE setCountry NOTIFY countryChanged) + Q_PROPERTY(QString countryCode READ countryCode WRITE setCountryCode NOTIFY countryCodeChanged) + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) + Q_PROPERTY(QString county READ county WRITE setCounty NOTIFY countyChanged) + Q_PROPERTY(QString city READ city WRITE setCity NOTIFY cityChanged) + Q_PROPERTY(QString district READ district WRITE setDistrict NOTIFY districtChanged) + Q_PROPERTY(QString street READ street WRITE setStreet NOTIFY streetChanged) + Q_PROPERTY(QString postalCode READ postalCode WRITE setPostalCode NOTIFY postalCodeChanged) + Q_PROPERTY(bool isTextGenerated READ isTextGenerated NOTIFY isTextGeneratedChanged) + +public: + explicit QDeclarativeGeoAddress(QObject *parent = 0); + QDeclarativeGeoAddress(const QGeoAddress &address, QObject *parent = 0); + QGeoAddress address() const; + void setAddress(const QGeoAddress &address); + + QString text() const; + void setText(const QString &address); + + QString country() const; + void setCountry(const QString &country); + QString countryCode() const; + void setCountryCode(const QString &countryCode); + QString state() const; + void setState(const QString &state); + QString county() const; + void setCounty(const QString &county); + QString city() const; + void setCity(const QString &city); + QString district() const; + void setDistrict(const QString &district); + QString street() const; + void setStreet(const QString &street); + QString postalCode() const; + void setPostalCode(const QString &postalCode); + bool isTextGenerated() const; + +Q_SIGNALS: + void textChanged(); + void countryChanged(); + void countryCodeChanged(); + void stateChanged(); + void countyChanged(); + void cityChanged(); + void districtChanged(); + void streetChanged(); + void postalCodeChanged(); + void isTextGeneratedChanged(); + +private: + QGeoAddress m_address; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVEGEOADDRESS_P_H diff --git a/src/positioning/qdeclarativegeolocation.cpp b/src/positioning/qdeclarativegeolocation.cpp new file mode 100644 index 0000000..9e3b71e --- /dev/null +++ b/src/positioning/qdeclarativegeolocation.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativegeolocation_p.h" + +QT_USE_NAMESPACE + +/*! + \qmltype Location + \instantiates QDeclarativeGeoLocation + \inqmlmodule QtPositioning + \since 5.2 + + \brief The Location type holds location data. + + Location types represent a geographic "location", in a human sense. This + consists of a specific \l {coordinate}, an \l {address} and a \l {boundingBox}{bounding box}. + The \l {boundingBox}{bounding box} represents the recommended region + to display when viewing this location. + + The Location type is most commonly seen as the contents of a search + model such as the GeocodeModel. When a GeocodeModel returns the list of + locations found for a given query, it represents these as Location objects. + + \section2 Example Usage + + The following example shows a simple Location object being declared: + + \code + Location { + coordinate { + latitude: -27.3 + longitude: 153.1 + } + address: Address { + ... + } + } + \endcode +*/ + +QDeclarativeGeoLocation::QDeclarativeGeoLocation(QObject *parent) +: QObject(parent), m_address(0) + +{ + setLocation(QGeoLocation()); +} + +QDeclarativeGeoLocation::QDeclarativeGeoLocation(const QGeoLocation &src, QObject *parent) +: QObject(parent), m_address(0) +{ + setLocation(src); +} + +QDeclarativeGeoLocation::~QDeclarativeGeoLocation() +{ +} + +/*! + \qmlproperty QGeoLocation QtPositioning::Location::location + + For details on how to use this property to interface between C++ and QML see + "\l {Location - QGeoLocation} {Interfaces between C++ and QML Code}". +*/ +void QDeclarativeGeoLocation::setLocation(const QGeoLocation &src) +{ + if (m_address && m_address->parent() == this) { + m_address->setAddress(src.address()); + } else if (!m_address || m_address->parent() != this) { + m_address = new QDeclarativeGeoAddress(src.address(), this); + emit addressChanged(); + } + + setCoordinate(src.coordinate()); + setBoundingBox(src.boundingBox()); +} + +QGeoLocation QDeclarativeGeoLocation::location() const +{ + QGeoLocation retValue; + retValue.setAddress(m_address ? m_address->address() : QGeoAddress()); + retValue.setCoordinate(m_coordinate); + retValue.setBoundingBox(m_boundingBox); + return retValue; +} + +/*! + \qmlproperty Address QtPositioning::Location::address + + This property holds the address of the location which can be use to retrieve address details of the location. +*/ +void QDeclarativeGeoLocation::setAddress(QDeclarativeGeoAddress *address) +{ + if (m_address == address) + return; + + if (m_address && m_address->parent() == this) + delete m_address; + + m_address = address; + emit addressChanged(); +} + +QDeclarativeGeoAddress *QDeclarativeGeoLocation::address() const +{ + return m_address; +} + +/*! + \qmlproperty coordinate QtPositioning::Location::coordinate + + This property holds the exact geographical coordinate of the location which can be used to retrieve the latitude, longitude and altitude of the location. + + \note this property's changed() signal is currently emitted only if the + whole object changes, not if only the contents of the object change. +*/ +void QDeclarativeGeoLocation::setCoordinate(const QGeoCoordinate coordinate) +{ + if (m_coordinate == coordinate) + return; + + m_coordinate = coordinate; + emit coordinateChanged(); +} + +QGeoCoordinate QDeclarativeGeoLocation::coordinate() const +{ + return m_coordinate; +} + +/*! + \qmlproperty georectangle QtPositioning::Location::boundingBox + + This property holds the recommended region to use when displaying the location. + For example, a building's location may have a region centered around the building, + but the region is large enough to show it's immediate surrounding geographical + context. + + Note: this property's changed() signal is currently emitted only if the + whole object changes, not if only the contents of the object change. +*/ +void QDeclarativeGeoLocation::setBoundingBox(const QGeoRectangle &boundingBox) +{ + if (m_boundingBox == boundingBox) + return; + + m_boundingBox = boundingBox; + emit boundingBoxChanged(); +} + +QGeoRectangle QDeclarativeGeoLocation::boundingBox() const +{ + return m_boundingBox; +} diff --git a/src/positioning/qdeclarativegeolocation_p.h b/src/positioning/qdeclarativegeolocation_p.h new file mode 100644 index 0000000..a02d7b4 --- /dev/null +++ b/src/positioning/qdeclarativegeolocation_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEGEOLOCATION_P_H +#define QDECLARATIVEGEOLOCATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_EXPORT QDeclarativeGeoLocation : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QGeoLocation location READ location WRITE setLocation) + Q_PROPERTY(QDeclarativeGeoAddress *address READ address WRITE setAddress NOTIFY addressChanged) + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) + Q_PROPERTY(QGeoRectangle boundingBox READ boundingBox WRITE setBoundingBox NOTIFY boundingBoxChanged) + +public: + explicit QDeclarativeGeoLocation(QObject *parent = 0); + explicit QDeclarativeGeoLocation(const QGeoLocation &src, QObject *parent = 0); + ~QDeclarativeGeoLocation(); + + QGeoLocation location() const; + void setLocation(const QGeoLocation &src); + + QDeclarativeGeoAddress *address() const; + void setAddress(QDeclarativeGeoAddress *address); + QGeoCoordinate coordinate() const; + void setCoordinate(const QGeoCoordinate coordinate); + + QGeoRectangle boundingBox() const; + void setBoundingBox(const QGeoRectangle &boundingBox); + +Q_SIGNALS: + void addressChanged(); + void coordinateChanged(); + void boundingBoxChanged(); + +private: + QDeclarativeGeoAddress *m_address; + QGeoRectangle m_boundingBox; + QGeoCoordinate m_coordinate; +}; + +QT_END_NAMESPACE + +#endif // QDECLARATIVELOCATION_P_H diff --git a/src/positioning/qdoublematrix4x4.cpp b/src/positioning/qdoublematrix4x4.cpp new file mode 100644 index 0000000..32cc7f1 --- /dev/null +++ b/src/positioning/qdoublematrix4x4.cpp @@ -0,0 +1,1112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdoublematrix4x4_p.h" +#include +//#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const double inv_dist_to_plane = 1.0 / 1024.0; + +QDoubleMatrix4x4::QDoubleMatrix4x4(const double *values) +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + m[col][row] = values[row * 4 + col]; + flagBits = General; +} + +QDoubleMatrix4x4::QDoubleMatrix4x4(const double *values, int cols, int rows) +{ + for (int col = 0; col < 4; ++col) { + for (int row = 0; row < 4; ++row) { + if (col < cols && row < rows) + m[col][row] = values[col * rows + row]; + else if (col == row) + m[col][row] = 1.0; + else + m[col][row] = 0.0; + } + } + flagBits = General; +} + +static inline double matrixDet2(const double m[4][4], int col0, int col1, int row0, int row1) +{ + return m[col0][row0] * m[col1][row1] - m[col0][row1] * m[col1][row0]; +} + +static inline double matrixDet3 + (const double m[4][4], int col0, int col1, int col2, + int row0, int row1, int row2) +{ + return m[col0][row0] * matrixDet2(m, col1, col2, row1, row2) + - m[col1][row0] * matrixDet2(m, col0, col2, row1, row2) + + m[col2][row0] * matrixDet2(m, col0, col1, row1, row2); +} + +static inline double matrixDet4(const double m[4][4]) +{ + double det; + det = m[0][0] * matrixDet3(m, 1, 2, 3, 1, 2, 3); + det -= m[1][0] * matrixDet3(m, 0, 2, 3, 1, 2, 3); + det += m[2][0] * matrixDet3(m, 0, 1, 3, 1, 2, 3); + det -= m[3][0] * matrixDet3(m, 0, 1, 2, 1, 2, 3); + return det; +} + +double QDoubleMatrix4x4::determinant() const +{ + if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) + return 1.0; + + if (flagBits < Rotation2D) + return m[0][0] * m[1][1] * m[2][2]; // Translation | Scale + if (flagBits < Perspective) + return matrixDet3(m, 0, 1, 2, 0, 1, 2); + return matrixDet4(m); +} + +QDoubleMatrix4x4 QDoubleMatrix4x4::inverted(bool *invertible) const +{ + // Handle some of the easy cases first. + if (flagBits == Identity) { + if (invertible) + *invertible = true; + return QDoubleMatrix4x4(); + } else if (flagBits == Translation) { + QDoubleMatrix4x4 inv; + inv.m[3][0] = -m[3][0]; + inv.m[3][1] = -m[3][1]; + inv.m[3][2] = -m[3][2]; + inv.flagBits = Translation; + if (invertible) + *invertible = true; + return inv; + } else if (flagBits < Rotation2D) { + // Translation | Scale + if (m[0][0] == 0 || m[1][1] == 0 || m[2][2] == 0) { + if (invertible) + *invertible = false; + return QDoubleMatrix4x4(); + } + QDoubleMatrix4x4 inv; + inv.m[0][0] = 1.0 / m[0][0]; + inv.m[1][1] = 1.0 / m[1][1]; + inv.m[2][2] = 1.0 / m[2][2]; + inv.m[3][0] = -m[3][0] * inv.m[0][0]; + inv.m[3][1] = -m[3][1] * inv.m[1][1]; + inv.m[3][2] = -m[3][2] * inv.m[2][2]; + inv.flagBits = flagBits; + + if (invertible) + *invertible = true; + return inv; + } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) { + if (invertible) + *invertible = true; + return orthonormalInverse(); + } else if (flagBits < Perspective) { + QDoubleMatrix4x4 inv(1); // The "1" says to not load the identity. + + double det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + if (det == 0.0) { + if (invertible) + *invertible = false; + return QDoubleMatrix4x4(); + } + det = 1.0 / det; + + inv.m[0][0] = matrixDet2(m, 1, 2, 1, 2) * det; + inv.m[0][1] = -matrixDet2(m, 0, 2, 1, 2) * det; + inv.m[0][2] = matrixDet2(m, 0, 1, 1, 2) * det; + inv.m[0][3] = 0; + inv.m[1][0] = -matrixDet2(m, 1, 2, 0, 2) * det; + inv.m[1][1] = matrixDet2(m, 0, 2, 0, 2) * det; + inv.m[1][2] = -matrixDet2(m, 0, 1, 0, 2) * det; + inv.m[1][3] = 0; + inv.m[2][0] = matrixDet2(m, 1, 2, 0, 1) * det; + inv.m[2][1] = -matrixDet2(m, 0, 2, 0, 1) * det; + inv.m[2][2] = matrixDet2(m, 0, 1, 0, 1) * det; + inv.m[2][3] = 0; + inv.m[3][0] = -inv.m[0][0] * m[3][0] - inv.m[1][0] * m[3][1] - inv.m[2][0] * m[3][2]; + inv.m[3][1] = -inv.m[0][1] * m[3][0] - inv.m[1][1] * m[3][1] - inv.m[2][1] * m[3][2]; + inv.m[3][2] = -inv.m[0][2] * m[3][0] - inv.m[1][2] * m[3][1] - inv.m[2][2] * m[3][2]; + inv.m[3][3] = 1; + inv.flagBits = flagBits; + + if (invertible) + *invertible = true; + return inv; + } + + QDoubleMatrix4x4 inv(1); // The "1" says to not load the identity. + + double det = matrixDet4(m); + if (det == 0.0) { + if (invertible) + *invertible = false; + return QDoubleMatrix4x4(); + } + det = 1.0 / det; + + inv.m[0][0] = matrixDet3(m, 1, 2, 3, 1, 2, 3) * det; + inv.m[0][1] = -matrixDet3(m, 0, 2, 3, 1, 2, 3) * det; + inv.m[0][2] = matrixDet3(m, 0, 1, 3, 1, 2, 3) * det; + inv.m[0][3] = -matrixDet3(m, 0, 1, 2, 1, 2, 3) * det; + inv.m[1][0] = -matrixDet3(m, 1, 2, 3, 0, 2, 3) * det; + inv.m[1][1] = matrixDet3(m, 0, 2, 3, 0, 2, 3) * det; + inv.m[1][2] = -matrixDet3(m, 0, 1, 3, 0, 2, 3) * det; + inv.m[1][3] = matrixDet3(m, 0, 1, 2, 0, 2, 3) * det; + inv.m[2][0] = matrixDet3(m, 1, 2, 3, 0, 1, 3) * det; + inv.m[2][1] = -matrixDet3(m, 0, 2, 3, 0, 1, 3) * det; + inv.m[2][2] = matrixDet3(m, 0, 1, 3, 0, 1, 3) * det; + inv.m[2][3] = -matrixDet3(m, 0, 1, 2, 0, 1, 3) * det; + inv.m[3][0] = -matrixDet3(m, 1, 2, 3, 0, 1, 2) * det; + inv.m[3][1] = matrixDet3(m, 0, 2, 3, 0, 1, 2) * det; + inv.m[3][2] = -matrixDet3(m, 0, 1, 3, 0, 1, 2) * det; + inv.m[3][3] = matrixDet3(m, 0, 1, 2, 0, 1, 2) * det; + inv.flagBits = flagBits; + + if (invertible) + *invertible = true; + return inv; +} + +QDoubleMatrix4x4 QDoubleMatrix4x4::transposed() const +{ + QDoubleMatrix4x4 result(1); // The "1" says to not load the identity. + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + result.m[col][row] = m[row][col]; + } + } + // When a translation is transposed, it becomes a perspective transformation. + result.flagBits = (flagBits & Translation ? General : flagBits); + return result; +} + +QDoubleMatrix4x4& QDoubleMatrix4x4::operator/=(double divisor) +{ + m[0][0] /= divisor; + m[0][1] /= divisor; + m[0][2] /= divisor; + m[0][3] /= divisor; + m[1][0] /= divisor; + m[1][1] /= divisor; + m[1][2] /= divisor; + m[1][3] /= divisor; + m[2][0] /= divisor; + m[2][1] /= divisor; + m[2][2] /= divisor; + m[2][3] /= divisor; + m[3][0] /= divisor; + m[3][1] /= divisor; + m[3][2] /= divisor; + m[3][3] /= divisor; + flagBits = General; + return *this; +} + +QDoubleMatrix4x4 operator/(const QDoubleMatrix4x4& matrix, double divisor) +{ + QDoubleMatrix4x4 m(1); // The "1" says to not load the identity. + m.m[0][0] = matrix.m[0][0] / divisor; + m.m[0][1] = matrix.m[0][1] / divisor; + m.m[0][2] = matrix.m[0][2] / divisor; + m.m[0][3] = matrix.m[0][3] / divisor; + m.m[1][0] = matrix.m[1][0] / divisor; + m.m[1][1] = matrix.m[1][1] / divisor; + m.m[1][2] = matrix.m[1][2] / divisor; + m.m[1][3] = matrix.m[1][3] / divisor; + m.m[2][0] = matrix.m[2][0] / divisor; + m.m[2][1] = matrix.m[2][1] / divisor; + m.m[2][2] = matrix.m[2][2] / divisor; + m.m[2][3] = matrix.m[2][3] / divisor; + m.m[3][0] = matrix.m[3][0] / divisor; + m.m[3][1] = matrix.m[3][1] / divisor; + m.m[3][2] = matrix.m[3][2] / divisor; + m.m[3][3] = matrix.m[3][3] / divisor; + m.flagBits = QDoubleMatrix4x4::General; + return m; +} + +void QDoubleMatrix4x4::scale(const QDoubleVector3D& vector) +{ + double vx = vector.x(); + double vy = vector.y(); + double vz = vector.z(); + if (flagBits < Scale) { + m[0][0] = vx; + m[1][1] = vy; + m[2][2] = vz; + } else if (flagBits < Rotation2D) { + m[0][0] *= vx; + m[1][1] *= vy; + m[2][2] *= vz; + } else if (flagBits < Rotation) { + m[0][0] *= vx; + m[0][1] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[2][2] *= vz; + } else { + m[0][0] *= vx; + m[0][1] *= vx; + m[0][2] *= vx; + m[0][3] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[1][2] *= vy; + m[1][3] *= vy; + m[2][0] *= vz; + m[2][1] *= vz; + m[2][2] *= vz; + m[2][3] *= vz; + } + flagBits |= Scale; +} + +void QDoubleMatrix4x4::scale(double x, double y) +{ + if (flagBits < Scale) { + m[0][0] = x; + m[1][1] = y; + } else if (flagBits < Rotation2D) { + m[0][0] *= x; + m[1][1] *= y; + } else if (flagBits < Rotation) { + m[0][0] *= x; + m[0][1] *= x; + m[1][0] *= y; + m[1][1] *= y; + } else { + m[0][0] *= x; + m[0][1] *= x; + m[0][2] *= x; + m[0][3] *= x; + m[1][0] *= y; + m[1][1] *= y; + m[1][2] *= y; + m[1][3] *= y; + } + flagBits |= Scale; +} + +void QDoubleMatrix4x4::scale(double x, double y, double z) +{ + if (flagBits < Scale) { + m[0][0] = x; + m[1][1] = y; + m[2][2] = z; + } else if (flagBits < Rotation2D) { + m[0][0] *= x; + m[1][1] *= y; + m[2][2] *= z; + } else if (flagBits < Rotation) { + m[0][0] *= x; + m[0][1] *= x; + m[1][0] *= y; + m[1][1] *= y; + m[2][2] *= z; + } else { + m[0][0] *= x; + m[0][1] *= x; + m[0][2] *= x; + m[0][3] *= x; + m[1][0] *= y; + m[1][1] *= y; + m[1][2] *= y; + m[1][3] *= y; + m[2][0] *= z; + m[2][1] *= z; + m[2][2] *= z; + m[2][3] *= z; + } + flagBits |= Scale; +} + +void QDoubleMatrix4x4::scale(double factor) +{ + if (flagBits < Scale) { + m[0][0] = factor; + m[1][1] = factor; + m[2][2] = factor; + } else if (flagBits < Rotation2D) { + m[0][0] *= factor; + m[1][1] *= factor; + m[2][2] *= factor; + } else if (flagBits < Rotation) { + m[0][0] *= factor; + m[0][1] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[2][2] *= factor; + } else { + m[0][0] *= factor; + m[0][1] *= factor; + m[0][2] *= factor; + m[0][3] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[1][2] *= factor; + m[1][3] *= factor; + m[2][0] *= factor; + m[2][1] *= factor; + m[2][2] *= factor; + m[2][3] *= factor; + } + flagBits |= Scale; +} + +void QDoubleMatrix4x4::translate(const QDoubleVector3D& vector) +{ + double vx = vector.x(); + double vy = vector.y(); + double vz = vector.z(); + if (flagBits == Identity) { + m[3][0] = vx; + m[3][1] = vy; + m[3][2] = vz; + } else if (flagBits == Translation) { + m[3][0] += vx; + m[3][1] += vy; + m[3][2] += vz; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * vx; + m[3][1] = m[1][1] * vy; + m[3][2] = m[2][2] * vz; + } else if (flagBits == (Translation | Scale)) { + m[3][0] += m[0][0] * vx; + m[3][1] += m[1][1] * vy; + m[3][2] += m[2][2] * vz; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * vx + m[1][0] * vy; + m[3][1] += m[0][1] * vx + m[1][1] * vy; + m[3][2] += m[2][2] * vz; + } else { + m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz; + m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz; + m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz; + m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz; + } + flagBits |= Translation; +} + +void QDoubleMatrix4x4::translate(double x, double y) +{ + if (flagBits == Identity) { + m[3][0] = x; + m[3][1] = y; + } else if (flagBits == Translation) { + m[3][0] += x; + m[3][1] += y; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * x; + m[3][1] = m[1][1] * y; + } else if (flagBits == (Translation | Scale)) { + m[3][0] += m[0][0] * x; + m[3][1] += m[1][1] * y; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * x + m[1][0] * y; + m[3][1] += m[0][1] * x + m[1][1] * y; + } else { + m[3][0] += m[0][0] * x + m[1][0] * y; + m[3][1] += m[0][1] * x + m[1][1] * y; + m[3][2] += m[0][2] * x + m[1][2] * y; + m[3][3] += m[0][3] * x + m[1][3] * y; + } + flagBits |= Translation; +} + +void QDoubleMatrix4x4::translate(double x, double y, double z) +{ + if (flagBits == Identity) { + m[3][0] = x; + m[3][1] = y; + m[3][2] = z; + } else if (flagBits == Translation) { + m[3][0] += x; + m[3][1] += y; + m[3][2] += z; + } else if (flagBits == Scale) { + m[3][0] = m[0][0] * x; + m[3][1] = m[1][1] * y; + m[3][2] = m[2][2] * z; + } else if (flagBits == (Translation | Scale)) { + m[3][0] += m[0][0] * x; + m[3][1] += m[1][1] * y; + m[3][2] += m[2][2] * z; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * x + m[1][0] * y; + m[3][1] += m[0][1] * x + m[1][1] * y; + m[3][2] += m[2][2] * z; + } else { + m[3][0] += m[0][0] * x + m[1][0] * y + m[2][0] * z; + m[3][1] += m[0][1] * x + m[1][1] * y + m[2][1] * z; + m[3][2] += m[0][2] * x + m[1][2] * y + m[2][2] * z; + m[3][3] += m[0][3] * x + m[1][3] * y + m[2][3] * z; + } + flagBits |= Translation; +} + +void QDoubleMatrix4x4::rotate(double angle, const QDoubleVector3D& vector) +{ + rotate(angle, vector.x(), vector.y(), vector.z()); +} + +void QDoubleMatrix4x4::rotate(double angle, double x, double y, double z) +{ + if (angle == 0.0) + return; + double c, s; + if (angle == 90.0 || angle == -270.0) { + s = 1.0; + c = 0.0; + } else if (angle == -90.0 || angle == 270.0) { + s = -1.0; + c = 0.0; + } else if (angle == 180.0 || angle == -180.0) { + s = 0.0; + c = -1.0; + } else { + double a = qDegreesToRadians(angle); + c = std::cos(a); + s = std::sin(a); + } + if (x == 0.0) { + if (y == 0.0) { + if (z != 0.0) { + // Rotate around the Z axis. + if (z < 0) + s = -s; + double tmp; + m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s; + m[1][0] = m[1][0] * c - tmp * s; + m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s; + m[1][1] = m[1][1] * c - tmp * s; + m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s; + m[1][2] = m[1][2] * c - tmp * s; + m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s; + m[1][3] = m[1][3] * c - tmp * s; + + flagBits |= Rotation2D; + return; + } + } else if (z == 0.0) { + // Rotate around the Y axis. + if (y < 0) + s = -s; + double tmp; + m[2][0] = (tmp = m[2][0]) * c + m[0][0] * s; + m[0][0] = m[0][0] * c - tmp * s; + m[2][1] = (tmp = m[2][1]) * c + m[0][1] * s; + m[0][1] = m[0][1] * c - tmp * s; + m[2][2] = (tmp = m[2][2]) * c + m[0][2] * s; + m[0][2] = m[0][2] * c - tmp * s; + m[2][3] = (tmp = m[2][3]) * c + m[0][3] * s; + m[0][3] = m[0][3] * c - tmp * s; + + flagBits |= Rotation; + return; + } + } else if (y == 0.0 && z == 0.0) { + // Rotate around the X axis. + if (x < 0) + s = -s; + double tmp; + m[1][0] = (tmp = m[1][0]) * c + m[2][0] * s; + m[2][0] = m[2][0] * c - tmp * s; + m[1][1] = (tmp = m[1][1]) * c + m[2][1] * s; + m[2][1] = m[2][1] * c - tmp * s; + m[1][2] = (tmp = m[1][2]) * c + m[2][2] * s; + m[2][2] = m[2][2] * c - tmp * s; + m[1][3] = (tmp = m[1][3]) * c + m[2][3] * s; + m[2][3] = m[2][3] * c - tmp * s; + + flagBits |= Rotation; + return; + } + + double len = double(x) * double(x) + + double(y) * double(y) + + double(z) * double(z); + if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) { + len = std::sqrt(len); + x = double(double(x) / len); + y = double(double(y) / len); + z = double(double(z) / len); + } + double ic = 1.0 - c; + QDoubleMatrix4x4 rot(1); // The "1" says to not load the identity. + rot.m[0][0] = x * x * ic + c; + rot.m[1][0] = x * y * ic - z * s; + rot.m[2][0] = x * z * ic + y * s; + rot.m[3][0] = 0.0; + rot.m[0][1] = y * x * ic + z * s; + rot.m[1][1] = y * y * ic + c; + rot.m[2][1] = y * z * ic - x * s; + rot.m[3][1] = 0.0; + rot.m[0][2] = x * z * ic - y * s; + rot.m[1][2] = y * z * ic + x * s; + rot.m[2][2] = z * z * ic + c; + rot.m[3][2] = 0.0; + rot.m[0][3] = 0.0; + rot.m[1][3] = 0.0; + rot.m[2][3] = 0.0; + rot.m[3][3] = 1.0; + rot.flagBits = Rotation; + *this *= rot; +} + +void QDoubleMatrix4x4::projectedRotate(double angle, double x, double y, double z) +{ + // Used by QGraphicsRotation::applyTo() to perform a rotation + // and projection back to 2D in a single step. + if (angle == 0.0) + return; + double c, s; + if (angle == 90.0 || angle == -270.0) { + s = 1.0; + c = 0.0; + } else if (angle == -90.0 || angle == 270.0) { + s = -1.0; + c = 0.0; + } else if (angle == 180.0 || angle == -180.0) { + s = 0.0; + c = -1.0; + } else { + double a = qDegreesToRadians(angle); + c = std::cos(a); + s = std::sin(a); + } + if (x == 0.0) { + if (y == 0.0) { + if (z != 0.0) { + // Rotate around the Z axis. + if (z < 0) + s = -s; + double tmp; + m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s; + m[1][0] = m[1][0] * c - tmp * s; + m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s; + m[1][1] = m[1][1] * c - tmp * s; + m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s; + m[1][2] = m[1][2] * c - tmp * s; + m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s; + m[1][3] = m[1][3] * c - tmp * s; + + flagBits |= Rotation2D; + return; + } + } else if (z == 0.0) { + // Rotate around the Y axis. + if (y < 0) + s = -s; + m[0][0] = m[0][0] * c + m[3][0] * s * inv_dist_to_plane; + m[0][1] = m[0][1] * c + m[3][1] * s * inv_dist_to_plane; + m[0][2] = m[0][2] * c + m[3][2] * s * inv_dist_to_plane; + m[0][3] = m[0][3] * c + m[3][3] * s * inv_dist_to_plane; + flagBits = General; + return; + } + } else if (y == 0.0 && z == 0.0) { + // Rotate around the X axis. + if (x < 0) + s = -s; + m[1][0] = m[1][0] * c - m[3][0] * s * inv_dist_to_plane; + m[1][1] = m[1][1] * c - m[3][1] * s * inv_dist_to_plane; + m[1][2] = m[1][2] * c - m[3][2] * s * inv_dist_to_plane; + m[1][3] = m[1][3] * c - m[3][3] * s * inv_dist_to_plane; + flagBits = General; + return; + } + double len = double(x) * double(x) + + double(y) * double(y) + + double(z) * double(z); + if (!qFuzzyCompare(len, 1.0) && !qFuzzyIsNull(len)) { + len = std::sqrt(len); + x = double(double(x) / len); + y = double(double(y) / len); + z = double(double(z) / len); + } + double ic = 1.0 - c; + QDoubleMatrix4x4 rot(1); // The "1" says to not load the identity. + rot.m[0][0] = x * x * ic + c; + rot.m[1][0] = x * y * ic - z * s; + rot.m[2][0] = 0.0; + rot.m[3][0] = 0.0; + rot.m[0][1] = y * x * ic + z * s; + rot.m[1][1] = y * y * ic + c; + rot.m[2][1] = 0.0; + rot.m[3][1] = 0.0; + rot.m[0][2] = 0.0; + rot.m[1][2] = 0.0; + rot.m[2][2] = 1.0; + rot.m[3][2] = 0.0; + rot.m[0][3] = (x * z * ic - y * s) * -inv_dist_to_plane; + rot.m[1][3] = (y * z * ic + x * s) * -inv_dist_to_plane; + rot.m[2][3] = 0.0; + rot.m[3][3] = 1.0; + rot.flagBits = General; + *this *= rot; +} + +void QDoubleMatrix4x4::ortho(const QRect& rect) +{ + // Note: rect.right() and rect.bottom() subtract 1 in QRect, + // which gives the location of a pixel within the rectangle, + // instead of the extent of the rectangle. We want the extent. + // QRectF expresses the extent properly. + ortho(rect.x(), rect.x() + rect.width(), rect.y() + rect.height(), rect.y(), -1.0, 1.0); +} + +void QDoubleMatrix4x4::ortho(const QRectF& rect) +{ + ortho(rect.left(), rect.right(), rect.bottom(), rect.top(), -1.0, 1.0); +} + +void QDoubleMatrix4x4::ortho(double left, double right, double bottom, double top, double nearPlane, double farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (left == right || bottom == top || nearPlane == farPlane) + return; + + // Construct the projection. + double width = right - left; + double invheight = top - bottom; + double clip = farPlane - nearPlane; + QDoubleMatrix4x4 m(1); + m.m[0][0] = 2.0 / width; + m.m[1][0] = 0.0; + m.m[2][0] = 0.0; + m.m[3][0] = -(left + right) / width; + m.m[0][1] = 0.0; + m.m[1][1] = 2.0 / invheight; + m.m[2][1] = 0.0; + m.m[3][1] = -(top + bottom) / invheight; + m.m[0][2] = 0.0; + m.m[1][2] = 0.0; + m.m[2][2] = -2.0 / clip; + m.m[3][2] = -(nearPlane + farPlane) / clip; + m.m[0][3] = 0.0; + m.m[1][3] = 0.0; + m.m[2][3] = 0.0; + m.m[3][3] = 1.0; + m.flagBits = Translation | Scale; + + // Apply the projection. + *this *= m; +} + +void QDoubleMatrix4x4::frustum(double left, double right, double bottom, double top, double nearPlane, double farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (left == right || bottom == top || nearPlane == farPlane) + return; + + // Construct the projection. + QDoubleMatrix4x4 m(1); + double width = right - left; + double invheight = top - bottom; + double clip = farPlane - nearPlane; + m.m[0][0] = 2.0 * nearPlane / width; + m.m[1][0] = 0.0; + m.m[2][0] = (left + right) / width; + m.m[3][0] = 0.0; + m.m[0][1] = 0.0; + m.m[1][1] = 2.0 * nearPlane / invheight; + m.m[2][1] = (top + bottom) / invheight; + m.m[3][1] = 0.0; + m.m[0][2] = 0.0; + m.m[1][2] = 0.0; + m.m[2][2] = -(nearPlane + farPlane) / clip; + m.m[3][2] = -2.0 * nearPlane * farPlane / clip; + m.m[0][3] = 0.0; + m.m[1][3] = 0.0; + m.m[2][3] = -1.0; + m.m[3][3] = 0.0; + m.flagBits = General; + + // Apply the projection. + *this *= m; +} + +void QDoubleMatrix4x4::perspective(double verticalAngle, double aspectRatio, double nearPlane, double farPlane) +{ + // Bail out if the projection volume is zero-sized. + if (nearPlane == farPlane || aspectRatio == 0.0) + return; + + // Construct the projection. + QDoubleMatrix4x4 m(1); + double radians = qDegreesToRadians(verticalAngle / 2.0); + double sine = std::sin(radians); + if (sine == 0.0) + return; + double cotan = std::cos(radians) / sine; + double clip = farPlane - nearPlane; + m.m[0][0] = cotan / aspectRatio; + m.m[1][0] = 0.0; + m.m[2][0] = 0.0; + m.m[3][0] = 0.0; + m.m[0][1] = 0.0; + m.m[1][1] = cotan; + m.m[2][1] = 0.0; + m.m[3][1] = 0.0; + m.m[0][2] = 0.0; + m.m[1][2] = 0.0; + m.m[2][2] = -(nearPlane + farPlane) / clip; + m.m[3][2] = -(2.0 * nearPlane * farPlane) / clip; + m.m[0][3] = 0.0; + m.m[1][3] = 0.0; + m.m[2][3] = -1.0; + m.m[3][3] = 0.0; + m.flagBits = General; + + // Apply the projection. + *this *= m; +} + +void QDoubleMatrix4x4::lookAt(const QDoubleVector3D& eye, const QDoubleVector3D& center, const QDoubleVector3D& up) +{ + QDoubleVector3D forward = center - eye; + if (qFuzzyIsNull(forward.x()) && qFuzzyIsNull(forward.y()) && qFuzzyIsNull(forward.z())) + return; + + forward.normalize(); + QDoubleVector3D side = QDoubleVector3D::crossProduct(forward, up).normalized(); + QDoubleVector3D upVector = QDoubleVector3D::crossProduct(side, forward); + + QDoubleMatrix4x4 m(1); + m.m[0][0] = side.x(); + m.m[1][0] = side.y(); + m.m[2][0] = side.z(); + m.m[3][0] = 0.0; + m.m[0][1] = upVector.x(); + m.m[1][1] = upVector.y(); + m.m[2][1] = upVector.z(); + m.m[3][1] = 0.0; + m.m[0][2] = -forward.x(); + m.m[1][2] = -forward.y(); + m.m[2][2] = -forward.z(); + m.m[3][2] = 0.0; + m.m[0][3] = 0.0; + m.m[1][3] = 0.0; + m.m[2][3] = 0.0; + m.m[3][3] = 1.0; + m.flagBits = Rotation; + + *this *= m; + translate(-eye); +} + +void QDoubleMatrix4x4::viewport(double left, double bottom, double width, double height, double nearPlane, double farPlane) +{ + const double w2 = width / 2.0; + const double h2 = height / 2.0; + + QDoubleMatrix4x4 m(1); + m.m[0][0] = w2; + m.m[1][0] = 0.0; + m.m[2][0] = 0.0; + m.m[3][0] = left + w2; + m.m[0][1] = 0.0; + m.m[1][1] = h2; + m.m[2][1] = 0.0; + m.m[3][1] = bottom + h2; + m.m[0][2] = 0.0; + m.m[1][2] = 0.0; + m.m[2][2] = (farPlane - nearPlane) / 2.0; + m.m[3][2] = (nearPlane + farPlane) / 2.0; + m.m[0][3] = 0.0; + m.m[1][3] = 0.0; + m.m[2][3] = 0.0; + m.m[3][3] = 1.0; + m.flagBits = General; + + *this *= m; +} + +void QDoubleMatrix4x4::flipCoordinates() +{ + // Multiplying the y and z coordinates with -1 does NOT flip between right-handed and + // left-handed coordinate systems, it just rotates 180 degrees around the x axis, so + // I'm deprecating this function. + if (flagBits < Rotation2D) { + // Translation | Scale + m[1][1] = -m[1][1]; + m[2][2] = -m[2][2]; + } else { + m[1][0] = -m[1][0]; + m[1][1] = -m[1][1]; + m[1][2] = -m[1][2]; + m[1][3] = -m[1][3]; + m[2][0] = -m[2][0]; + m[2][1] = -m[2][1]; + m[2][2] = -m[2][2]; + m[2][3] = -m[2][3]; + } + flagBits |= Scale; +} + +void QDoubleMatrix4x4::copyDataTo(double *values) const +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + values[row * 4 + col] = double(m[col][row]); +} + +QRect QDoubleMatrix4x4::mapRect(const QRect& rect) const +{ + if (flagBits < Scale) { + // Translation + return QRect(qRound(rect.x() + m[3][0]), + qRound(rect.y() + m[3][1]), + rect.width(), rect.height()); + } else if (flagBits < Rotation2D) { + // Translation | Scale + double x = rect.x() * m[0][0] + m[3][0]; + double y = rect.y() * m[1][1] + m[3][1]; + double w = rect.width() * m[0][0]; + double h = rect.height() * m[1][1]; + if (w < 0) { + w = -w; + x -= w; + } + if (h < 0) { + h = -h; + y -= h; + } + return QRect(qRound(x), qRound(y), qRound(w), qRound(h)); + } + + QPoint tl = map(rect.topLeft()); + QPoint tr = map(QPoint(rect.x() + rect.width(), rect.y())); + QPoint bl = map(QPoint(rect.x(), rect.y() + rect.height())); + QPoint br = map(QPoint(rect.x() + rect.width(), + rect.y() + rect.height())); + + int xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x())); + int xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x())); + int ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y())); + int ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y())); + + return QRect(xmin, ymin, xmax - xmin, ymax - ymin); +} + +QRectF QDoubleMatrix4x4::mapRect(const QRectF& rect) const +{ + if (flagBits < Scale) { + // Translation + return rect.translated(m[3][0], m[3][1]); + } else if (flagBits < Rotation2D) { + // Translation | Scale + double x = rect.x() * m[0][0] + m[3][0]; + double y = rect.y() * m[1][1] + m[3][1]; + double w = rect.width() * m[0][0]; + double h = rect.height() * m[1][1]; + if (w < 0) { + w = -w; + x -= w; + } + if (h < 0) { + h = -h; + y -= h; + } + return QRectF(x, y, w, h); + } + + QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight()); + QPointF bl = map(rect.bottomLeft()); QPointF br = map(rect.bottomRight()); + + double xmin = qMin(qMin(tl.x(), tr.x()), qMin(bl.x(), br.x())); + double xmax = qMax(qMax(tl.x(), tr.x()), qMax(bl.x(), br.x())); + double ymin = qMin(qMin(tl.y(), tr.y()), qMin(bl.y(), br.y())); + double ymax = qMax(qMax(tl.y(), tr.y()), qMax(bl.y(), br.y())); + + return QRectF(QPointF(xmin, ymin), QPointF(xmax, ymax)); +} + +QDoubleMatrix4x4 QDoubleMatrix4x4::orthonormalInverse() const +{ + QDoubleMatrix4x4 result(1); // The '1' says not to load identity + + result.m[0][0] = m[0][0]; + result.m[1][0] = m[0][1]; + result.m[2][0] = m[0][2]; + + result.m[0][1] = m[1][0]; + result.m[1][1] = m[1][1]; + result.m[2][1] = m[1][2]; + + result.m[0][2] = m[2][0]; + result.m[1][2] = m[2][1]; + result.m[2][2] = m[2][2]; + + result.m[0][3] = 0.0; + result.m[1][3] = 0.0; + result.m[2][3] = 0.0; + + result.m[3][0] = -(result.m[0][0] * m[3][0] + result.m[1][0] * m[3][1] + result.m[2][0] * m[3][2]); + result.m[3][1] = -(result.m[0][1] * m[3][0] + result.m[1][1] * m[3][1] + result.m[2][1] * m[3][2]); + result.m[3][2] = -(result.m[0][2] * m[3][0] + result.m[1][2] * m[3][1] + result.m[2][2] * m[3][2]); + result.m[3][3] = 1.0; + + result.flagBits = flagBits; + + return result; +} + +void QDoubleMatrix4x4::optimize() +{ + // If the last row is not (0, 0, 0, 1), the matrix is not a special type. + flagBits = General; + if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0 || m[3][3] != 1) + return; + + flagBits &= ~Perspective; + + // If the last column is (0, 0, 0, 1), then there is no translation. + if (m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0) + flagBits &= ~Translation; + + // If the two first elements of row 3 and column 3 are 0, then any rotation must be about Z. + if (!m[0][2] && !m[1][2] && !m[2][0] && !m[2][1]) { + flagBits &= ~Rotation; + // If the six non-diagonal elements in the top left 3x3 matrix are 0, there is no rotation. + if (!m[0][1] && !m[1][0]) { + flagBits &= ~Rotation2D; + // Check for identity. + if (m[0][0] == 1 && m[1][1] == 1 && m[2][2] == 1) + flagBits &= ~Scale; + } else { + // If the columns are orthonormal and form a right-handed system, then there is no scale. + double det = matrixDet2(m, 0, 1, 0, 1); + double lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1]; + double lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1]; + double lenZ = m[2][2]; + if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0) + && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0)) + { + flagBits &= ~Scale; + } + } + } else { + // If the columns are orthonormal and form a right-handed system, then there is no scale. + double det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + double lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2]; + double lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1] + m[1][2] * m[1][2]; + double lenZ = m[2][0] * m[2][0] + m[2][1] * m[2][1] + m[2][2] * m[2][2]; + if (qFuzzyCompare(det, 1.0) && qFuzzyCompare(lenX, 1.0) + && qFuzzyCompare(lenY, 1.0) && qFuzzyCompare(lenZ, 1.0)) + { + flagBits &= ~Scale; + } + } +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QDoubleMatrix4x4 &m) +{ + QDebugStateSaver saver(dbg); + // Create a string that represents the matrix type. + QByteArray bits; + if (m.flagBits == QDoubleMatrix4x4::Identity) { + bits = "Identity"; + } else if (m.flagBits == QDoubleMatrix4x4::General) { + bits = "General"; + } else { + if ((m.flagBits & QDoubleMatrix4x4::Translation) != 0) + bits += "Translation,"; + if ((m.flagBits & QDoubleMatrix4x4::Scale) != 0) + bits += "Scale,"; + if ((m.flagBits & QDoubleMatrix4x4::Rotation2D) != 0) + bits += "Rotation2D,"; + if ((m.flagBits & QDoubleMatrix4x4::Rotation) != 0) + bits += "Rotation,"; + if ((m.flagBits & QDoubleMatrix4x4::Perspective) != 0) + bits += "Perspective,"; + if (bits.size() > 0) + bits = bits.left(bits.size() - 1); + } + + // Output in row-major order because it is more human-readable. + dbg.nospace() << "QDoubleMatrix4x4(type:" << bits.constData() << endl + << qSetFieldWidth(10) + << m(0, 0) << m(0, 1) << m(0, 2) << m(0, 3) << endl + << m(1, 0) << m(1, 1) << m(1, 2) << m(1, 3) << endl + << m(2, 0) << m(2, 1) << m(2, 2) << m(2, 3) << endl + << m(3, 0) << m(3, 1) << m(3, 2) << m(3, 3) << endl + << qSetFieldWidth(0) << ')'; + return dbg; +} + +#endif + +#ifndef QT_NO_DATASTREAM + +QDataStream &operator<<(QDataStream &stream, const QDoubleMatrix4x4 &matrix) +{ + for (int row = 0; row < 4; ++row) + for (int col = 0; col < 4; ++col) + stream << matrix(row, col); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QDoubleMatrix4x4 &matrix) +{ + double x; + for (int row = 0; row < 4; ++row) { + for (int col = 0; col < 4; ++col) { + stream >> x; + matrix(row, col) = x; + } + } + matrix.optimize(); + return stream; +} + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/positioning/qdoublematrix4x4_p.h b/src/positioning/qdoublematrix4x4_p.h new file mode 100644 index 0000000..aaa0b27 --- /dev/null +++ b/src/positioning/qdoublematrix4x4_p.h @@ -0,0 +1,946 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDOUBLEMATRIX4X4_H +#define QDOUBLEMATRIX4X4_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/* + * This class is a copy/paste/replace of QMatrix4x4 + * No algorithm has been changed. + * Some methods have been removed. + */ + +class Q_POSITIONING_PRIVATE_EXPORT QDoubleMatrix4x4 +{ +public: + inline QDoubleMatrix4x4() { setToIdentity(); } + explicit QDoubleMatrix4x4(Qt::Initialization) : flagBits(General) {} + explicit QDoubleMatrix4x4(const double *values); + inline QDoubleMatrix4x4(double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double m41, double m42, double m43, double m44); + + QDoubleMatrix4x4(const double *values, int cols, int rows); + + inline const double& operator()(int row, int column) const; + inline double& operator()(int row, int column); + + inline bool isAffine() const; + + inline bool isIdentity() const; + inline void setToIdentity(); + + inline void fill(double value); + + double determinant() const; + QDoubleMatrix4x4 inverted(bool *invertible = nullptr) const; + QDoubleMatrix4x4 transposed() const; + + inline QDoubleMatrix4x4& operator+=(const QDoubleMatrix4x4& other); + inline QDoubleMatrix4x4& operator-=(const QDoubleMatrix4x4& other); + inline QDoubleMatrix4x4& operator*=(const QDoubleMatrix4x4& other); + inline QDoubleMatrix4x4& operator*=(double factor); + QDoubleMatrix4x4& operator/=(double divisor); + inline bool operator==(const QDoubleMatrix4x4& other) const; + inline bool operator!=(const QDoubleMatrix4x4& other) const; + + friend QDoubleMatrix4x4 operator+(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2); + friend QDoubleMatrix4x4 operator-(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2); + friend QDoubleMatrix4x4 operator*(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2); + + friend QDoubleVector3D operator*(const QDoubleMatrix4x4& matrix, const QDoubleVector3D& vector); + friend QDoubleVector3D operator*(const QDoubleVector3D& vector, const QDoubleMatrix4x4& matrix); + + friend QPoint operator*(const QPoint& point, const QDoubleMatrix4x4& matrix); + friend QPointF operator*(const QPointF& point, const QDoubleMatrix4x4& matrix); + friend QDoubleMatrix4x4 operator-(const QDoubleMatrix4x4& matrix); + friend QPoint operator*(const QDoubleMatrix4x4& matrix, const QPoint& point); + friend QPointF operator*(const QDoubleMatrix4x4& matrix, const QPointF& point); + friend QDoubleMatrix4x4 operator*(double factor, const QDoubleMatrix4x4& matrix); + friend QDoubleMatrix4x4 operator*(const QDoubleMatrix4x4& matrix, double factor); + friend Q_POSITIONING_PRIVATE_EXPORT QDoubleMatrix4x4 operator/(const QDoubleMatrix4x4& matrix, double divisor); + + friend inline bool qFuzzyCompare(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2); + + + void scale(const QDoubleVector3D& vector); + void translate(const QDoubleVector3D& vector); + void rotate(double angle, const QDoubleVector3D& vector); + + void scale(double x, double y); + void scale(double x, double y, double z); + void scale(double factor); + void translate(double x, double y); + void translate(double x, double y, double z); + void rotate(double angle, double x, double y, double z = 0.0f); + + void ortho(const QRect& rect); + void ortho(const QRectF& rect); + void ortho(double left, double right, double bottom, double top, double nearPlane, double farPlane); + void frustum(double left, double right, double bottom, double top, double nearPlane, double farPlane); + void perspective(double verticalAngle, double aspectRatio, double nearPlane, double farPlane); + + void lookAt(const QDoubleVector3D& eye, const QDoubleVector3D& center, const QDoubleVector3D& up); + + void viewport(const QRectF &rect); + void viewport(double left, double bottom, double width, double height, double nearPlane = 0.0f, double farPlane = 1.0f); + void flipCoordinates(); + + void copyDataTo(double *values) const; + + QPoint map(const QPoint& point) const; + QPointF map(const QPointF& point) const; + + QDoubleVector3D map(const QDoubleVector3D& point) const; + QDoubleVector3D mapVector(const QDoubleVector3D& vector) const; + + QRect mapRect(const QRect& rect) const; + QRectF mapRect(const QRectF& rect) const; + + inline double *data(); + inline const double *data() const { return *m; } + inline const double *constData() const { return *m; } + + void optimize(); + +#ifndef QT_NO_DEBUG_STREAM + friend Q_POSITIONING_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QDoubleMatrix4x4 &m); +#endif + +private: + double m[4][4]; // Column-major order to match OpenGL. + int flagBits; // Flag bits from the enum below. + + // When matrices are multiplied, the flag bits are or-ed together. + enum { + Identity = 0x0000, // Identity matrix + Translation = 0x0001, // Contains a translation + Scale = 0x0002, // Contains a scale + Rotation2D = 0x0004, // Contains a rotation about the Z axis + Rotation = 0x0008, // Contains an arbitrary rotation + Perspective = 0x0010, // Last row is different from (0, 0, 0, 1) + General = 0x001f // General matrix, unknown contents + }; + + // Construct without initializing identity matrix. + explicit QDoubleMatrix4x4(int) { } + + QDoubleMatrix4x4 orthonormalInverse() const; + + void projectedRotate(double angle, double x, double y, double z); +}; + +Q_DECLARE_TYPEINFO(QDoubleMatrix4x4, Q_MOVABLE_TYPE); + +inline QDoubleMatrix4x4::QDoubleMatrix4x4 + (double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double m41, double m42, double m43, double m44) +{ + m[0][0] = m11; m[0][1] = m21; m[0][2] = m31; m[0][3] = m41; + m[1][0] = m12; m[1][1] = m22; m[1][2] = m32; m[1][3] = m42; + m[2][0] = m13; m[2][1] = m23; m[2][2] = m33; m[2][3] = m43; + m[3][0] = m14; m[3][1] = m24; m[3][2] = m34; m[3][3] = m44; + flagBits = General; +} + +inline const double& QDoubleMatrix4x4::operator()(int aRow, int aColumn) const +{ + Q_ASSERT(aRow >= 0 && aRow < 4 && aColumn >= 0 && aColumn < 4); + return m[aColumn][aRow]; +} + +inline double& QDoubleMatrix4x4::operator()(int aRow, int aColumn) +{ + Q_ASSERT(aRow >= 0 && aRow < 4 && aColumn >= 0 && aColumn < 4); + flagBits = General; + return m[aColumn][aRow]; +} + +Q_POSITIONING_PRIVATE_EXPORT QDoubleMatrix4x4 operator/(const QDoubleMatrix4x4& matrix, double divisor); + +inline bool QDoubleMatrix4x4::isAffine() const +{ + return m[0][3] == 0.0f && m[1][3] == 0.0f && m[2][3] == 0.0f && m[3][3] == 1.0f; +} + +inline bool QDoubleMatrix4x4::isIdentity() const +{ + if (flagBits == Identity) + return true; + if (m[0][0] != 1.0f || m[0][1] != 0.0f || m[0][2] != 0.0f) + return false; + if (m[0][3] != 0.0f || m[1][0] != 0.0f || m[1][1] != 1.0f) + return false; + if (m[1][2] != 0.0f || m[1][3] != 0.0f || m[2][0] != 0.0f) + return false; + if (m[2][1] != 0.0f || m[2][2] != 1.0f || m[2][3] != 0.0f) + return false; + if (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f) + return false; + return (m[3][3] == 1.0f); +} + +inline void QDoubleMatrix4x4::setToIdentity() +{ + m[0][0] = 1.0f; + m[0][1] = 0.0f; + m[0][2] = 0.0f; + m[0][3] = 0.0f; + m[1][0] = 0.0f; + m[1][1] = 1.0f; + m[1][2] = 0.0f; + m[1][3] = 0.0f; + m[2][0] = 0.0f; + m[2][1] = 0.0f; + m[2][2] = 1.0f; + m[2][3] = 0.0f; + m[3][0] = 0.0f; + m[3][1] = 0.0f; + m[3][2] = 0.0f; + m[3][3] = 1.0f; + flagBits = Identity; +} + +inline void QDoubleMatrix4x4::fill(double value) +{ + m[0][0] = value; + m[0][1] = value; + m[0][2] = value; + m[0][3] = value; + m[1][0] = value; + m[1][1] = value; + m[1][2] = value; + m[1][3] = value; + m[2][0] = value; + m[2][1] = value; + m[2][2] = value; + m[2][3] = value; + m[3][0] = value; + m[3][1] = value; + m[3][2] = value; + m[3][3] = value; + flagBits = General; +} + +inline QDoubleMatrix4x4& QDoubleMatrix4x4::operator+=(const QDoubleMatrix4x4& other) +{ + m[0][0] += other.m[0][0]; + m[0][1] += other.m[0][1]; + m[0][2] += other.m[0][2]; + m[0][3] += other.m[0][3]; + m[1][0] += other.m[1][0]; + m[1][1] += other.m[1][1]; + m[1][2] += other.m[1][2]; + m[1][3] += other.m[1][3]; + m[2][0] += other.m[2][0]; + m[2][1] += other.m[2][1]; + m[2][2] += other.m[2][2]; + m[2][3] += other.m[2][3]; + m[3][0] += other.m[3][0]; + m[3][1] += other.m[3][1]; + m[3][2] += other.m[3][2]; + m[3][3] += other.m[3][3]; + flagBits = General; + return *this; +} + +inline QDoubleMatrix4x4& QDoubleMatrix4x4::operator-=(const QDoubleMatrix4x4& other) +{ + m[0][0] -= other.m[0][0]; + m[0][1] -= other.m[0][1]; + m[0][2] -= other.m[0][2]; + m[0][3] -= other.m[0][3]; + m[1][0] -= other.m[1][0]; + m[1][1] -= other.m[1][1]; + m[1][2] -= other.m[1][2]; + m[1][3] -= other.m[1][3]; + m[2][0] -= other.m[2][0]; + m[2][1] -= other.m[2][1]; + m[2][2] -= other.m[2][2]; + m[2][3] -= other.m[2][3]; + m[3][0] -= other.m[3][0]; + m[3][1] -= other.m[3][1]; + m[3][2] -= other.m[3][2]; + m[3][3] -= other.m[3][3]; + flagBits = General; + return *this; +} + +inline QDoubleMatrix4x4& QDoubleMatrix4x4::operator*=(const QDoubleMatrix4x4& other) +{ + flagBits |= other.flagBits; + + if (flagBits < Rotation2D) { + m[3][0] += m[0][0] * other.m[3][0]; + m[3][1] += m[1][1] * other.m[3][1]; + m[3][2] += m[2][2] * other.m[3][2]; + + m[0][0] *= other.m[0][0]; + m[1][1] *= other.m[1][1]; + m[2][2] *= other.m[2][2]; + return *this; + } + + double m0, m1, m2; + m0 = m[0][0] * other.m[0][0] + + m[1][0] * other.m[0][1] + + m[2][0] * other.m[0][2] + + m[3][0] * other.m[0][3]; + m1 = m[0][0] * other.m[1][0] + + m[1][0] * other.m[1][1] + + m[2][0] * other.m[1][2] + + m[3][0] * other.m[1][3]; + m2 = m[0][0] * other.m[2][0] + + m[1][0] * other.m[2][1] + + m[2][0] * other.m[2][2] + + m[3][0] * other.m[2][3]; + m[3][0] = m[0][0] * other.m[3][0] + + m[1][0] * other.m[3][1] + + m[2][0] * other.m[3][2] + + m[3][0] * other.m[3][3]; + m[0][0] = m0; + m[1][0] = m1; + m[2][0] = m2; + + m0 = m[0][1] * other.m[0][0] + + m[1][1] * other.m[0][1] + + m[2][1] * other.m[0][2] + + m[3][1] * other.m[0][3]; + m1 = m[0][1] * other.m[1][0] + + m[1][1] * other.m[1][1] + + m[2][1] * other.m[1][2] + + m[3][1] * other.m[1][3]; + m2 = m[0][1] * other.m[2][0] + + m[1][1] * other.m[2][1] + + m[2][1] * other.m[2][2] + + m[3][1] * other.m[2][3]; + m[3][1] = m[0][1] * other.m[3][0] + + m[1][1] * other.m[3][1] + + m[2][1] * other.m[3][2] + + m[3][1] * other.m[3][3]; + m[0][1] = m0; + m[1][1] = m1; + m[2][1] = m2; + + m0 = m[0][2] * other.m[0][0] + + m[1][2] * other.m[0][1] + + m[2][2] * other.m[0][2] + + m[3][2] * other.m[0][3]; + m1 = m[0][2] * other.m[1][0] + + m[1][2] * other.m[1][1] + + m[2][2] * other.m[1][2] + + m[3][2] * other.m[1][3]; + m2 = m[0][2] * other.m[2][0] + + m[1][2] * other.m[2][1] + + m[2][2] * other.m[2][2] + + m[3][2] * other.m[2][3]; + m[3][2] = m[0][2] * other.m[3][0] + + m[1][2] * other.m[3][1] + + m[2][2] * other.m[3][2] + + m[3][2] * other.m[3][3]; + m[0][2] = m0; + m[1][2] = m1; + m[2][2] = m2; + + m0 = m[0][3] * other.m[0][0] + + m[1][3] * other.m[0][1] + + m[2][3] * other.m[0][2] + + m[3][3] * other.m[0][3]; + m1 = m[0][3] * other.m[1][0] + + m[1][3] * other.m[1][1] + + m[2][3] * other.m[1][2] + + m[3][3] * other.m[1][3]; + m2 = m[0][3] * other.m[2][0] + + m[1][3] * other.m[2][1] + + m[2][3] * other.m[2][2] + + m[3][3] * other.m[2][3]; + m[3][3] = m[0][3] * other.m[3][0] + + m[1][3] * other.m[3][1] + + m[2][3] * other.m[3][2] + + m[3][3] * other.m[3][3]; + m[0][3] = m0; + m[1][3] = m1; + m[2][3] = m2; + return *this; +} + +inline QDoubleMatrix4x4& QDoubleMatrix4x4::operator*=(double factor) +{ + m[0][0] *= factor; + m[0][1] *= factor; + m[0][2] *= factor; + m[0][3] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[1][2] *= factor; + m[1][3] *= factor; + m[2][0] *= factor; + m[2][1] *= factor; + m[2][2] *= factor; + m[2][3] *= factor; + m[3][0] *= factor; + m[3][1] *= factor; + m[3][2] *= factor; + m[3][3] *= factor; + flagBits = General; + return *this; +} + +inline bool QDoubleMatrix4x4::operator==(const QDoubleMatrix4x4& other) const +{ + return m[0][0] == other.m[0][0] && + m[0][1] == other.m[0][1] && + m[0][2] == other.m[0][2] && + m[0][3] == other.m[0][3] && + m[1][0] == other.m[1][0] && + m[1][1] == other.m[1][1] && + m[1][2] == other.m[1][2] && + m[1][3] == other.m[1][3] && + m[2][0] == other.m[2][0] && + m[2][1] == other.m[2][1] && + m[2][2] == other.m[2][2] && + m[2][3] == other.m[2][3] && + m[3][0] == other.m[3][0] && + m[3][1] == other.m[3][1] && + m[3][2] == other.m[3][2] && + m[3][3] == other.m[3][3]; +} + +inline bool QDoubleMatrix4x4::operator!=(const QDoubleMatrix4x4& other) const +{ + return m[0][0] != other.m[0][0] || + m[0][1] != other.m[0][1] || + m[0][2] != other.m[0][2] || + m[0][3] != other.m[0][3] || + m[1][0] != other.m[1][0] || + m[1][1] != other.m[1][1] || + m[1][2] != other.m[1][2] || + m[1][3] != other.m[1][3] || + m[2][0] != other.m[2][0] || + m[2][1] != other.m[2][1] || + m[2][2] != other.m[2][2] || + m[2][3] != other.m[2][3] || + m[3][0] != other.m[3][0] || + m[3][1] != other.m[3][1] || + m[3][2] != other.m[3][2] || + m[3][3] != other.m[3][3]; +} + +inline QDoubleMatrix4x4 operator+(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2) +{ + QDoubleMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] + m2.m[0][0]; + m.m[0][1] = m1.m[0][1] + m2.m[0][1]; + m.m[0][2] = m1.m[0][2] + m2.m[0][2]; + m.m[0][3] = m1.m[0][3] + m2.m[0][3]; + m.m[1][0] = m1.m[1][0] + m2.m[1][0]; + m.m[1][1] = m1.m[1][1] + m2.m[1][1]; + m.m[1][2] = m1.m[1][2] + m2.m[1][2]; + m.m[1][3] = m1.m[1][3] + m2.m[1][3]; + m.m[2][0] = m1.m[2][0] + m2.m[2][0]; + m.m[2][1] = m1.m[2][1] + m2.m[2][1]; + m.m[2][2] = m1.m[2][2] + m2.m[2][2]; + m.m[2][3] = m1.m[2][3] + m2.m[2][3]; + m.m[3][0] = m1.m[3][0] + m2.m[3][0]; + m.m[3][1] = m1.m[3][1] + m2.m[3][1]; + m.m[3][2] = m1.m[3][2] + m2.m[3][2]; + m.m[3][3] = m1.m[3][3] + m2.m[3][3]; + m.flagBits = QDoubleMatrix4x4::General; + return m; +} + +inline QDoubleMatrix4x4 operator-(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2) +{ + QDoubleMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] - m2.m[0][0]; + m.m[0][1] = m1.m[0][1] - m2.m[0][1]; + m.m[0][2] = m1.m[0][2] - m2.m[0][2]; + m.m[0][3] = m1.m[0][3] - m2.m[0][3]; + m.m[1][0] = m1.m[1][0] - m2.m[1][0]; + m.m[1][1] = m1.m[1][1] - m2.m[1][1]; + m.m[1][2] = m1.m[1][2] - m2.m[1][2]; + m.m[1][3] = m1.m[1][3] - m2.m[1][3]; + m.m[2][0] = m1.m[2][0] - m2.m[2][0]; + m.m[2][1] = m1.m[2][1] - m2.m[2][1]; + m.m[2][2] = m1.m[2][2] - m2.m[2][2]; + m.m[2][3] = m1.m[2][3] - m2.m[2][3]; + m.m[3][0] = m1.m[3][0] - m2.m[3][0]; + m.m[3][1] = m1.m[3][1] - m2.m[3][1]; + m.m[3][2] = m1.m[3][2] - m2.m[3][2]; + m.m[3][3] = m1.m[3][3] - m2.m[3][3]; + m.flagBits = QDoubleMatrix4x4::General; + return m; +} + +inline QDoubleMatrix4x4 operator*(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2) +{ + int flagBits = m1.flagBits | m2.flagBits; + if (flagBits < QDoubleMatrix4x4::Rotation2D) { + QDoubleMatrix4x4 m = m1; + m.m[3][0] += m.m[0][0] * m2.m[3][0]; + m.m[3][1] += m.m[1][1] * m2.m[3][1]; + m.m[3][2] += m.m[2][2] * m2.m[3][2]; + + m.m[0][0] *= m2.m[0][0]; + m.m[1][1] *= m2.m[1][1]; + m.m[2][2] *= m2.m[2][2]; + m.flagBits = flagBits; + return m; + } + + QDoubleMatrix4x4 m(1); + m.m[0][0] = m1.m[0][0] * m2.m[0][0] + + m1.m[1][0] * m2.m[0][1] + + m1.m[2][0] * m2.m[0][2] + + m1.m[3][0] * m2.m[0][3]; + m.m[0][1] = m1.m[0][1] * m2.m[0][0] + + m1.m[1][1] * m2.m[0][1] + + m1.m[2][1] * m2.m[0][2] + + m1.m[3][1] * m2.m[0][3]; + m.m[0][2] = m1.m[0][2] * m2.m[0][0] + + m1.m[1][2] * m2.m[0][1] + + m1.m[2][2] * m2.m[0][2] + + m1.m[3][2] * m2.m[0][3]; + m.m[0][3] = m1.m[0][3] * m2.m[0][0] + + m1.m[1][3] * m2.m[0][1] + + m1.m[2][3] * m2.m[0][2] + + m1.m[3][3] * m2.m[0][3]; + + m.m[1][0] = m1.m[0][0] * m2.m[1][0] + + m1.m[1][0] * m2.m[1][1] + + m1.m[2][0] * m2.m[1][2] + + m1.m[3][0] * m2.m[1][3]; + m.m[1][1] = m1.m[0][1] * m2.m[1][0] + + m1.m[1][1] * m2.m[1][1] + + m1.m[2][1] * m2.m[1][2] + + m1.m[3][1] * m2.m[1][3]; + m.m[1][2] = m1.m[0][2] * m2.m[1][0] + + m1.m[1][2] * m2.m[1][1] + + m1.m[2][2] * m2.m[1][2] + + m1.m[3][2] * m2.m[1][3]; + m.m[1][3] = m1.m[0][3] * m2.m[1][0] + + m1.m[1][3] * m2.m[1][1] + + m1.m[2][3] * m2.m[1][2] + + m1.m[3][3] * m2.m[1][3]; + + m.m[2][0] = m1.m[0][0] * m2.m[2][0] + + m1.m[1][0] * m2.m[2][1] + + m1.m[2][0] * m2.m[2][2] + + m1.m[3][0] * m2.m[2][3]; + m.m[2][1] = m1.m[0][1] * m2.m[2][0] + + m1.m[1][1] * m2.m[2][1] + + m1.m[2][1] * m2.m[2][2] + + m1.m[3][1] * m2.m[2][3]; + m.m[2][2] = m1.m[0][2] * m2.m[2][0] + + m1.m[1][2] * m2.m[2][1] + + m1.m[2][2] * m2.m[2][2] + + m1.m[3][2] * m2.m[2][3]; + m.m[2][3] = m1.m[0][3] * m2.m[2][0] + + m1.m[1][3] * m2.m[2][1] + + m1.m[2][3] * m2.m[2][2] + + m1.m[3][3] * m2.m[2][3]; + + m.m[3][0] = m1.m[0][0] * m2.m[3][0] + + m1.m[1][0] * m2.m[3][1] + + m1.m[2][0] * m2.m[3][2] + + m1.m[3][0] * m2.m[3][3]; + m.m[3][1] = m1.m[0][1] * m2.m[3][0] + + m1.m[1][1] * m2.m[3][1] + + m1.m[2][1] * m2.m[3][2] + + m1.m[3][1] * m2.m[3][3]; + m.m[3][2] = m1.m[0][2] * m2.m[3][0] + + m1.m[1][2] * m2.m[3][1] + + m1.m[2][2] * m2.m[3][2] + + m1.m[3][2] * m2.m[3][3]; + m.m[3][3] = m1.m[0][3] * m2.m[3][0] + + m1.m[1][3] * m2.m[3][1] + + m1.m[2][3] * m2.m[3][2] + + m1.m[3][3] * m2.m[3][3]; + m.flagBits = flagBits; + return m; +} + +inline QDoubleVector3D operator*(const QDoubleVector3D& vector, const QDoubleMatrix4x4& matrix) +{ + double x, y, z, w; + x = vector.x() * matrix.m[0][0] + + vector.y() * matrix.m[0][1] + + vector.z() * matrix.m[0][2] + + matrix.m[0][3]; + y = vector.x() * matrix.m[1][0] + + vector.y() * matrix.m[1][1] + + vector.z() * matrix.m[1][2] + + matrix.m[1][3]; + z = vector.x() * matrix.m[2][0] + + vector.y() * matrix.m[2][1] + + vector.z() * matrix.m[2][2] + + matrix.m[2][3]; + w = vector.x() * matrix.m[3][0] + + vector.y() * matrix.m[3][1] + + vector.z() * matrix.m[3][2] + + matrix.m[3][3]; + if (w == 1.0f) + return QDoubleVector3D(x, y, z); + else + return QDoubleVector3D(x / w, y / w, z / w); +} + +inline QDoubleVector3D operator*(const QDoubleMatrix4x4& matrix, const QDoubleVector3D& vector) +{ + double x, y, z, w; + if (matrix.flagBits == QDoubleMatrix4x4::Identity) { + return vector; + } else if (matrix.flagBits < QDoubleMatrix4x4::Rotation2D) { + // Translation | Scale + return QDoubleVector3D(vector.x() * matrix.m[0][0] + matrix.m[3][0], + vector.y() * matrix.m[1][1] + matrix.m[3][1], + vector.z() * matrix.m[2][2] + matrix.m[3][2]); + } else if (matrix.flagBits < QDoubleMatrix4x4::Rotation) { + // Translation | Scale | Rotation2D + return QDoubleVector3D(vector.x() * matrix.m[0][0] + vector.y() * matrix.m[1][0] + matrix.m[3][0], + vector.x() * matrix.m[0][1] + vector.y() * matrix.m[1][1] + matrix.m[3][1], + vector.z() * matrix.m[2][2] + matrix.m[3][2]); + } else { + x = vector.x() * matrix.m[0][0] + + vector.y() * matrix.m[1][0] + + vector.z() * matrix.m[2][0] + + matrix.m[3][0]; + y = vector.x() * matrix.m[0][1] + + vector.y() * matrix.m[1][1] + + vector.z() * matrix.m[2][1] + + matrix.m[3][1]; + z = vector.x() * matrix.m[0][2] + + vector.y() * matrix.m[1][2] + + vector.z() * matrix.m[2][2] + + matrix.m[3][2]; + w = vector.x() * matrix.m[0][3] + + vector.y() * matrix.m[1][3] + + vector.z() * matrix.m[2][3] + + matrix.m[3][3]; + if (w == 1.0f) + return QDoubleVector3D(x, y, z); + else + return QDoubleVector3D(x / w, y / w, z / w); + } +} + +inline QPoint operator*(const QPoint& point, const QDoubleMatrix4x4& matrix) +{ + double xin, yin; + double x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[0][1] + + matrix.m[0][3]; + y = xin * matrix.m[1][0] + + yin * matrix.m[1][1] + + matrix.m[1][3]; + w = xin * matrix.m[3][0] + + yin * matrix.m[3][1] + + matrix.m[3][3]; + if (w == 1.0f) + return QPoint(qRound(x), qRound(y)); + else + return QPoint(qRound(x / w), qRound(y / w)); +} + +inline QPointF operator*(const QPointF& point, const QDoubleMatrix4x4& matrix) +{ + double xin, yin; + double x, y, w; + xin = point.x(); + yin = point.y(); + x = xin * matrix.m[0][0] + + yin * matrix.m[0][1] + + matrix.m[0][3]; + y = xin * matrix.m[1][0] + + yin * matrix.m[1][1] + + matrix.m[1][3]; + w = xin * matrix.m[3][0] + + yin * matrix.m[3][1] + + matrix.m[3][3]; + if (w == 1.0f) { + return QPointF(double(x), double(y)); + } else { + return QPointF(double(x / w), double(y / w)); + } +} + +inline QPoint operator*(const QDoubleMatrix4x4& matrix, const QPoint& point) +{ + double xin, yin; + double x, y, w; + xin = point.x(); + yin = point.y(); + if (matrix.flagBits == QDoubleMatrix4x4::Identity) { + return point; + } else if (matrix.flagBits < QDoubleMatrix4x4::Rotation2D) { + // Translation | Scale + return QPoint(qRound(xin * matrix.m[0][0] + matrix.m[3][0]), + qRound(yin * matrix.m[1][1] + matrix.m[3][1])); + } else if (matrix.flagBits < QDoubleMatrix4x4::Perspective) { + return QPoint(qRound(xin * matrix.m[0][0] + yin * matrix.m[1][0] + matrix.m[3][0]), + qRound(xin * matrix.m[0][1] + yin * matrix.m[1][1] + matrix.m[3][1])); + } else { + x = xin * matrix.m[0][0] + + yin * matrix.m[1][0] + + matrix.m[3][0]; + y = xin * matrix.m[0][1] + + yin * matrix.m[1][1] + + matrix.m[3][1]; + w = xin * matrix.m[0][3] + + yin * matrix.m[1][3] + + matrix.m[3][3]; + if (w == 1.0f) + return QPoint(qRound(x), qRound(y)); + else + return QPoint(qRound(x / w), qRound(y / w)); + } +} + +inline QPointF operator*(const QDoubleMatrix4x4& matrix, const QPointF& point) +{ + double xin, yin; + double x, y, w; + xin = point.x(); + yin = point.y(); + if (matrix.flagBits == QDoubleMatrix4x4::Identity) { + return point; + } else if (matrix.flagBits < QDoubleMatrix4x4::Rotation2D) { + // Translation | Scale + return QPointF(xin * matrix.m[0][0] + matrix.m[3][0], + yin * matrix.m[1][1] + matrix.m[3][1]); + } else if (matrix.flagBits < QDoubleMatrix4x4::Perspective) { + return QPointF(xin * matrix.m[0][0] + yin * matrix.m[1][0] + matrix.m[3][0], + xin * matrix.m[0][1] + yin * matrix.m[1][1] + matrix.m[3][1]); + } else { + x = xin * matrix.m[0][0] + + yin * matrix.m[1][0] + + matrix.m[3][0]; + y = xin * matrix.m[0][1] + + yin * matrix.m[1][1] + + matrix.m[3][1]; + w = xin * matrix.m[0][3] + + yin * matrix.m[1][3] + + matrix.m[3][3]; + if (w == 1.0f) { + return QPointF(double(x), double(y)); + } else { + return QPointF(double(x / w), double(y / w)); + } + } +} + +inline QDoubleMatrix4x4 operator-(const QDoubleMatrix4x4& matrix) +{ + QDoubleMatrix4x4 m(1); + m.m[0][0] = -matrix.m[0][0]; + m.m[0][1] = -matrix.m[0][1]; + m.m[0][2] = -matrix.m[0][2]; + m.m[0][3] = -matrix.m[0][3]; + m.m[1][0] = -matrix.m[1][0]; + m.m[1][1] = -matrix.m[1][1]; + m.m[1][2] = -matrix.m[1][2]; + m.m[1][3] = -matrix.m[1][3]; + m.m[2][0] = -matrix.m[2][0]; + m.m[2][1] = -matrix.m[2][1]; + m.m[2][2] = -matrix.m[2][2]; + m.m[2][3] = -matrix.m[2][3]; + m.m[3][0] = -matrix.m[3][0]; + m.m[3][1] = -matrix.m[3][1]; + m.m[3][2] = -matrix.m[3][2]; + m.m[3][3] = -matrix.m[3][3]; + m.flagBits = QDoubleMatrix4x4::General; + return m; +} + +inline QDoubleMatrix4x4 operator*(double factor, const QDoubleMatrix4x4& matrix) +{ + QDoubleMatrix4x4 m(1); + m.m[0][0] = matrix.m[0][0] * factor; + m.m[0][1] = matrix.m[0][1] * factor; + m.m[0][2] = matrix.m[0][2] * factor; + m.m[0][3] = matrix.m[0][3] * factor; + m.m[1][0] = matrix.m[1][0] * factor; + m.m[1][1] = matrix.m[1][1] * factor; + m.m[1][2] = matrix.m[1][2] * factor; + m.m[1][3] = matrix.m[1][3] * factor; + m.m[2][0] = matrix.m[2][0] * factor; + m.m[2][1] = matrix.m[2][1] * factor; + m.m[2][2] = matrix.m[2][2] * factor; + m.m[2][3] = matrix.m[2][3] * factor; + m.m[3][0] = matrix.m[3][0] * factor; + m.m[3][1] = matrix.m[3][1] * factor; + m.m[3][2] = matrix.m[3][2] * factor; + m.m[3][3] = matrix.m[3][3] * factor; + m.flagBits = QDoubleMatrix4x4::General; + return m; +} + +inline QDoubleMatrix4x4 operator*(const QDoubleMatrix4x4& matrix, double factor) +{ + QDoubleMatrix4x4 m(1); + m.m[0][0] = matrix.m[0][0] * factor; + m.m[0][1] = matrix.m[0][1] * factor; + m.m[0][2] = matrix.m[0][2] * factor; + m.m[0][3] = matrix.m[0][3] * factor; + m.m[1][0] = matrix.m[1][0] * factor; + m.m[1][1] = matrix.m[1][1] * factor; + m.m[1][2] = matrix.m[1][2] * factor; + m.m[1][3] = matrix.m[1][3] * factor; + m.m[2][0] = matrix.m[2][0] * factor; + m.m[2][1] = matrix.m[2][1] * factor; + m.m[2][2] = matrix.m[2][2] * factor; + m.m[2][3] = matrix.m[2][3] * factor; + m.m[3][0] = matrix.m[3][0] * factor; + m.m[3][1] = matrix.m[3][1] * factor; + m.m[3][2] = matrix.m[3][2] * factor; + m.m[3][3] = matrix.m[3][3] * factor; + m.flagBits = QDoubleMatrix4x4::General; + return m; +} + +inline bool qFuzzyCompare(const QDoubleMatrix4x4& m1, const QDoubleMatrix4x4& m2) +{ + return qFuzzyCompare(m1.m[0][0], m2.m[0][0]) && + qFuzzyCompare(m1.m[0][1], m2.m[0][1]) && + qFuzzyCompare(m1.m[0][2], m2.m[0][2]) && + qFuzzyCompare(m1.m[0][3], m2.m[0][3]) && + qFuzzyCompare(m1.m[1][0], m2.m[1][0]) && + qFuzzyCompare(m1.m[1][1], m2.m[1][1]) && + qFuzzyCompare(m1.m[1][2], m2.m[1][2]) && + qFuzzyCompare(m1.m[1][3], m2.m[1][3]) && + qFuzzyCompare(m1.m[2][0], m2.m[2][0]) && + qFuzzyCompare(m1.m[2][1], m2.m[2][1]) && + qFuzzyCompare(m1.m[2][2], m2.m[2][2]) && + qFuzzyCompare(m1.m[2][3], m2.m[2][3]) && + qFuzzyCompare(m1.m[3][0], m2.m[3][0]) && + qFuzzyCompare(m1.m[3][1], m2.m[3][1]) && + qFuzzyCompare(m1.m[3][2], m2.m[3][2]) && + qFuzzyCompare(m1.m[3][3], m2.m[3][3]); +} + +inline QPoint QDoubleMatrix4x4::map(const QPoint& point) const +{ + return *this * point; +} + +inline QPointF QDoubleMatrix4x4::map(const QPointF& point) const +{ + return *this * point; +} + +inline QDoubleVector3D QDoubleMatrix4x4::map(const QDoubleVector3D& point) const +{ + return *this * point; +} + +inline QDoubleVector3D QDoubleMatrix4x4::mapVector(const QDoubleVector3D& vector) const +{ + if (flagBits < Scale) { + // Translation + return vector; + } else if (flagBits < Rotation2D) { + // Translation | Scale + return QDoubleVector3D(vector.x() * m[0][0], + vector.y() * m[1][1], + vector.z() * m[2][2]); + } else { + return QDoubleVector3D(vector.x() * m[0][0] + + vector.y() * m[1][0] + + vector.z() * m[2][0], + vector.x() * m[0][1] + + vector.y() * m[1][1] + + vector.z() * m[2][1], + vector.x() * m[0][2] + + vector.y() * m[1][2] + + vector.z() * m[2][2]); + } +} + +inline double *QDoubleMatrix4x4::data() +{ + // We have to assume that the caller will modify the matrix elements, + // so we flip it over to "General" mode. + flagBits = General; + return *m; +} + +inline void QDoubleMatrix4x4::viewport(const QRectF &rect) +{ + viewport(rect.x(), rect.y(), rect.width(), rect.height()); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_PRIVATE_EXPORT QDebug operator<<(QDebug dbg, const QDoubleMatrix4x4 &m); +#endif + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &, const QDoubleMatrix4x4 &); +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &, QDoubleMatrix4x4 &); +#endif + + +QT_END_NAMESPACE + + +#endif // QDOUBLEMATRIX4X4_H diff --git a/src/positioning/qdoublevector2d.cpp b/src/positioning/qdoublevector2d.cpp new file mode 100644 index 0000000..c18f236 --- /dev/null +++ b/src/positioning/qdoublevector2d.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdoublevector2d_p.h" +#include "qdoublevector3d_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDoubleVector2D::QDoubleVector2D(const QDoubleVector3D &vector) : + xp(vector.xp), yp(vector.yp) +{ +} + +double QDoubleVector2D::length() const +{ + return qSqrt(xp * xp + yp * yp); +} + +QDoubleVector2D QDoubleVector2D::normalized() const +{ + // Need some extra precision if the length is very small. + double len = double(xp) * double(xp) + + double(yp) * double(yp); + if (qFuzzyIsNull(len - 1.0)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / (double)qSqrt(len); + else + return QDoubleVector2D(); +} + +void QDoubleVector2D::normalize() +{ + // Need some extra precision if the length is very small. + double len = double(xp) * double(xp) + + double(yp) * double(yp); + if (qFuzzyIsNull(len - 1.0) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + xp /= len; + yp /= len; +} + +QDoubleVector3D QDoubleVector2D::toVector3D() const +{ + return QDoubleVector3D(xp, yp, 0.0); +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QDoubleVector2D &vector) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QDoubleVector2D(" << vector.x() << ", " << vector.y() << ')'; + return dbg; +} + +#endif + +#ifndef QT_NO_DATASTREAM + +QDataStream &operator<<(QDataStream &stream, const QDoubleVector2D &vector) +{ + stream << double(vector.x()) << double(vector.y()); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QDoubleVector2D &vector) +{ + double x, y; + stream >> x; + stream >> y; + vector.setX(double(x)); + vector.setY(double(y)); + return stream; +} + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/positioning/qdoublevector2d_p.h b/src/positioning/qdoublevector2d_p.h new file mode 100644 index 0000000..92ccfe8 --- /dev/null +++ b/src/positioning/qdoublevector2d_p.h @@ -0,0 +1,262 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDOUBLEVECTOR2D_P_H +#define QDOUBLEVECTOR2D_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifdef QT_BUILD_LOCATION_LIB +#include +#endif + +#include "qpositioningglobal_p.h" +#include +#include + +QT_BEGIN_NAMESPACE + +class QDoubleVector3D; + +class Q_POSITIONING_PRIVATE_EXPORT QDoubleVector2D +{ +public: + Q_DECL_CONSTEXPR inline QDoubleVector2D(); + Q_DECL_CONSTEXPR inline QDoubleVector2D(double xpos, double ypos); + Q_DECL_CONSTEXPR explicit inline QDoubleVector2D(const QPointF &p); + explicit QDoubleVector2D(const QDoubleVector3D &vector); + + Q_DECL_CONSTEXPR inline double manhattanLength() const; + inline bool isNull() const; + inline bool isFinite() const; + + Q_DECL_CONSTEXPR inline double x() const; + Q_DECL_CONSTEXPR inline double y() const; + + inline void setX(double x); + inline void setY(double y); + + double length() const; + Q_DECL_CONSTEXPR inline double lengthSquared() const; + + QDoubleVector2D normalized() const; + void normalize(); + + inline QDoubleVector2D &operator+=(const QDoubleVector2D &vector); + inline QDoubleVector2D &operator-=(const QDoubleVector2D &vector); + inline QDoubleVector2D &operator*=(double factor); + inline QDoubleVector2D &operator*=(const QDoubleVector2D &vector); + inline QDoubleVector2D &operator/=(double divisor); + inline QDoubleVector2D &operator/=(const QDoubleVector2D &vector); + + Q_DECL_CONSTEXPR static inline double dotProduct(const QDoubleVector2D &v1, const QDoubleVector2D &v2) + { return v1.xp * v2.xp + v1.yp * v2.yp; } + + + friend Q_DECL_CONSTEXPR inline bool operator==(const QDoubleVector2D &v1, const QDoubleVector2D &v2); + friend Q_DECL_CONSTEXPR inline bool operator!=(const QDoubleVector2D &v1, const QDoubleVector2D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector2D operator+(const QDoubleVector2D &v1, const QDoubleVector2D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector2D operator-(const QDoubleVector2D &v1, const QDoubleVector2D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector2D operator*(double factor, const QDoubleVector2D &vector); + friend Q_DECL_CONSTEXPR inline const QDoubleVector2D operator*(const QDoubleVector2D &vector, double factor); + friend Q_DECL_CONSTEXPR inline const QDoubleVector2D operator*(const QDoubleVector2D &v1, const QDoubleVector2D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector2D operator-(const QDoubleVector2D &vector); + friend Q_DECL_CONSTEXPR inline const QDoubleVector2D operator/(const QDoubleVector2D &vector, double divisor); + + friend Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QDoubleVector2D &v1, const QDoubleVector2D &v2); + + QDoubleVector3D toVector3D() const; + Q_DECL_CONSTEXPR inline QPointF toPointF() const; + +private: + double xp, yp; + + friend class QDoubleVector3D; +}; + +Q_DECLARE_TYPEINFO(QDoubleVector2D, Q_MOVABLE_TYPE); + +Q_DECL_CONSTEXPR inline QDoubleVector2D::QDoubleVector2D() : xp(0.0), yp(0.0) {} + +Q_DECL_CONSTEXPR inline QDoubleVector2D::QDoubleVector2D(double xpos, double ypos) : xp(xpos), yp(ypos) {} + +Q_DECL_CONSTEXPR inline QDoubleVector2D::QDoubleVector2D(const QPointF &p) : xp(p.x()), yp(p.y()) { } + +Q_DECL_CONSTEXPR inline double QDoubleVector2D::manhattanLength() const +{ + return qAbs(x())+qAbs(y()); +} + +inline bool QDoubleVector2D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp); +} + +inline bool QDoubleVector2D::isFinite() const +{ + return qIsFinite(xp) && qIsFinite(yp); +} + +Q_DECL_CONSTEXPR inline double QDoubleVector2D::x() const { return xp; } +Q_DECL_CONSTEXPR inline double QDoubleVector2D::y() const { return yp; } + +inline void QDoubleVector2D::setX(double aX) { xp = aX; } +inline void QDoubleVector2D::setY(double aY) { yp = aY; } + +Q_DECL_CONSTEXPR inline double QDoubleVector2D::lengthSquared() const +{ return xp * xp + yp * yp; } + +inline QDoubleVector2D &QDoubleVector2D::operator+=(const QDoubleVector2D &vector) +{ + xp += vector.xp; + yp += vector.yp; + return *this; +} + +inline QDoubleVector2D &QDoubleVector2D::operator-=(const QDoubleVector2D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + return *this; +} + +inline QDoubleVector2D &QDoubleVector2D::operator*=(double factor) +{ + xp *= factor; + yp *= factor; + return *this; +} + +inline QDoubleVector2D &QDoubleVector2D::operator*=(const QDoubleVector2D &vector) +{ + xp *= vector.xp; + yp *= vector.yp; + return *this; +} + +inline QDoubleVector2D &QDoubleVector2D::operator/=(double divisor) +{ + xp /= divisor; + yp /= divisor; + return *this; +} + +inline QDoubleVector2D &QDoubleVector2D::operator/=(const QDoubleVector2D &vector) +{ + xp /= vector.xp; + yp /= vector.yp; + return *this; +} + +Q_DECL_CONSTEXPR inline bool operator==(const QDoubleVector2D &v1, const QDoubleVector2D &v2) +{ + return v1.xp == v2.xp && v1.yp == v2.yp; +} + +Q_DECL_CONSTEXPR inline bool operator!=(const QDoubleVector2D &v1, const QDoubleVector2D &v2) +{ + return v1.xp != v2.xp || v1.yp != v2.yp; +} + +Q_DECL_CONSTEXPR inline const QDoubleVector2D operator+(const QDoubleVector2D &v1, const QDoubleVector2D &v2) +{ + return QDoubleVector2D(v1.xp + v2.xp, v1.yp + v2.yp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector2D operator-(const QDoubleVector2D &v1, const QDoubleVector2D &v2) +{ + return QDoubleVector2D(v1.xp - v2.xp, v1.yp - v2.yp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector2D operator*(double factor, const QDoubleVector2D &vector) +{ + return QDoubleVector2D(vector.xp * factor, vector.yp * factor); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector2D operator*(const QDoubleVector2D &vector, double factor) +{ + return QDoubleVector2D(vector.xp * factor, vector.yp * factor); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector2D operator*(const QDoubleVector2D &v1, const QDoubleVector2D &v2) +{ + return QDoubleVector2D(v1.xp * v2.xp, v1.yp * v2.yp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector2D operator-(const QDoubleVector2D &vector) +{ + return QDoubleVector2D(-vector.xp, -vector.yp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector2D operator/(const QDoubleVector2D &vector, double divisor) +{ + return QDoubleVector2D(vector.xp / divisor, vector.yp / divisor); +} + +Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QDoubleVector2D &v1, const QDoubleVector2D &v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && qFuzzyCompare(v1.yp, v2.yp); +} + +Q_DECL_CONSTEXPR inline QPointF QDoubleVector2D::toPointF() const +{ + return QPointF(qreal(xp), qreal(yp)); +} + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_EXPORT QDebug operator<<(QDebug dbg, const QDoubleVector2D &vector); +#endif + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &, const QDoubleVector2D &); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &, QDoubleVector2D &); +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qdoublevector3d.cpp b/src/positioning/qdoublevector3d.cpp new file mode 100644 index 0000000..b308084 --- /dev/null +++ b/src/positioning/qdoublevector3d.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdoublevector3d_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDoubleVector3D QDoubleVector3D::normalized() const +{ + // Need some extra precision if the length is very small. + double len = double(xp) * double(xp) + + double(yp) * double(yp) + + double(zp) * double(zp); + if (qFuzzyIsNull(len - 1.0)) + return *this; + else if (!qFuzzyIsNull(len)) + return *this / (double)qSqrt(len); + else + return QDoubleVector3D(); +} + +void QDoubleVector3D::normalize() +{ + // Need some extra precision if the length is very small. + double len = double(xp) * double(xp) + + double(yp) * double(yp) + + double(zp) * double(zp); + if (qFuzzyIsNull(len - 1.0) || qFuzzyIsNull(len)) + return; + + len = qSqrt(len); + + xp /= len; + yp /= len; + zp /= len; +} + +QDoubleVector3D QDoubleVector3D::normal(const QDoubleVector3D &v1, const QDoubleVector3D &v2) +{ + return crossProduct(v1, v2).normalized(); +} + +QDoubleVector3D QDoubleVector3D::normal + (const QDoubleVector3D &v1, const QDoubleVector3D &v2, const QDoubleVector3D &v3) +{ + return crossProduct((v2 - v1), (v3 - v1)).normalized(); +} + +double QDoubleVector3D::distanceToPlane + (const QDoubleVector3D &plane1, const QDoubleVector3D &plane2, const QDoubleVector3D &plane3) const +{ + QDoubleVector3D n = normal(plane2 - plane1, plane3 - plane1); + return dotProduct(*this - plane1, n); +} + +double QDoubleVector3D::distanceToLine + (const QDoubleVector3D &point, const QDoubleVector3D &direction) const +{ + if (direction.isNull()) + return (*this - point).length(); + QDoubleVector3D p = point + dotProduct(*this - point, direction) * direction; + return (*this - p).length(); +} + +double QDoubleVector3D::length() const +{ + return qSqrt(xp * xp + yp * yp + zp * zp); +} + +#ifndef QT_NO_DEBUG_STREAM + +QDebug operator<<(QDebug dbg, const QDoubleVector3D &vector) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QDoubleVector3D(" + << vector.x() << ", " << vector.y() << ", " << vector.z() << ')'; + return dbg; +} + +#endif + +#ifndef QT_NO_DATASTREAM + +QDataStream &operator<<(QDataStream &stream, const QDoubleVector3D &vector) +{ + stream << double(vector.x()) << double(vector.y()) + << double(vector.z()); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QDoubleVector3D &vector) +{ + double x, y, z; + stream >> x; + stream >> y; + stream >> z; + vector.setX(double(x)); + vector.setY(double(y)); + vector.setZ(double(z)); + return stream; +} + +#endif // QT_NO_DATASTREAM + +QT_END_NAMESPACE diff --git a/src/positioning/qdoublevector3d_p.h b/src/positioning/qdoublevector3d_p.h new file mode 100644 index 0000000..059d38f --- /dev/null +++ b/src/positioning/qdoublevector3d_p.h @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDOUBLEVECTOR3D_P_H +#define QDOUBLEVECTOR3D_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifdef QT_BUILD_LOCATION_LIB +#include +#endif + +#include "qpositioningglobal_p.h" +#include "qdoublevector2d_p.h" +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_PRIVATE_EXPORT QDoubleVector3D +{ +public: + Q_DECL_CONSTEXPR inline QDoubleVector3D(); + Q_DECL_CONSTEXPR inline QDoubleVector3D(double xpos, double ypos, double zpos); + Q_DECL_CONSTEXPR inline QDoubleVector3D(const QDoubleVector2D &vector); + Q_DECL_CONSTEXPR inline QDoubleVector3D(const QDoubleVector2D &vector, double zpos); + + inline bool isNull() const; + + Q_DECL_CONSTEXPR inline double x() const; + Q_DECL_CONSTEXPR inline double y() const; + Q_DECL_CONSTEXPR inline double z() const; + + inline void setX(double x); + inline void setY(double y); + inline void setZ(double z); + + inline double get(int i) const; + inline void set(int i, double value); + + double length() const; + Q_DECL_CONSTEXPR inline double lengthSquared() const; + + QDoubleVector3D normalized() const; + void normalize(); + + inline QDoubleVector3D &operator+=(const QDoubleVector3D &vector); + inline QDoubleVector3D &operator-=(const QDoubleVector3D &vector); + inline QDoubleVector3D &operator*=(double factor); + inline QDoubleVector3D &operator*=(const QDoubleVector3D &vector); + inline QDoubleVector3D &operator/=(double divisor); + + Q_DECL_CONSTEXPR static inline double dotProduct(const QDoubleVector3D &v1, const QDoubleVector3D &v2) + { return v1.xp * v2.xp + v1.yp * v2.yp + v1.zp * v2.zp; } + + Q_DECL_CONSTEXPR static inline QDoubleVector3D crossProduct(const QDoubleVector3D &v1, const QDoubleVector3D &v2) + { return QDoubleVector3D(v1.yp * v2.zp - v1.zp * v2.yp, + v1.zp * v2.xp - v1.xp * v2.zp, + v1.xp * v2.yp - v1.yp * v2.xp); } + + static QDoubleVector3D normal(const QDoubleVector3D &v1, const QDoubleVector3D &v2); + static QDoubleVector3D normal + (const QDoubleVector3D &v1, const QDoubleVector3D &v2, const QDoubleVector3D &v3); + + double distanceToPlane(const QDoubleVector3D &plane, const QDoubleVector3D &normal) const; + double distanceToPlane(const QDoubleVector3D &plane1, const QDoubleVector3D &plane2, const QDoubleVector3D &plane3) const; + double distanceToLine(const QDoubleVector3D &point, const QDoubleVector3D &direction) const; + + friend Q_DECL_CONSTEXPR inline bool operator==(const QDoubleVector3D &v1, const QDoubleVector3D &v2); + friend Q_DECL_CONSTEXPR inline bool operator!=(const QDoubleVector3D &v1, const QDoubleVector3D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector3D operator+(const QDoubleVector3D &v1, const QDoubleVector3D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector3D operator-(const QDoubleVector3D &v1, const QDoubleVector3D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector3D operator*(double factor, const QDoubleVector3D &vector); + friend Q_DECL_CONSTEXPR inline const QDoubleVector3D operator*(const QDoubleVector3D &vector, double factor); + friend Q_DECL_CONSTEXPR inline const QDoubleVector3D operator*(const QDoubleVector3D &v1, const QDoubleVector3D &v2); + friend Q_DECL_CONSTEXPR inline const QDoubleVector3D operator-(const QDoubleVector3D &vector); + friend Q_DECL_CONSTEXPR inline const QDoubleVector3D operator/(const QDoubleVector3D &vector, double divisor); + + friend Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QDoubleVector3D &v1, const QDoubleVector3D &v2); + + Q_DECL_CONSTEXPR inline QDoubleVector2D toVector2D() const; + +private: + double xp, yp, zp; + + friend class QDoubleVector2D; +}; + +Q_DECLARE_TYPEINFO(QDoubleVector3D, Q_MOVABLE_TYPE); + +Q_DECL_CONSTEXPR inline QDoubleVector3D::QDoubleVector3D() : xp(0.0), yp(0.0), zp(0.0) {} + +Q_DECL_CONSTEXPR inline QDoubleVector3D::QDoubleVector3D(double xpos, double ypos, double zpos) : xp(xpos), yp(ypos), zp(zpos) {} + +Q_DECL_CONSTEXPR inline QDoubleVector3D::QDoubleVector3D(const QDoubleVector2D &v) + : xp(v.xp), yp(v.yp), zp(0.0) {} + +Q_DECL_CONSTEXPR inline QDoubleVector3D::QDoubleVector3D(const QDoubleVector2D &v, double zpos) + : xp(v.xp), yp(v.yp), zp(zpos) {} + +inline bool QDoubleVector3D::isNull() const +{ + return qIsNull(xp) && qIsNull(yp) && qIsNull(zp); +} + +Q_DECL_CONSTEXPR inline double QDoubleVector3D::x() const { return xp; } +Q_DECL_CONSTEXPR inline double QDoubleVector3D::y() const { return yp; } +Q_DECL_CONSTEXPR inline double QDoubleVector3D::z() const { return zp; } + +Q_DECL_CONSTEXPR inline double QDoubleVector3D::lengthSquared() const +{ return xp * xp + yp * yp + zp * zp; } + + +inline void QDoubleVector3D::setX(double aX) { xp = aX; } +inline void QDoubleVector3D::setY(double aY) { yp = aY; } +inline void QDoubleVector3D::setZ(double aZ) { zp = aZ; } + +inline double QDoubleVector3D::get(int i) const +{ + switch (i) { + case 0: + return xp; + case 1: + return yp; + case 2: + return zp; + default: + return 0.0; + } +} + +inline void QDoubleVector3D::set(int i, double value) +{ + switch (i) { + case 0: + xp = value; + break; + case 1: + yp = value; + break; + case 2: + zp = value; + break; + default: + break; + } +} + +inline QDoubleVector3D &QDoubleVector3D::operator+=(const QDoubleVector3D &vector) +{ + xp += vector.xp; + yp += vector.yp; + zp += vector.zp; + return *this; +} + +inline QDoubleVector3D &QDoubleVector3D::operator-=(const QDoubleVector3D &vector) +{ + xp -= vector.xp; + yp -= vector.yp; + zp -= vector.zp; + return *this; +} + +inline QDoubleVector3D &QDoubleVector3D::operator*=(double factor) +{ + xp *= factor; + yp *= factor; + zp *= factor; + return *this; +} + +inline QDoubleVector3D &QDoubleVector3D::operator*=(const QDoubleVector3D &vector) +{ + xp *= vector.xp; + yp *= vector.yp; + zp *= vector.zp; + return *this; +} + +inline QDoubleVector3D &QDoubleVector3D::operator/=(double divisor) +{ + xp /= divisor; + yp /= divisor; + zp /= divisor; + return *this; +} + +Q_DECL_CONSTEXPR inline bool operator==(const QDoubleVector3D &v1, const QDoubleVector3D &v2) +{ + return v1.xp == v2.xp && v1.yp == v2.yp && v1.zp == v2.zp; +} + +Q_DECL_CONSTEXPR inline bool operator!=(const QDoubleVector3D &v1, const QDoubleVector3D &v2) +{ + return v1.xp != v2.xp || v1.yp != v2.yp || v1.zp != v2.zp; +} + +Q_DECL_CONSTEXPR inline const QDoubleVector3D operator+(const QDoubleVector3D &v1, const QDoubleVector3D &v2) +{ + return QDoubleVector3D(v1.xp + v2.xp, v1.yp + v2.yp, v1.zp + v2.zp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector3D operator-(const QDoubleVector3D &v1, const QDoubleVector3D &v2) +{ + return QDoubleVector3D(v1.xp - v2.xp, v1.yp - v2.yp, v1.zp - v2.zp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector3D operator*(double factor, const QDoubleVector3D &vector) +{ + return QDoubleVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector3D operator*(const QDoubleVector3D &vector, double factor) +{ + return QDoubleVector3D(vector.xp * factor, vector.yp * factor, vector.zp * factor); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector3D operator*(const QDoubleVector3D &v1, const QDoubleVector3D &v2) +{ + return QDoubleVector3D(v1.xp * v2.xp, v1.yp * v2.yp, v1.zp * v2.zp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector3D operator-(const QDoubleVector3D &vector) +{ + return QDoubleVector3D(-vector.xp, -vector.yp, -vector.zp); +} + +Q_DECL_CONSTEXPR inline const QDoubleVector3D operator/(const QDoubleVector3D &vector, double divisor) +{ + return QDoubleVector3D(vector.xp / divisor, vector.yp / divisor, vector.zp / divisor); +} + +Q_DECL_CONSTEXPR inline bool qFuzzyCompare(const QDoubleVector3D &v1, const QDoubleVector3D &v2) +{ + return qFuzzyCompare(v1.xp, v2.xp) && + qFuzzyCompare(v1.yp, v2.yp) && + qFuzzyCompare(v1.zp, v2.zp); +} + +Q_DECL_CONSTEXPR inline QDoubleVector2D QDoubleVector3D::toVector2D() const +{ + return QDoubleVector2D(xp, yp); +} + + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_EXPORT QDebug operator<<(QDebug dbg, const QDoubleVector3D &vector); +#endif + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &, const QDoubleVector3D &); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &, QDoubleVector3D &); +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeoaddress.cpp b/src/positioning/qgeoaddress.cpp new file mode 100644 index 0000000..6f08f2d --- /dev/null +++ b/src/positioning/qgeoaddress.cpp @@ -0,0 +1,635 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoaddress.h" +#include "qgeoaddress_p.h" + +#include + +#ifdef QGEOADDRESS_DEBUG +#include +#endif + +QT_BEGIN_NAMESPACE + +/* + Combines a list of address parts into a single line. + + The parts parameter contains both address elements such as city, state and so on + as well as separators such as spaces and commas. + + It is expected that an element is always followed by a separator and the last + sepator is usually a new line delimeter. + + For example: Springfield, 8900 + would have four parts + ["Springfield", ", ", "8900", "
    "] + + The addressLine takes care of putting in separators appropriately or leaving + them out depending on whether the adjacent elements are present or not. + For example if city were empty in the above scenario the returned string is "8900
    " + If the postal code was empty, returned string is "Springfield
    " + If both city and postal code were empty, the returned string is "". +*/ +static QString addressLine(const QStringList &parts) +{ + QString line; + Q_ASSERT(parts.count() % 2 == 0); + + //iterate until just before the last pair + QString penultimateSeparator; + for (int i = 0; i < parts.count() - 2; i += 2) { + if (!parts.at(i).isEmpty()) { + line.append(parts.at(i) + parts.at(i + 1)); + penultimateSeparator = parts.at(i + 1); + } + } + + if (parts.at(parts.count() - 2).isEmpty()) { + line.chop(penultimateSeparator.length()); + + if (!line.isEmpty()) + line.append(parts.at(parts.count() - 1)); + } else { + line.append(parts.at(parts.count() - 2)); + line.append(parts.at(parts.count() - 1)); + } + + return line; +} + +/* + Returns a single formatted string representing the \a address. Lines of the address + are delimited by \a newLine. By default lines are delimited by
    . The \l + {QGeoAddress::countryCode} {countryCode} of the \a address determines the format of + the resultant string. +*/ +static QString formattedAddress(const QGeoAddress &address, + const QString &newLine = QLatin1String("
    ")) +{ + const QString Comma(QStringLiteral(", ")); + const QString Dash(QStringLiteral("-")); + const QString Space(QStringLiteral(" ")); + + QString text; + + if (address.countryCode() == QLatin1String("ALB") + || address.countryCode() == QLatin1String("MTQ")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.postalCode() << Comma + << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("AND") + || address.countryCode() == QLatin1String("AUT") + || address.countryCode() == QLatin1String("FRA") + || address.countryCode() == QLatin1String("GLP") + || address.countryCode() == QLatin1String("GUF") + || address.countryCode() == QLatin1String("ITA") + || address.countryCode() == QLatin1String("LUX") + || address.countryCode() == QLatin1String("MCO") + || address.countryCode() == QLatin1String("REU") + || address.countryCode() == QLatin1String("RUS") + || address.countryCode() == QLatin1String("SMR") + || address.countryCode() == QLatin1String("VAT")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.postalCode() << Space + << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("ARE") + || address.countryCode() == QLatin1String("BHS")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Space + << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("AUS")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << (address.district().isEmpty() ? address.city() : address.district()) + << Space << address.state() << Space << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("BHR")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Comma + << address.city() << Comma << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("BRA")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Space + << address.city() << Dash << address.state() << Space << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("BRN") + || address.countryCode() == QLatin1String("JOR") + || address.countryCode() == QLatin1String("LBN") + || address.countryCode() == QLatin1String("NZL")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Space + << address.city() << Space << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("CAN") + || address.countryCode() == QLatin1String("USA") + || address.countryCode() == QLatin1String("VIR")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.city() << Comma << address.state() << Space + << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("CHN")) { + text += addressLine(QStringList() << address.street() << Comma << address.city() << newLine); + text += addressLine(QStringList() << address.postalCode() << Space << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("CHL")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.postalCode() << Space + << address.district() << Comma << address.city() << Comma + << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("CYM")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.state() << Space + << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("GBR")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Comma + << address.city() << Comma << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("GIB")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("HKG")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << newLine); + text += addressLine(QStringList() << address.city() << newLine); + } else if (address.countryCode() == QLatin1String("IND")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.city() << Space << address.postalCode() << Space + << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("IDN") + || address.countryCode() == QLatin1String("JEY") + || address.countryCode() == QLatin1String("LVA")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.city() << Comma << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("IRL")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Comma << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("KWT")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.postalCode() << Comma + << address.district() << Comma << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("MLT") + || address.countryCode() == QLatin1String("SGP") + || address.countryCode() == QLatin1String("UKR")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.city() << Space << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("MEX")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << newLine); + text += addressLine(QStringList() << address.postalCode() << Space << address.city() << Comma + << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("MYS")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.postalCode() << Space << address.city() << newLine); + text += addressLine(QStringList() << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("OMN")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Comma + << address.postalCode() << Comma + << address.city() << Comma + << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("PRI")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Comma << address.city() << Comma + << address.state() << Comma << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("QAT")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Space << address.city() << Comma + << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("SAU")) { + text += addressLine(QStringList() << address.street() << Space << address.district() << newLine); + text += addressLine(QStringList() << address.city() << Space << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("TWN")) { + text += addressLine(QStringList() << address.street() << Comma + << address.district() << Comma << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("THA")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Comma << address.city() << Space + << address.postalCode() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("TUR")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.postalCode() << Space << address.district() << Comma + << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("VEN")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.city() << Space << address.postalCode() << Comma + << address.state() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else if (address.countryCode() == QLatin1String("ZAF")) { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.district() << Comma << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } else { + text += addressLine(QStringList() << address.street() << newLine); + text += addressLine(QStringList() << address.postalCode() << Space << address.city() << newLine); + text += addressLine(QStringList() << address.country() << newLine); + } + + text.chop(newLine.length()); + return text; +} + +QGeoAddressPrivate::QGeoAddressPrivate() + : QSharedData(), + m_autoGeneratedText(false) +{ +} + +QGeoAddressPrivate::QGeoAddressPrivate(const QGeoAddressPrivate &other) + : QSharedData(other), + sCountry(other.sCountry), + sCountryCode(other.sCountryCode), + sState(other.sState), + sCounty(other.sCounty), + sCity(other.sCity), + sDistrict(other.sDistrict), + sStreet(other.sStreet), + sPostalCode(other.sPostalCode), + sText(other.sText), + m_autoGeneratedText(false) +{ +} + +QGeoAddressPrivate::~QGeoAddressPrivate() +{ +} + +/*! + \class QGeoAddress + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \ingroup QtLocation-places-data + \ingroup QtLocation-places + \since 5.2 + + \brief The QGeoAddress class represents an address of a \l QGeoLocation. + + The address' attributes are normalized to US feature names and can be mapped + to the local feature levels (for example State matches "Bundesland" in Germany). + + The address contains a \l text() for displaying purposes and additional + properties to access the components of an address: + + \list + \li QGeoAddress::country() + \li QGeoAddress::countryCode() + \li QGeoAddress::state() + \li QGeoAddress::city() + \li QGeoAddress::district() + \li QGeoAddress::street() + \li QGeoAddress::postalCode() + \endlist +*/ + +/*! + Default constructor. +*/ +QGeoAddress::QGeoAddress() + : d(new QGeoAddressPrivate) +{ +} + +/*! + Constructs a copy of \a other. +*/ +QGeoAddress::QGeoAddress(const QGeoAddress &other) + : d(other.d) +{ +} + +/*! + Destroys this address. +*/ +QGeoAddress::~QGeoAddress() +{ +} + +/*! + Assigns the given \a address to this address and + returns a reference to this address. +*/ +QGeoAddress &QGeoAddress::operator=(const QGeoAddress & address) +{ + if (this == &address) + return *this; + + d = address.d; + return *this; +} + +/*! + Returns true if this address is equal to \a other, + otherwise returns false. +*/ +bool QGeoAddress::operator==(const QGeoAddress &other) const +{ +#ifdef QGEOADDRESS_DEBUG + qDebug() << "country" << (d->sCountry == other.country()); + qDebug() << "countryCode" << (d->sCountryCode == other.countryCode()); + qDebug() << "state:" << (d->sState == other.state()); + qDebug() << "county:" << (d->sCounty == other.county()); + qDebug() << "city:" << (d->sCity == other.city()); + qDebug() << "district:" << (d->sDistrict == other.district()); + qDebug() << "street:" << (d->sStreet == other.street()); + qDebug() << "postalCode:" << (d->sPostalCode == other.postalCode()); + qDebug() << "text:" << (text() == other.text()); +#endif + + return d->sCountry == other.country() && + d->sCountryCode == other.countryCode() && + d->sState == other.state() && + d->sCounty == other.county() && + d->sCity == other.city() && + d->sDistrict == other.district() && + d->sStreet == other.street() && + d->sPostalCode == other.postalCode() && + this->text() == other.text(); +} + +/*! + \fn bool QGeoAddress::operator!=(const QGeoAddress &other) const + + Returns true if this address is not equal to \a other, + otherwise returns false. +*/ + +/*! + Returns the address as a single formatted string. It is the recommended string + to use to display the address to the user. It typically takes the format of + an address as found on an envelope, but this is not always necessarily the case. + + The address text is either automatically generated or explicitly assigned. + This can be determined by checking \l {QGeoAddress::isTextGenerated()} {isTextGenerated}. + + If an empty string is provided to setText(), then isTextGenerated() will be set + to true and text() will return a string which is locally formatted according to + countryCode() and based on the elements of the address such as street, city and so on. + Because the text string is generated from the address elements, a sequence + of calls such as text(), setStreet(), text() may return different strings for each + invocation of text(). + + If a non-empty string is provided to setText(), then isTextGenerated() will be + set to false and text() will always return the explicitly assigned string. + Calls to modify other elements such as setStreet(), setCity() and so on will not + affect the resultant string from text(). +*/ +QString QGeoAddress::text() const +{ + if (d->sText.isEmpty()) + return formattedAddress(*this); + else + return d->sText; +} + +/*! + If \a text is not empty, explicitly assigns \a text as the string to be returned by + text(). isTextGenerated() will return false. + + If \a text is empty, indicates that text() should be automatically generated + from the address elements. isTextGenerated() will return true. +*/ +void QGeoAddress::setText(const QString &text) +{ + d->sText = text; +} + +/*! + Returns the country name. +*/ +QString QGeoAddress::country() const +{ + return d->sCountry; +} + +/*! + Sets the \a country name. +*/ +void QGeoAddress::setCountry(const QString &country) +{ + d->sCountry = country; +} + +/*! + Returns the country code according to ISO 3166-1 alpha-3 +*/ +QString QGeoAddress::countryCode() const +{ + return d->sCountryCode; +} + +/*! + Sets the \a countryCode according to ISO 3166-1 alpha-3 +*/ +void QGeoAddress::setCountryCode(const QString &countryCode) +{ + d->sCountryCode = countryCode; +} + +/*! + Returns the state. The state is considered the first subdivision below country. +*/ +QString QGeoAddress::state() const +{ + return d->sState; +} + +/*! + Sets the \a state. +*/ +void QGeoAddress::setState(const QString &state) +{ + d->sState = state; +} + +/*! + Returns the county. The county is considered the second subdivision below country. +*/ +QString QGeoAddress::county() const +{ + return d->sCounty; +} + +/*! + Sets the \a county. +*/ +void QGeoAddress::setCounty(const QString &county) +{ + d->sCounty = county; +} + +/*! + Returns the city. +*/ +QString QGeoAddress::city() const +{ + return d->sCity; +} + +/*! + Sets the \a city. +*/ +void QGeoAddress::setCity(const QString &city) +{ + d->sCity = city; +} + +/*! + Returns the district. The district is considered the subdivison below city. +*/ +QString QGeoAddress::district() const +{ + return d->sDistrict; +} + +/*! + Sets the \a district. +*/ +void QGeoAddress::setDistrict(const QString &district) +{ + d->sDistrict = district; +} + +/*! + Returns the street-level component of the address. + + This typically includes a street number and street name + but may also contain things like a unit number, a building + name, or anything else that might be used to + distinguish one address from another. +*/ +QString QGeoAddress::street() const +{ + return d->sStreet; +} + +/*! + Sets the street-level component of the address to \a street. + + This typically includes a street number and street name + but may also contain things like a unit number, a building + name, or anything else that might be used to + distinguish one address from another. +*/ +void QGeoAddress::setStreet(const QString &street) +{ + d->sStreet = street; +} + +/*! + Returns the postal code. +*/ +QString QGeoAddress::postalCode() const +{ + return d->sPostalCode; +} + +/*! + Sets the \a postalCode. +*/ +void QGeoAddress::setPostalCode(const QString &postalCode) +{ + d->sPostalCode = postalCode; +} + +/*! + Returns whether this address is empty. An address is considered empty + if \e all of its fields are empty. +*/ +bool QGeoAddress::isEmpty() const +{ + return d->sCountry.isEmpty() && + d->sCountryCode.isEmpty() && + d->sState.isEmpty() && + d->sCounty.isEmpty() && + d->sCity.isEmpty() && + d->sDistrict.isEmpty() && + d->sStreet.isEmpty() && + d->sPostalCode.isEmpty() && + d->sText.isEmpty(); + +} + +/*! + Clears all of the address' data fields. +*/ +void QGeoAddress::clear() +{ + d->sCountry.clear(); + d->sCountryCode.clear(); + d->sState.clear(); + d->sCounty.clear(); + d->sCity.clear(); + d->sDistrict.clear(); + d->sStreet.clear(); + d->sPostalCode.clear(); + d->sText.clear(); +} + +/*! + Returns true if QGeoAddress::text() is automatically generated from address elements, + otherwise returns false if text() has been explicitly assigned. + + \sa text(), setText() +*/ +bool QGeoAddress::isTextGenerated() const +{ + return d->sText.isEmpty(); +} + +QT_END_NAMESPACE + diff --git a/src/positioning/qgeoaddress.h b/src/positioning/qgeoaddress.h new file mode 100644 index 0000000..6231274 --- /dev/null +++ b/src/positioning/qgeoaddress.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOADDRESS_H +#define QGEOADDRESS_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QString; +class QGeoAddressPrivate; +class Q_POSITIONING_EXPORT QGeoAddress +{ +public: + QGeoAddress(); + QGeoAddress(const QGeoAddress &other); + ~QGeoAddress(); + + QGeoAddress &operator=(const QGeoAddress &other); + bool operator==(const QGeoAddress &other) const; + bool operator!=(const QGeoAddress &other) const { + return !(other == *this); + } + + QString text() const; + void setText(const QString &text); + + QString country() const; + void setCountry(const QString &country); + + QString countryCode() const; + void setCountryCode(const QString &countryCode); + + QString state() const; + void setState(const QString &state); + + QString county() const; + void setCounty(const QString &county); + + QString city() const; + void setCity(const QString &city); + + QString district() const; + void setDistrict(const QString &district); + + QString postalCode() const; + void setPostalCode(const QString &postalCode); + + QString street() const; + void setStreet(const QString &street); + + bool isEmpty() const; + void clear(); + + bool isTextGenerated() const; + +private: + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(QGeoAddress, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoAddress) + +#endif diff --git a/src/positioning/qgeoaddress_p.h b/src/positioning/qgeoaddress_p.h new file mode 100644 index 0000000..ca0897e --- /dev/null +++ b/src/positioning/qgeoaddress_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QLOCATION_GEOADDRESS_P_H +#define QLOCATION_GEOADDRESS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoAddressPrivate : public QSharedData +{ +public: + QGeoAddressPrivate(); + QGeoAddressPrivate(const QGeoAddressPrivate &other); + ~QGeoAddressPrivate(); + + QString sCountry; //!< country field + QString sCountryCode; //!< country code field + QString sState; //!< state field + QString sCounty; //!< county field + QString sCity; //!< city field + QString sDistrict; //!< district field + QString sStreet; //!< street name field + QString sPostalCode; //!< postal code field + QString sText; + bool m_autoGeneratedText; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeoareamonitorinfo.cpp b/src/positioning/qgeoareamonitorinfo.cpp new file mode 100644 index 0000000..5d39dee --- /dev/null +++ b/src/positioning/qgeoareamonitorinfo.cpp @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#ifndef QT_NO_DEBUG_STREAM +#include +#endif + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoAreaMonitorInfo + \inmodule QtPositioning + \since 5.2 + \ingroup QtPositioning-positioning + + \brief The QGeoAreaMonitorInfo class describes the parameters of an area or region + to be monitored for proximity. + + The purpose of area monitoring is to inform a user when he/she comes close to an area of + interest. In general such an area is described by a \l QGeoCircle. The circle's center + represents the place of interest and the area around it identifies the geographical region + within which notifications are sent. + + A QGeoAreaMonitorInfo object is valid if it has a non-empty name and a valid \l area(). + Such objects must be registered with a \l QGeoAreaMonitorSource to start and stop the + monitoring process. Note that extensive monitoring can be very resource consuming + because the positioning engine must remain active and has to match the current position + with each QGeoAreaMonitorInfo instance. + + To further reduce the burden on the system there are optional attributes which can + set. Each monitored area can have an expiry date which automatically removes the + to-be-monitored area from the monitoring source once the expiry date has been reached. + Another option is to adjust the persistence of a monitored area. A QGeoAreaMonitorInfo + that \l isPersistent() will remain active beyond + the current applications lifetime. If an area is entered while the monitoring + application is not running the application will be started. Note that this feature is + not available on all platforms. Its availability can be checked via + \l QGeoAreaMonitorSource::supportedAreaMonitorFeatures(). + + \sa QGeoAreaMonitorSource + + */ + +class QGeoAreaMonitorInfoPrivate : public QSharedData +{ +public: + QGeoAreaMonitorInfoPrivate() : QSharedData(), persistent(false) {} + QGeoAreaMonitorInfoPrivate(const QGeoAreaMonitorInfoPrivate &other) + : QSharedData(other) + { + uid = other.uid; + name = other.name; + shape = other.shape; + persistent = other.persistent; + notificationParameters = other.notificationParameters; + expiry = other.expiry; + } + ~QGeoAreaMonitorInfoPrivate() {} + + QUuid uid; + QString name; + QGeoShape shape; + bool persistent; + QVariantMap notificationParameters; + QDateTime expiry; +}; + +/*! + Constructs a QGeoAreaMonitorInfo object with the specified \a name. + + \sa name() + */ +QGeoAreaMonitorInfo::QGeoAreaMonitorInfo(const QString &name) +{ + d = new QGeoAreaMonitorInfoPrivate; + d->name = name; + d->uid = QUuid::createUuid(); +} + +/*! + Constructs a QGeoAreaMonitorInfo object as a copy of \a other. + */ +QGeoAreaMonitorInfo::QGeoAreaMonitorInfo(const QGeoAreaMonitorInfo &other) + : d(other.d) +{ +} + +/*! + Destructor + */ +QGeoAreaMonitorInfo::~QGeoAreaMonitorInfo() +{ +} + +/*! + Assigns \a other to this QGeoAreaMonitorInfo object and returns a reference + to this QGeoAreaMonitorInfo object. + */ +QGeoAreaMonitorInfo &QGeoAreaMonitorInfo::operator=(const QGeoAreaMonitorInfo &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if all of this object's values are the same as those of + \a other. +*/ +bool QGeoAreaMonitorInfo::operator==(const QGeoAreaMonitorInfo &other) const +{ + return (d->name == other.d->name && + d->uid == other.d->uid && + d->shape == other.d->shape && + d->persistent == other.d->persistent && + d->expiry == other.d->expiry && + d->notificationParameters == other.d->notificationParameters); +} + +/*! + Returns true if any of this object's values are not the same as those of + \a other. +*/ +bool QGeoAreaMonitorInfo::operator!=(const QGeoAreaMonitorInfo &other) const +{ + return !QGeoAreaMonitorInfo::operator ==(other); +} + +/*! + Returns the name of the QGeoAreaMonitorInfo object. The name should be used to + for user-visibility purposes. + */ +QString QGeoAreaMonitorInfo::name() const +{ + return d->name; +} + +/*! + Sets the user visibile \a name. + */ +void QGeoAreaMonitorInfo::setName(const QString &name) +{ + if (d->name != name) + d->name = name; +} + +/*! + Returns the identifier of the QGeoAreaMonitorInfo object. + The identifier is automatically generated upon construction of a new + QGeoAreaMonitorInfo object. +*/ + +QString QGeoAreaMonitorInfo::identifier() const +{ + return d->uid.toString(); +} + +/*! + Returns true, if the monitor is valid. A valid QGeoAreaMonitorInfo has a non-empty name() + and the monitored area is not \l {QGeoShape::isEmpty()}{empty()}. + Otherwise this function returns false. + */ +bool QGeoAreaMonitorInfo::isValid() const +{ + return (!d->name.isEmpty() && !d->shape.isEmpty()); +} + +/*! + Returns the boundaries of the to-be-monitored area. This area must not be empty. + + \sa setArea() + */ +QGeoShape QGeoAreaMonitorInfo::area() const +{ + return d->shape; +} + +/*! + Sets the to-be-monitored area to \a newShape. + + \sa area() + */ +void QGeoAreaMonitorInfo::setArea(const QGeoShape &newShape) +{ + d->shape = newShape; +} + +/*! + Returns the expiry date. + + After an active QGeoAreaMonitorInfo has expired the region is no longer monitored + and the QGeoAreaMonitorInfo object is removed from the list of + \l {QGeoAreaMonitorSource::activeMonitors()}{active monitors}. + + If the expiry \l QDateTime is invalid the QGeoAreaMonitorInfo object is treated as not having + an expiry date. This implies an indefinite monitoring period if the object is persistent or + until the current application closes if the object is non-persistent. + + \sa QGeoAreaMonitorSource::activeMonitors() + */ +QDateTime QGeoAreaMonitorInfo::expiration() const +{ + return d->expiry; +} + +/*! + Sets the expiry date and time to \a expiry. + */ +void QGeoAreaMonitorInfo::setExpiration(const QDateTime &expiry) +{ + d->expiry = expiry; +} + +/*! + Returns true if the QGeoAreaMonitorInfo is persistent. + The default value for this property is false. + + A non-persistent QGeoAreaMonitorInfo will be removed by the system once + the application owning the monitor object stops. Persistent objects remain + active and can be retrieved once the application restarts. + + If the system triggers an event associated to a persistent QGeoAreaMonitorInfo + the relevant application will be re-started and the appropriate signal emitted. + + \sa setPersistent() + */ +bool QGeoAreaMonitorInfo::isPersistent() const +{ + return d->persistent; +} + +/*! + Sets the QGeoAreaMonitorInfo objects persistence to \a isPersistent. + + Note that setting this flag does not imply that QGeoAreaMonitorInfoSource supports persistent + monitoring. \l QGeoAreaMonitorSource::supportedAreaMonitorFeatures() can be used to + check for this feature's availability. + + \sa isPersistent() + */ +void QGeoAreaMonitorInfo::setPersistent(bool isPersistent) +{ + d->persistent = isPersistent; +} + + +/*! + Returns the set of platform specific paraemters used by this QGeoAreaMonitorInfo. + + \sa setNotificationParameters() + */ +QVariantMap QGeoAreaMonitorInfo::notificationParameters() const +{ + return d->notificationParameters; +} + +/*! + Sets the set of platform specific \a parameters used by QGeoAreaMonitorInfo. + + \sa notificationParameters() + */ +void QGeoAreaMonitorInfo::setNotificationParameters(const QVariantMap ¶meters) +{ + d->notificationParameters = parameters; +} + +#ifndef QT_NO_DATASTREAM + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QGeoAreaMonitorInfo &monitor) + \relates QGeoAreaMonitorInfo + + Writes the given \a monitor to the specified \a stream. + + \sa {Serializing Qt Data Types} +*/ +QDataStream &operator<<(QDataStream &ds, const QGeoAreaMonitorInfo &monitor) +{ + ds << monitor.name() << monitor.d->uid << monitor.area() + << monitor.isPersistent() << monitor.notificationParameters() << monitor.expiration(); + return ds; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QGeoAreaMonitorInfo &monitor) + \relates QGeoAreaMonitorInfo + + Reads a area monitoring data from the specified \a stream into the given + \a monitor. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator>>(QDataStream &ds, QGeoAreaMonitorInfo &monitor) +{ + QString s; + ds >> s; + monitor = QGeoAreaMonitorInfo(s); + + QUuid id; + ds >> id; + monitor.d->uid = id; + + QGeoShape shape; + ds >> shape; + monitor.setArea(shape); + + bool persistent; + ds >> persistent; + monitor.setPersistent(persistent); + + QVariantMap map; + ds >> map; + monitor.setNotificationParameters(map); + + QDateTime dt; + ds >> dt; + monitor.setExpiration(dt); + + return ds; +} + +#endif + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGeoAreaMonitorInfo &monitor) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QGeoAreaMonitorInfo(\"" << qPrintable(monitor.name()) + << "\", " << monitor.area() + << ", persistent: " << monitor.isPersistent() + << ", expiry: " << monitor.expiration() << ")"; + return dbg; +} + +#endif + +QT_END_NAMESPACE diff --git a/src/positioning/qgeoareamonitorinfo.h b/src/positioning/qgeoareamonitorinfo.h new file mode 100644 index 0000000..ae65d2c --- /dev/null +++ b/src/positioning/qgeoareamonitorinfo.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOAREAMONITORINFO_H +#define QGEOAREAMONITORINFO_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDataStream; +class QGeoAreaMonitorInfo; + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &, const QGeoAreaMonitorInfo &); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &, QGeoAreaMonitorInfo &); +#endif + +class QGeoAreaMonitorInfoPrivate; +class Q_POSITIONING_EXPORT QGeoAreaMonitorInfo +{ +public: + explicit QGeoAreaMonitorInfo(const QString &name = QString()); + QGeoAreaMonitorInfo(const QGeoAreaMonitorInfo &other); + ~QGeoAreaMonitorInfo(); + + QGeoAreaMonitorInfo &operator=(const QGeoAreaMonitorInfo &other); + + bool operator==(const QGeoAreaMonitorInfo &other) const; + bool operator!=(const QGeoAreaMonitorInfo &other) const; + + QString name() const; + void setName(const QString &name); + + QString identifier() const; + bool isValid() const; + + QGeoShape area() const; + void setArea(const QGeoShape &newShape); + + QDateTime expiration() const; + void setExpiration(const QDateTime &expiry); + + bool isPersistent() const; + void setPersistent(bool isPersistent); + + QVariantMap notificationParameters() const; + void setNotificationParameters(const QVariantMap ¶meters); +private: + QSharedDataPointer d; + +#ifndef QT_NO_DATASTREAM + friend Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &, const QGeoAreaMonitorInfo &); + friend Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &, QGeoAreaMonitorInfo &); +#endif +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_EXPORT QDebug operator<<(QDebug, const QGeoAreaMonitorInfo &); +#endif + +QT_END_NAMESPACE + +#endif // QGEOAREAMONITORINFO_H diff --git a/src/positioning/qgeoareamonitorsource.cpp b/src/positioning/qgeoareamonitorsource.cpp new file mode 100644 index 0000000..1db9c74 --- /dev/null +++ b/src/positioning/qgeoareamonitorsource.cpp @@ -0,0 +1,389 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qgeopositioninfosourcefactory.h" +#include "qgeopositioninfosource_p.h" + +/*! + \class QGeoAreaMonitorSource + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoAreaMonitorSource class enables the detection of proximity + changes for a specified set of coordinates. + + A QGeoAreaMonitorSource emits signals when the current position is in + range, or has moved out of range, of a specified area. + Each area is specified by a \l QGeoAreaMonitorInfo object. + For example: + + \snippet cpp/cppqml.cpp BigBen + + \c QGeoAreaMonitorSource follows a singleton pattern. Each instance of + the class with the same \l sourceName() shares the same area monitoring backend. + If a new \l QGeoAreaMonitorInfo object is added via \l startMonitoring() + or \l requestUpdate() it can be retrieved by another instance of this class + (provided that they are sourced from the same area monitor provider plug-in). + The same singleton pattern applies to the \l QGeoPositionInfoSource instance + used by this class. The following code snippet emphasizes the behavior: + + \code + QGeoAreaMonitorSource *s1 = QGeoAreaMonitorSource::createSource("blah", this); + QGeoAreaMonitorSource *s2 = QGeoAreaMonitorSource::createSource("blah", this); + QVERIFY(s1->positionInfoSource() == s2->positionInfoSource); + \endcode +*/ + +QT_BEGIN_NAMESPACE + + + +class QGeoAreaMonitorSourcePrivate +{ +public: + QGeoPositionInfoSource *source; + QString providerName; +}; + +/*! + \enum QGeoAreaMonitorSource::Error + Defines the types of positioning methods. + + The Error enumeration represents the errors which can occur. + + \value AccessError The connection setup to the remote area monitoring backend failed because the + application lacked the required privileges. + \value InsufficientPositionInfo The area monitoring source could not retrieve a location fix or + the accuracy of the fix is not high enough to provide an effective area monitoring. + \value NoError No error has occurred. + \value UnknownSourceError An unidentified error occurred. +*/ + +/*! + \enum QGeoAreaMonitorSource::AreaMonitorFeature + Defines the types of area monitoring capabilities. + + \value PersistentAreaMonitorFeature QGeoAreaMonitorInfo instances can be made persistent. + A persistent monitor continues to be active even when the application managing the monitor is + not running. + \value AnyAreaMonitorFeature Matches all possible area monitoring features. +*/ + +/*! + \fn virtual AreaMonitoringFeatures QGeoAreaMonitorSource::supportedAreaMonitorFeatures() const = 0; + + Returns the area monitoring features available to this source. +*/ + +/*! + \fn virtual QGeoAreaMonitorSource::Error QGeoAreaMonitorSource::error() const + + Returns the type of error that last occurred. +*/ + +/*! + Creates a monitor with the given \a parent. +*/ +QGeoAreaMonitorSource::QGeoAreaMonitorSource(QObject *parent) + : QObject(parent), + d(new QGeoAreaMonitorSourcePrivate) +{ + d->source = 0; +} + +/*! + Destroys the monitor. +*/ +QGeoAreaMonitorSource::~QGeoAreaMonitorSource() +{ + delete d; +} + +/*! + Creates and returns a monitor with the given \a parent that + monitors areas using resources on the underlying system. + + Returns 0 if the system has no support for position monitoring. +*/ +QGeoAreaMonitorSource *QGeoAreaMonitorSource::createDefaultSource(QObject *parent) +{ + QList plugins = QGeoPositionInfoSourcePrivate::pluginsSorted(); + foreach (const QJsonObject &obj, plugins) { + if (obj.value(QStringLiteral("Monitor")).isBool() + && obj.value(QStringLiteral("Monitor")).toBool()) + { + QGeoPositionInfoSourcePrivate d; + d.metaData = obj; + d.loadPlugin(); + QGeoAreaMonitorSource *s = 0; + if (d.factory) + s = d.factory->areaMonitor(parent); + if (s) + s->d->providerName = d.metaData.value(QStringLiteral("Provider")).toString(); + return s; + } + } + + return 0; +} + +/*! + Creates and returns a monitor with the given \a parent, + by loading the plugin named \a sourceName. + + Returns 0 if the plugin cannot be found. +*/ +QGeoAreaMonitorSource *QGeoAreaMonitorSource::createSource(const QString &sourceName, QObject *parent) +{ + QHash plugins = QGeoPositionInfoSourcePrivate::plugins(); + if (plugins.contains(sourceName)) { + QGeoPositionInfoSourcePrivate d; + d.metaData = plugins.value(sourceName); + d.loadPlugin(); + QGeoAreaMonitorSource *s = 0; + if (d.factory) + s = d.factory->areaMonitor(parent); + if (s) + s->d->providerName = d.metaData.value(QStringLiteral("Provider")).toString(); + return s; + } + + return 0; +} + +/*! + Returns a list of available monitor plugins, including the default system + backend if one is available. +*/ +QStringList QGeoAreaMonitorSource::availableSources() +{ + QStringList plugins; + const QHash meta = QGeoPositionInfoSourcePrivate::plugins(); + for (auto it = meta.cbegin(), end = meta.cend(); it != end; ++it) { + if (it.value().value(QStringLiteral("Monitor")).isBool() + && it.value().value(QStringLiteral("Monitor")).toBool()) { + plugins << it.key(); + } + } + + return plugins; +} + +/*! + Returns the unique name of the area monitor source implementation in use. + + This is the same name that can be passed to createSource() in order to + create a new instance of a particular area monitor source implementation. +*/ +QString QGeoAreaMonitorSource::sourceName() const +{ + return d->providerName; +} + +/*! + Returns the current QGeoPositionInfoSource used by this QGeoAreaMonitorSource + object. The function will return \l QGeoPositionInfoSource::createDefaultSource() + if no other object has been set. + + The function returns 0 if not even a default QGeoPositionInfoSource exists. + + Any usage of the returned \l QGeoPositionInfoSource instance should account + for the fact that it may reside in a different thread. + + \sa QGeoPositionInfoSource, setPositionInfoSource() +*/ +QGeoPositionInfoSource* QGeoAreaMonitorSource::positionInfoSource() const +{ + return d->source; +} + +/*! + Sets the new \l QGeoPositionInfoSource to be used by this QGeoAreaMonitorSource object. + The area monitoring backend becomes the new QObject parent for \a newSource. + The previous \l QGeoPositionInfoSource object will be deleted. All QGeoAreaMonitorSource + instances based on the same \l sourceName() share the same QGeoPositionInfoSource + instance. + + This may be useful when it is desirable to manipulate the positioning system + used by the area monitoring engine. + + Note that ownership must be taken care of by subclasses of QGeoAreaMonitorSource. + Due to the singleton pattern behind this class \a newSource may be moved to a + new thread. + + \sa positionInfoSource() + */ +void QGeoAreaMonitorSource::setPositionInfoSource(QGeoPositionInfoSource *newSource) +{ + d->source = newSource; +} + + +/*! + \fn virtual bool QGeoAreaMonitorSource::startMonitoring(const QGeoAreaMonitorInfo &monitor) + + Returns \c true if the monitoring of \a monitor could be successfully started; otherwise + returns false. A reason for not being able to start monitoring could be the unavailability + of an appropriate default position info source while no alternative QGeoPositionInfoSource + has been set via \l setPositionInfoSource(). + + If \a monitor is already active the existing monitor object will be replaced by the new \a monitor reference. + The identification of QGeoAreaMonitorInfo instances happens via \l QGeoAreaMonitorInfo::identifier(). + Therefore this function can also be used to update active monitors. + + If \a monitor has an expiry date that has been passed this function returns false. Calling + this function for an already via \l requestUpdate() registered single shot monitor + switches the monitor to a permanent monitoring mode. + + Requesting persistent monitoring on a QGeoAreaMonitorSource instance fails if the area monitoring + backend doesn't support \l QGeoAreaMonitorSource::PersistentAreaMonitorFeature. + + \sa stopMonitoring() +*/ + +/*! + \fn virtual bool QGeoAreaMonitorSource::requestUpdate(const QGeoAreaMonitorInfo &monitor, const char *signal) + + Enables single shot area monitoring. Area monitoring for \a monitor will be performed + until this QGeoAreaMonitorSource instance emits \a signal for the first time. Once + the signal was emitted, \a monitor is automatically removed from the list of \l activeMonitors(). + If \a monitor is invalid or has an expiry date that has been passed this function returns false. + + \code + QGeoAreaMonitor singleShotMonitor; + QGeoAreaMonitorSource * source = QGeoAreaMonitorSource::createDefaultSource(this); + //... + bool ret = source->requestUpdate(singleShotMonitor, + SIGNAL(areaExited(QGeoAreaMonitor,QGeoPositionInfo))); + \endcode + + The above \c singleShotMonitor object will cease to send updates once the \l areaExited() signal + was emitted for the first time. Until this point in time any other signal may be emitted + zero or more times depending on the area context. + + It is not possible to simultanously request updates for more than one signal of the same monitor object. + The last call to this function determines the signal upon which the updates cease to continue. + At this stage only the \l areaEntered() and \l areaExited() signals can be used to + terminate the monitoring process. + + Requesting persistent monitoring on a QGeoAreaMonitorSource instance fails if the area monitoring + backend doesn't support \l QGeoAreaMonitorSource::PersistentAreaMonitorFeature. + + If \a monitor was already registered via \l startMonitoring() it is converted to a single + shot behavior. + + \sa startMonitoring(), stopMonitoring() + */ + +/*! + \fn virtual bool QGeoAreaMonitorSource::stopMonitoring(const QGeoAreaMonitorInfo &monitor) + + Returns true if \a monitor was successfully removed from the list of \l activeMonitors(); + otherwise returns false. This behavior is independent on whether \a monitor was registered + via \l startMonitoring() or \l requestUpdate(). +*/ + +/*! + \fn virtual QList QGeoAreaMonitorSource::activeMonitors() const + + Returns the list of all active monitors known to the QGeoAreaMonitorSource object. + + An active monitor was started via startMonitoring() the source object will emit + the required signals such as areaEntered() or areaExited(). Multiple \l QGeoAreaMonitorSource + instances within the same application share the same active monitor objects. + + Unless an active QGeoAreaMonitorInfo \l {QGeoAreaMonitorInfo::isPersistent()}{isPersistent()} an active QGeoAreaMonitorInfo + will be stopped once the current application terminates. +*/ + +/*! + \fn virtual QList QGeoAreaMonitorSource::activeMonitors(const QGeoShape &lookupArea) const + + Returns the list of all active monitors known to the QGeoAreaMonitorSource object whose + center lies within \a lookupArea. If \a lookupArea is empty the returned list will be empty. + + An active monitor was started via startMonitoring() and the source object will emit + the required signals such as areaEntered() or areaExited(). Multiple QGeoAreaMonitorSource + instances within the same application share the same monitor objects. + + Unless an active QGeoAreaMonitorInfo \l {QGeoAreaMonitorInfo::isPersistent()}{isPersistent()} an active QGeoAreaMonitorInfo + will be stopped once the current application terminates. + + \sa QGeoShape +*/ + + +/*! + \fn void QGeoAreaMonitorSource::monitorExpired(const QGeoAreaMonitorInfo &monitor) + + Emitted when \a monitor has expired. An expired area monitor is automatically + removed from the list of \l activeMonitors(). + + \sa activeMonitors() +*/ + +/*! + \fn void QGeoAreaMonitorSource::areaEntered(const QGeoAreaMonitorInfo &monitor, const QGeoPositionInfo &update) + + Emitted when the current position has moved from a position outside of the active \a monitor + to a position within the monitored area. + + The \a update holds the new position. +*/ + +/*! + \fn void QGeoAreaMonitorSource::areaExited(const QGeoAreaMonitorInfo &monitor, const QGeoPositionInfo &update) + + Emitted when the current position has moved from a position within the active \a monitor + to a position outside the monitored area. + + The \a update holds the new position. +*/ + +/*! + \fn void QGeoAreaMonitorSource::error(QGeoAreaMonitorSource::Error areaMonitoringError) + + This signal is emitted after an error occurred. The \a areaMonitoringError + parameter describes the type of error that occurred. + +*/ + +QT_END_NAMESPACE diff --git a/src/positioning/qgeoareamonitorsource.h b/src/positioning/qgeoareamonitorsource.h new file mode 100644 index 0000000..519f313 --- /dev/null +++ b/src/positioning/qgeoareamonitorsource.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOAREAMONITORSOURCE_H +#define QGEOAREAMONITORSOURCE_H + +#include +#include +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoPositionInfo; +class QGeoAreaMonitorSourcePrivate; +class Q_POSITIONING_EXPORT QGeoAreaMonitorSource : public QObject +{ + Q_OBJECT + +public: + enum Error { + AccessError = 0, + InsufficientPositionInfo = 1, + UnknownSourceError = 2, + NoError = 3 + }; + Q_ENUMS(Error) + + enum AreaMonitorFeature { + PersistentAreaMonitorFeature = 0x00000001, + AnyAreaMonitorFeature = 0xffffffff + }; + Q_DECLARE_FLAGS(AreaMonitorFeatures, AreaMonitorFeature) + + explicit QGeoAreaMonitorSource(QObject *parent); + virtual ~QGeoAreaMonitorSource(); + + static QGeoAreaMonitorSource *createDefaultSource(QObject *parent); + static QGeoAreaMonitorSource *createSource(const QString& sourceName, QObject *parent); + static QStringList availableSources(); + + virtual void setPositionInfoSource(QGeoPositionInfoSource *source); + virtual QGeoPositionInfoSource* positionInfoSource() const; + + QString sourceName() const; + + virtual Error error() const = 0; + virtual AreaMonitorFeatures supportedAreaMonitorFeatures() const = 0; + + virtual bool startMonitoring(const QGeoAreaMonitorInfo &monitor) = 0; + virtual bool stopMonitoring(const QGeoAreaMonitorInfo &monitor) = 0; + virtual bool requestUpdate(const QGeoAreaMonitorInfo &monitor, const char *signal) = 0; + + virtual QList activeMonitors() const = 0; + virtual QList activeMonitors(const QGeoShape &lookupArea) const = 0; + +Q_SIGNALS: + void areaEntered(const QGeoAreaMonitorInfo &monitor, const QGeoPositionInfo &update); + void areaExited(const QGeoAreaMonitorInfo &monitor, const QGeoPositionInfo &update); + void monitorExpired(const QGeoAreaMonitorInfo &monitor); + void error(QGeoAreaMonitorSource::Error error); + +private: + Q_DISABLE_COPY(QGeoAreaMonitorSource) + QGeoAreaMonitorSourcePrivate *d; +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeocircle.cpp b/src/positioning/qgeocircle.cpp new file mode 100644 index 0000000..140f78a --- /dev/null +++ b/src/positioning/qgeocircle.cpp @@ -0,0 +1,492 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocircle.h" +#include "qgeocircle_p.h" + +#include "qgeocoordinate.h" +#include "qnumeric.h" +#include "qlocationutils_p.h" + +#include "qdoublevector2d_p.h" +#include "qdoublevector3d_p.h" +#include +QT_BEGIN_NAMESPACE + +/*! + \class QGeoCircle + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoCircle class defines a circular geographic area. + + The circle is defined in terms of a QGeoCoordinate which specifies the + center of the circle and a qreal which specifies the radius of the circle + in meters. + + The circle is considered invalid if the center coordinate is invalid + or if the radius is less than zero. + + This class is a \l Q_GADGET since Qt 5.5. It can be + \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. +*/ + +/*! + \property QGeoCircle::center + \brief This property holds the center coordinate for the geo circle. + + The circle is considered invalid if this property contains an invalid + coordinate. + + A default constructed QGeoCircle uses an invalid \l QGeoCoordinate + as center. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoCircle::radius + \brief This property holds the circle radius in meters. + + The circle is considered invalid if this property is negative. + + By default, the radius is initialized with \c -1. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +inline QGeoCirclePrivate *QGeoCircle::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QGeoCirclePrivate *QGeoCircle::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +struct CircleVariantConversions +{ + CircleVariantConversions() + { + QMetaType::registerConverter(); + QMetaType::registerConverter(); + } +}; + +Q_GLOBAL_STATIC(CircleVariantConversions, initCircleConversions) + +/*! + Constructs a new, invalid geo circle. +*/ +QGeoCircle::QGeoCircle() +: QGeoShape(new QGeoCirclePrivate) +{ + initCircleConversions(); +} + +/*! + Constructs a new geo circle centered at \a center and with a radius of \a radius meters. +*/ +QGeoCircle::QGeoCircle(const QGeoCoordinate ¢er, qreal radius) +{ + initCircleConversions(); + d_ptr = new QGeoCirclePrivate(center, radius); +} + +/*! + Constructs a new geo circle from the contents of \a other. +*/ +QGeoCircle::QGeoCircle(const QGeoCircle &other) +: QGeoShape(other) +{ + initCircleConversions(); +} + +/*! + Constructs a new geo circle from the contents of \a other. +*/ +QGeoCircle::QGeoCircle(const QGeoShape &other) +: QGeoShape(other) +{ + initCircleConversions(); + if (type() != QGeoShape::CircleType) + d_ptr = new QGeoCirclePrivate; +} + +/*! + Destroys this geo circle. +*/ +QGeoCircle::~QGeoCircle() {} + +/*! + Assigns \a other to this geo circle and returns a reference to this geo circle. +*/ +QGeoCircle &QGeoCircle::operator=(const QGeoCircle &other) +{ + QGeoShape::operator=(other); + return *this; +} + +/*! + Returns whether this geo circle is equal to \a other. +*/ +bool QGeoCircle::operator==(const QGeoCircle &other) const +{ + Q_D(const QGeoCircle); + + return *d == *other.d_func(); +} + +/*! + Returns whether this geo circle is not equal to \a other. +*/ +bool QGeoCircle::operator!=(const QGeoCircle &other) const +{ + Q_D(const QGeoCircle); + + return !(*d == *other.d_func()); +} + +bool QGeoCirclePrivate::isValid() const +{ + return m_center.isValid() && !qIsNaN(m_radius) && m_radius >= -1e-7; +} + +bool QGeoCirclePrivate::isEmpty() const +{ + return !isValid() || m_radius <= 1e-7; +} + +/*! + Sets the center coordinate of this geo circle to \a center. +*/ +void QGeoCircle::setCenter(const QGeoCoordinate ¢er) +{ + Q_D(QGeoCircle); + + d->setCenter(center); +} + +/*! + Returns the center coordinate of this geo circle. Equivalent to QGeoShape::center(). +*/ +QGeoCoordinate QGeoCircle::center() const +{ + Q_D(const QGeoCircle); + + return d->center(); +} + +/*! + Sets the radius in meters of this geo circle to \a radius. +*/ +void QGeoCircle::setRadius(qreal radius) +{ + Q_D(QGeoCircle); + + d->setRadius(radius); +} + +/*! + Returns the radius in meters of this geo circle. +*/ +qreal QGeoCircle::radius() const +{ + Q_D(const QGeoCircle); + + return d->m_radius; +} + +bool QGeoCirclePrivate::contains(const QGeoCoordinate &coordinate) const +{ + if (!isValid() || !coordinate.isValid()) + return false; + + // see QTBUG-41447 for details + qreal distance = m_center.distanceTo(coordinate); + if (qFuzzyCompare(distance, m_radius) || distance <= m_radius) + return true; + + return false; +} + +QGeoCoordinate QGeoCirclePrivate::center() const +{ + return m_center; +} + +QGeoRectangle QGeoCirclePrivate::boundingGeoRectangle() const +{ + return m_bbox; +} + +void QGeoCirclePrivate::updateBoundingBox() +{ + if (isEmpty()) { + if (m_center.isValid()) { + m_bbox.setTopLeft(m_center); + m_bbox.setBottomRight(m_center); + } + return; + } + + bool crossNorth = crossNorthPole(); + bool crossSouth = crossSouthPole(); + + if (crossNorth && crossSouth) { + // Circle crossing both poles fills the whole map + m_bbox = QGeoRectangle(QGeoCoordinate(90.0, -180.0), QGeoCoordinate(-90.0, 180.0)); + } else if (crossNorth) { + // Circle crossing one pole fills the map in the longitudinal direction + m_bbox = QGeoRectangle(QGeoCoordinate(90.0, -180.0), QGeoCoordinate(m_center.atDistanceAndAzimuth(m_radius, 180.0).latitude(), 180.0)); + } else if (crossSouth) { + m_bbox = QGeoRectangle(QGeoCoordinate(m_center.atDistanceAndAzimuth(m_radius, 0.0).latitude(), -180.0), QGeoCoordinate(-90, 180.0)); + } else { + // Regular circle not crossing anything + + // Calculate geo bounding box of the circle + // + // A circle tangential point with a meridian, together with pole and + // the circle center create a spherical triangle. + // Finding the tangential point with the spherical law of sines: + // + // * lon_delta_in_rad : delta between the circle center and a tangential + // point (absolute value). + // * r_in_rad : angular radius of the circle + // * lat_in_rad : latitude of the circle center + // * alpha_in_rad : angle between meridian and radius of the circle. + // At the tangential point, sin(alpha_in_rad) == 1. + // * lat_delta_in_rad - absolute delta of latitudes between the circle center and + // any of the two points where the great circle going through the circle + // center and the pole crosses the circle. In other words, the points + // on the circle with azimuth 0 or 180. + // + // Using: + // sin(lon_delta_in_rad)/sin(r_in_rad) = sin(alpha_in_rad)/sin(pi/2 - lat_in_rad) + + double r_in_rad = m_radius / QLocationUtils::earthMeanRadius(); // angular r + double lat_delta_in_deg = QLocationUtils::degrees(r_in_rad); + double lon_delta_in_deg = QLocationUtils::degrees(std::asin( + std::sin(r_in_rad) / + std::cos(QLocationUtils::radians(m_center.latitude())) + )); + + QGeoCoordinate topLeft; + topLeft.setLatitude(QLocationUtils::clipLat(m_center.latitude() + lat_delta_in_deg)); + topLeft.setLongitude(QLocationUtils::wrapLong(m_center.longitude() - lon_delta_in_deg)); + QGeoCoordinate bottomRight; + bottomRight.setLatitude(QLocationUtils::clipLat(m_center.latitude() - lat_delta_in_deg)); + bottomRight.setLongitude(QLocationUtils::wrapLong(m_center.longitude() + lon_delta_in_deg)); + + m_bbox = QGeoRectangle(topLeft, bottomRight); + } +} + +void QGeoCirclePrivate::setCenter(const QGeoCoordinate &c) +{ + m_center = c; + updateBoundingBox(); +} + +void QGeoCirclePrivate::setRadius(const qreal r) +{ + m_radius = r; + updateBoundingBox(); +} + +bool QGeoCirclePrivate::crossNorthPole() const +{ + const QGeoCoordinate northPole(90.0, m_center.longitude()); + qreal distanceToPole = m_center.distanceTo(northPole); + if (distanceToPole < m_radius) + return true; + return false; +} + +bool QGeoCirclePrivate::crossSouthPole() const +{ + const QGeoCoordinate southPole(-90.0, m_center.longitude()); + qreal distanceToPole = m_center.distanceTo(southPole); + if (distanceToPole < m_radius) + return true; + return false; +} + +/* + Extends the circle to include \a coordinate. +*/ +void QGeoCirclePrivate::extendShape(const QGeoCoordinate &coordinate) +{ + if (!isValid() || !coordinate.isValid() || contains(coordinate)) + return; + + setRadius(m_center.distanceTo(coordinate)); +} + +/*! + Translates this geo circle by \a degreesLatitude northwards and \a degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. +*/ +void QGeoCircle::translate(double degreesLatitude, double degreesLongitude) +{ + // TODO handle dlat, dlon larger than 360 degrees + + Q_D(QGeoCircle); + + double lat = d->m_center.latitude(); + double lon = d->m_center.longitude(); + + lat += degreesLatitude; + lon += degreesLongitude; + lon = QLocationUtils::wrapLong(lon); + + // TODO: remove this and simply clip latitude. + if (lat > 90.0) { + lat = 180.0 - lat; + if (lon < 0.0) + lon = 180.0; + else + lon -= 180; + } + + if (lat < -90.0) { + lat = 180.0 + lat; + if (lon < 0.0) + lon = 180.0; + else + lon -= 180; + } + + d->setCenter(QGeoCoordinate(lat, lon)); +} + +/*! + Returns a copy of this geo circle translated by \a degreesLatitude northwards and + \a degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. + + \sa translate() +*/ +QGeoCircle QGeoCircle::translated(double degreesLatitude, double degreesLongitude) const +{ + QGeoCircle result(*this); + result.translate(degreesLatitude, degreesLongitude); + return result; +} + +/*! + Extends the geo circle to also cover the coordinate \a coordinate + + \since 5.9 +*/ +void QGeoCircle::extendCircle(const QGeoCoordinate &coordinate) +{ + Q_D(QGeoCircle); + d->extendShape(coordinate); +} + +/*! + Returns the geo circle properties as a string. + + \since 5.5 +*/ + +QString QGeoCircle::toString() const +{ + if (type() != QGeoShape::CircleType) { + qWarning("Not a circle"); + return QStringLiteral("QGeoCircle(not a circle)"); + } + + return QStringLiteral("QGeoCircle({%1, %2}, %3)") + .arg(center().latitude()) + .arg(center().longitude()) + .arg(radius()); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoCirclePrivate::QGeoCirclePrivate() +: QGeoShapePrivate(QGeoShape::CircleType), m_radius(-1.0) +{ +} + +QGeoCirclePrivate::QGeoCirclePrivate(const QGeoCoordinate ¢er, qreal radius) +: QGeoShapePrivate(QGeoShape::CircleType), m_center(center), m_radius(radius) +{ + updateBoundingBox(); +} + +QGeoCirclePrivate::QGeoCirclePrivate(const QGeoCirclePrivate &other) +: QGeoShapePrivate(QGeoShape::CircleType), m_center(other.m_center), + m_radius(other.m_radius), m_bbox(other.m_bbox) +{ +} + +QGeoCirclePrivate::~QGeoCirclePrivate() {} + +QGeoShapePrivate *QGeoCirclePrivate::clone() const +{ + return new QGeoCirclePrivate(*this); +} + +bool QGeoCirclePrivate::operator==(const QGeoShapePrivate &other) const +{ + if (!QGeoShapePrivate::operator==(other)) + return false; + + const QGeoCirclePrivate &otherCircle = static_cast(other); + + return m_radius == otherCircle.m_radius && m_center == otherCircle.m_center; +} + +QT_END_NAMESPACE diff --git a/src/positioning/qgeocircle.h b/src/positioning/qgeocircle.h new file mode 100644 index 0000000..b41fe26 --- /dev/null +++ b/src/positioning/qgeocircle.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCIRCLE_H +#define QGEOCIRCLE_H + +#include + +QT_BEGIN_NAMESPACE + +class QGeoCoordinate; +class QGeoCirclePrivate; + +class Q_POSITIONING_EXPORT QGeoCircle : public QGeoShape +{ + Q_GADGET + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter) + Q_PROPERTY(qreal radius READ radius WRITE setRadius) + +public: + QGeoCircle(); + QGeoCircle(const QGeoCoordinate ¢er, qreal radius = -1.0); + QGeoCircle(const QGeoCircle &other); + QGeoCircle(const QGeoShape &other); + + ~QGeoCircle(); + + QGeoCircle &operator=(const QGeoCircle &other); + + using QGeoShape::operator==; + bool operator==(const QGeoCircle &other) const; + + using QGeoShape::operator!=; + bool operator!=(const QGeoCircle &other) const; + + void setCenter(const QGeoCoordinate ¢er); + QGeoCoordinate center() const; + + void setRadius(qreal radius); + qreal radius() const; + + Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude); + Q_INVOKABLE QGeoCircle translated(double degreesLatitude, double degreesLongitude) const; + Q_INVOKABLE void extendCircle(const QGeoCoordinate &coordinate); + + Q_INVOKABLE QString toString() const; + +private: + inline QGeoCirclePrivate *d_func(); + inline const QGeoCirclePrivate *d_func() const; +}; + +Q_DECLARE_TYPEINFO(QGeoCircle, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoCircle) + +#endif + diff --git a/src/positioning/qgeocircle_p.h b/src/positioning/qgeocircle_p.h new file mode 100644 index 0000000..ca3f86e --- /dev/null +++ b/src/positioning/qgeocircle_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCIRCLE_P_H +#define QGEOCIRCLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeoshape_p.h" +#include "qgeocoordinate.h" + +QT_BEGIN_NAMESPACE + +class QGeoCirclePrivate : public QGeoShapePrivate +{ +public: + QGeoCirclePrivate(); + QGeoCirclePrivate(const QGeoCoordinate ¢er, qreal radius); + QGeoCirclePrivate(const QGeoCirclePrivate &other); + ~QGeoCirclePrivate(); + + bool isValid() const override; + bool isEmpty() const override; + bool contains(const QGeoCoordinate &coordinate) const override; + + QGeoCoordinate center() const override; + + QGeoRectangle boundingGeoRectangle() const override; + + bool crossNorthPole() const; + bool crossSouthPole() const; + void updateBoundingBox(); + void setCenter(const QGeoCoordinate &c); + void setRadius(const qreal r); + + void extendShape(const QGeoCoordinate &coordinate) override; + + QGeoShapePrivate *clone() const override; + + bool operator==(const QGeoShapePrivate &other) const override; + + QGeoCoordinate m_center; + qreal m_radius; + QGeoRectangle m_bbox; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeocoordinate.cpp b/src/positioning/qgeocoordinate.cpp new file mode 100644 index 0000000..80904d7 --- /dev/null +++ b/src/positioning/qgeocoordinate.cpp @@ -0,0 +1,767 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeocoordinate.h" +#include "qgeocoordinate_p.h" +#include "qlocationutils_p.h" + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +static const double qgeocoordinate_EARTH_MEAN_RADIUS = 6371.0072; + + +QGeoCoordinatePrivate::QGeoCoordinatePrivate(): + lat(qQNaN()), + lng(qQNaN()), + alt(qQNaN()) +{} + +QGeoCoordinatePrivate::QGeoCoordinatePrivate(const QGeoCoordinatePrivate &other) + : QSharedData(other), + lat(other.lat), + lng(other.lng), + alt(other.alt) +{} + +QGeoCoordinatePrivate::~QGeoCoordinatePrivate() +{} + + +QGeoMercatorCoordinatePrivate::QGeoMercatorCoordinatePrivate(): + QGeoCoordinatePrivate(), + m_mercatorX(qQNaN()), + m_mercatorY(qQNaN()) +{} + +QGeoMercatorCoordinatePrivate::QGeoMercatorCoordinatePrivate(const QGeoMercatorCoordinatePrivate &other) + : QGeoCoordinatePrivate(other), + m_mercatorX(other.m_mercatorX), + m_mercatorY(other.m_mercatorY) +{} + +QGeoMercatorCoordinatePrivate::~QGeoMercatorCoordinatePrivate() +{} + +/*! + \class QGeoCoordinate + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoCoordinate class defines a geographical position on the surface of the Earth. + + A QGeoCoordinate is defined by latitude, longitude, and optionally, altitude. + + Use type() to determine whether a coordinate is a 2D coordinate (has + latitude and longitude only) or 3D coordinate (has latitude, longitude + and altitude). Use distanceTo() and azimuthTo() to calculate the distance + and bearing between coordinates. + + The coordinate values should be specified using the WGS84 datum. For more information + on geographical terms see this article on \l {http://en.wikipedia.org/wiki/Geographic_coordinate_system}{coordinates} and + another on \l {http://en.wikipedia.org/wiki/Geodetic_system}{geodetic systems} + including WGS84. + + Azimuth in this context is equivalent to a compass bearing based on true north. + + This class is a \l Q_GADGET since Qt 5.5. It can be + \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. +*/ + +/*! + \enum QGeoCoordinate::CoordinateType + Defines the types of a coordinate. + + \value InvalidCoordinate An invalid coordinate. A coordinate is invalid if its latitude or longitude values are invalid. + \value Coordinate2D A coordinate with valid latitude and longitude values. + \value Coordinate3D A coordinate with valid latitude and longitude values, and also an altitude value. +*/ + +/*! + \enum QGeoCoordinate::CoordinateFormat + Defines the possible formatting options for toString(). + + \value Degrees Returns a string representation of the coordinates in decimal degrees format. + \value DegreesWithHemisphere Returns a string representation of the coordinates in decimal degrees format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates. + \value DegreesMinutes Returns a string representation of the coordinates in degrees-minutes format. + \value DegreesMinutesWithHemisphere Returns a string representation of the coordinates in degrees-minutes format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates. + \value DegreesMinutesSeconds Returns a string representation of the coordinates in degrees-minutes-seconds format. + \value DegreesMinutesSecondsWithHemisphere Returns a string representation of the coordinates in degrees-minutes-seconds format, using 'N', 'S', 'E' or 'W' to indicate the hemispheres of the coordinates. + + \sa toString() +*/ + +/*! + \property QGeoCoordinate::latitude + \brief This property holds the latitude in decimal degrees. + + The property is undefined (\l {qQNaN()}) if the latitude has not been set. + A positive latitude indicates the Northern Hemisphere, and a negative + latitude indicates the Southern Hemisphere. When setting the latitude the + new value should be in the + \l {http://en.wikipedia.org/wiki/World_Geodetic_System}{WGS84} datum format. + + To be valid, the latitude must be between -90 to 90 inclusive. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoCoordinate::longitude + \brief This property holds the longitude in decimal degrees. + + The property is undefined (\l {qQNaN()}) if the longitude has not been set. + A positive longitude indicates the Eastern Hemisphere, and a negative + longitude indicates the Western Hemisphere. When setting the longitude the + new value should be in the + \l {http://en.wikipedia.org/wiki/World_Geodetic_System}{WGS84} datum format. + + To be valid, the longitude must be between -180 to 180 inclusive. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoCoordinate::altitude + \brief This property holds the altitude in meters above sea level. + + The property is undefined (\l {qQNaN()}) if the altitude has not been set. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoCoordinate::isValid + \brief This property holds the validity of this geo coordinate. + + The geo coordinate is valid if the \l [CPP]{longitude} and \l [CPP]{latitude} + properties have been set to valid values. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + Constructs a coordinate. The coordinate will be invalid until + setLatitude() and setLongitude() have been called. +*/ +QGeoCoordinate::QGeoCoordinate() + : d(new QGeoCoordinatePrivate) +{ +} + +/*! + Constructs a coordinate with the given \a latitude and \a longitude. + + If the latitude is not between -90 to 90 inclusive, or the longitude + is not between -180 to 180 inclusive, none of the values are set and + the type() will be QGeoCoordinate::InvalidCoordinate. + + \sa isValid() +*/ +QGeoCoordinate::QGeoCoordinate(double latitude, double longitude) + : d(new QGeoCoordinatePrivate) +{ + if (QLocationUtils::isValidLat(latitude) && QLocationUtils::isValidLong(longitude)) { + d->lat = latitude; + d->lng = longitude; + } +} + +/*! + Constructs a coordinate with the given \a latitude, \a longitude + and \a altitude. + + If the latitude is not between -90 to 90 inclusive, or the longitude + is not between -180 to 180 inclusive, none of the values are set and + the type() will be QGeoCoordinate::InvalidCoordinate. + + Note that \a altitude specifies the meters above sea level. + + \sa isValid() +*/ +QGeoCoordinate::QGeoCoordinate(double latitude, double longitude, double altitude) + : d(new QGeoCoordinatePrivate) +{ + if (QLocationUtils::isValidLat(latitude) && QLocationUtils::isValidLong(longitude)) { + d->lat = latitude; + d->lng = longitude; + d->alt = altitude; + } +} + +/*! + Constructs a coordinate from the contents of \a other. +*/ +QGeoCoordinate::QGeoCoordinate(const QGeoCoordinate &other) + : d(other.d) +{} + +/*! + Assigns \a other to this coordinate and returns a reference to this coordinate. +*/ +QGeoCoordinate &QGeoCoordinate::operator=(const QGeoCoordinate &other) +{ + if (this == &other) + return *this; + + d = other.d; + return (*this); +} + +/*! + Destroys the coordinate object. +*/ +QGeoCoordinate::~QGeoCoordinate() +{ +} + +/*! + Returns true if the latitude, longitude and altitude of this + coordinate are the same as those of \a other. + + The longitude will be ignored if the latitude is +/- 90 degrees. +*/ +bool QGeoCoordinate::operator==(const QGeoCoordinate &other) const +{ + bool latEqual = (qIsNaN(d->lat) && qIsNaN(other.d->lat)) + || qFuzzyCompare(d->lat, other.d->lat); + bool lngEqual = (qIsNaN(d->lng) && qIsNaN(other.d->lng)) + || qFuzzyCompare(d->lng, other.d->lng); + bool altEqual = (qIsNaN(d->alt) && qIsNaN(other.d->alt)) + || qFuzzyCompare(d->alt, other.d->alt); + + if (!qIsNaN(d->lat) && ((d->lat == 90.0) || (d->lat == -90.0))) + lngEqual = true; + + return (latEqual && lngEqual && altEqual); +} + +/*! + \fn bool QGeoCoordinate::operator!=(const QGeoCoordinate &other) const + \fn bool QGeoCoordinate::operator!=(const QGeoCoordinateObject &other) const + \fn bool QGeoCoordinate::operator!=(const QGeoCoordinateObject *other) const + + Returns \c true if latitude, longitude, or altitude of this + coordinate are not identical to \a other. +*/ + +/*! + Returns \c true if the \l longitude and \l latitude are valid. +*/ +bool QGeoCoordinate::isValid() const +{ + CoordinateType t = type(); + return t == Coordinate2D || t == Coordinate3D; +} + +/*! + Returns the type of this coordinate. +*/ +QGeoCoordinate::CoordinateType QGeoCoordinate::type() const +{ + if (QLocationUtils::isValidLat(d->lat) + && QLocationUtils::isValidLong(d->lng)) { + if (qIsNaN(d->alt)) + return Coordinate2D; + return Coordinate3D; + } + return InvalidCoordinate; +} + + +/*! + Returns the latitude, in decimal degrees. The return value is undefined + if the latitude has not been set. + + A positive latitude indicates the Northern Hemisphere, and a negative + latitude indicates the Southern Hemisphere. + + \sa setLatitude(), type() +*/ +double QGeoCoordinate::latitude() const +{ + return d->lat; +} + +/*! + Sets the latitude (in decimal degrees) to \a latitude. The value should + be in the WGS84 datum. + + To be valid, the latitude must be between -90 to 90 inclusive. + + \sa latitude() +*/ +void QGeoCoordinate::setLatitude(double latitude) +{ + d->lat = latitude; +} + +/*! + Returns the longitude, in decimal degrees. The return value is undefined + if the longitude has not been set. + + A positive longitude indicates the Eastern Hemisphere, and a negative + longitude indicates the Western Hemisphere. + + \sa setLongitude(), type() +*/ +double QGeoCoordinate::longitude() const +{ + return d->lng; +} + +/*! + Sets the longitude (in decimal degrees) to \a longitude. The value should + be in the WGS84 datum. + + To be valid, the longitude must be between -180 to 180 inclusive. + + \sa longitude() +*/ +void QGeoCoordinate::setLongitude(double longitude) +{ + d->lng = longitude; +} + +/*! + Returns the altitude (meters above sea level). + + The return value is undefined if the altitude has not been set. + + \sa setAltitude(), type() +*/ +double QGeoCoordinate::altitude() const +{ + return d->alt; +} + +/*! + Sets the altitude (meters above sea level) to \a altitude. + + \sa altitude() +*/ +void QGeoCoordinate::setAltitude(double altitude) +{ + d->alt = altitude; +} + +/*! + Returns the distance (in meters) from this coordinate to the coordinate + specified by \a other. Altitude is not used in the calculation. + + This calculation returns the great-circle distance between the two + coordinates, with an assumption that the Earth is spherical for the + purpose of this calculation. + + Returns 0 if the type of this coordinate or the type of \a other is + QGeoCoordinate::InvalidCoordinate. +*/ +qreal QGeoCoordinate::distanceTo(const QGeoCoordinate &other) const +{ + if (type() == QGeoCoordinate::InvalidCoordinate + || other.type() == QGeoCoordinate::InvalidCoordinate) { + return 0; + } + + // Haversine formula + double dlat = qDegreesToRadians(other.d->lat - d->lat); + double dlon = qDegreesToRadians(other.d->lng - d->lng); + double haversine_dlat = sin(dlat / 2.0); + haversine_dlat *= haversine_dlat; + double haversine_dlon = sin(dlon / 2.0); + haversine_dlon *= haversine_dlon; + double y = haversine_dlat + + cos(qDegreesToRadians(d->lat)) + * cos(qDegreesToRadians(other.d->lat)) + * haversine_dlon; + double x = 2 * asin(sqrt(y)); + return qreal(x * qgeocoordinate_EARTH_MEAN_RADIUS * 1000); +} + +/*! + Returns the azimuth (or bearing) in degrees from this coordinate to the + coordinate specified by \a other. Altitude is not used in the calculation. + + The bearing returned is the bearing from the origin to \a other along the + great-circle between the two coordinates. There is an assumption that the + Earth is spherical for the purpose of this calculation. + + Returns 0 if the type of this coordinate or the type of \a other is + QGeoCoordinate::InvalidCoordinate. +*/ +qreal QGeoCoordinate::azimuthTo(const QGeoCoordinate &other) const +{ + if (type() == QGeoCoordinate::InvalidCoordinate + || other.type() == QGeoCoordinate::InvalidCoordinate) { + return 0; + } + + double dlon = qDegreesToRadians(other.d->lng - d->lng); + double lat1Rad = qDegreesToRadians(d->lat); + double lat2Rad = qDegreesToRadians(other.d->lat); + + double y = sin(dlon) * cos(lat2Rad); + double x = cos(lat1Rad) * sin(lat2Rad) - sin(lat1Rad) * cos(lat2Rad) * cos(dlon); + + double azimuth = qRadiansToDegrees(atan2(y, x)) + 360.0; + double whole; + double fraction = modf(azimuth, &whole); + return qreal((int(whole + 360) % 360) + fraction); +} + +void QGeoCoordinatePrivate::atDistanceAndAzimuth(const QGeoCoordinate &coord, + qreal distance, qreal azimuth, + double *lon, double *lat) +{ + double latRad = qDegreesToRadians(coord.d->lat); + double lonRad = qDegreesToRadians(coord.d->lng); + double cosLatRad = cos(latRad); + double sinLatRad = sin(latRad); + + double azimuthRad = qDegreesToRadians(azimuth); + + double ratio = (distance / (qgeocoordinate_EARTH_MEAN_RADIUS * 1000.0)); + double cosRatio = cos(ratio); + double sinRatio = sin(ratio); + + double resultLatRad = asin(sinLatRad * cosRatio + + cosLatRad * sinRatio * cos(azimuthRad)); + double resultLonRad = lonRad + atan2(sin(azimuthRad) * sinRatio * cosLatRad, + cosRatio - sinLatRad * sin(resultLatRad)); + + *lat = qRadiansToDegrees(resultLatRad); + *lon = qRadiansToDegrees(resultLonRad); +} + +/*! + Returns the coordinate that is reached by traveling \a distance meters + from the current coordinate at \a azimuth (or bearing) along a great-circle. + There is an assumption that the Earth is spherical for the purpose of this + calculation. + + The altitude will have \a distanceUp added to it. + + Returns an invalid coordinate if this coordinate is invalid. +*/ +QGeoCoordinate QGeoCoordinate::atDistanceAndAzimuth(qreal distance, qreal azimuth, qreal distanceUp) const +{ + if (!isValid()) + return QGeoCoordinate(); + + double resultLon, resultLat; + QGeoCoordinatePrivate::atDistanceAndAzimuth(*this, distance, azimuth, + &resultLon, &resultLat); + double resultAlt = d->alt + distanceUp; + return QGeoCoordinate(resultLat, QLocationUtils::wrapLong(resultLon), resultAlt); +} + +/*! + Returns this coordinate as a string in the specified \a format. + + For example, if this coordinate has a latitude of -27.46758, a longitude + of 153.027892 and an altitude of 28.1, these are the strings + returned depending on \a format: + + \table + \header + \li \a format value + \li Returned string + \row + \li \l Degrees + \li -27.46758\unicode{0xB0}, 153.02789\unicode{0xB0}, 28.1m + \row + \li \l DegreesWithHemisphere + \li 27.46758\unicode{0xB0} S, 153.02789\unicode{0xB0} E, 28.1m + \row + \li \l DegreesMinutes + \li -27\unicode{0xB0} 28.054', 153\unicode{0xB0} 1.673', 28.1m + \row + \li \l DegreesMinutesWithHemisphere + \li 27\unicode{0xB0} 28.054 S', 153\unicode{0xB0} 1.673' E, 28.1m + \row + \li \l DegreesMinutesSeconds + \li -27\unicode{0xB0} 28' 3.2", 153\unicode{0xB0} 1' 40.4", 28.1m + \row + \li \l DegreesMinutesSecondsWithHemisphere + \li 27\unicode{0xB0} 28' 3.2" S, 153\unicode{0xB0} 1' 40.4" E, 28.1m + \endtable + + The altitude field is omitted if no altitude is set. + + If the coordinate is invalid, an empty string is returned. +*/ +QString QGeoCoordinate::toString(CoordinateFormat format) const +{ + if (type() == QGeoCoordinate::InvalidCoordinate) + return QString(); + + QString latStr; + QString longStr; + + double absLat = qAbs(d->lat); + double absLng = qAbs(d->lng); + QChar symbol(0x00B0); // degrees symbol + + switch (format) { + case Degrees: + case DegreesWithHemisphere: { + latStr = QString::number(absLat, 'f', 5) + symbol; + longStr = QString::number(absLng, 'f', 5) + symbol; + break; + } + case DegreesMinutes: + case DegreesMinutesWithHemisphere: { + double latMin = (absLat - int(absLat)) * 60; + double lngMin = (absLng - int(absLng)) * 60; + + if (qRound(latMin) >= 60) { + absLat++; + latMin = qAbs(latMin - 60.0f); + //avoid invalid latitude due to latMin rounding below + if (qRound(absLat) >= 90) + latMin = 0.0f; + } + if (qRound(lngMin) >= 60) { + absLng++; + lngMin = qAbs(lngMin - 60.0f); + // avoid invalid longitude due to lngMin rounding below + if (qRound(absLng) >= 180) + lngMin = 0.0f; + } + + latStr = QString::fromLatin1("%1%2 %3'") + .arg(QString::number(int(absLat))) + .arg(symbol) + .arg(QString::number(latMin, 'f', 3)); + longStr = QString::fromLatin1("%1%2 %3'") + .arg(QString::number(int(absLng))) + .arg(symbol) + .arg(QString::number(lngMin, 'f', 3)); + break; + } + case DegreesMinutesSeconds: + case DegreesMinutesSecondsWithHemisphere: { + double latMin = (absLat - int(absLat)) * 60; + double lngMin = (absLng - int(absLng)) * 60; + double latSec = (latMin - int(latMin)) * 60; + double lngSec = (lngMin - int(lngMin)) * 60; + + // overflow to full minutes + if (qRound(latSec) >= 60) { + latMin++; + latSec = qAbs(latSec - 60.0f); + // overflow to full degrees + if (qRound(latMin) >= 60) { + absLat++; + latMin = qAbs(latMin - 60.0f); + // avoid invalid latitude due to latSec rounding below + if (qRound(absLat) >= 90) + latSec = 0.0f; + } + } + if (qRound(lngSec) >= 60) { + lngMin++; + lngSec = qAbs(lngSec - 60.0f); + if (qRound(lngMin) >= 60) { + absLng++; + lngMin = qAbs(lngMin - 60.0f); + // avoid invalid longitude due to lngSec rounding below + if (qRound(absLng) >= 180) + lngSec = 0.0f; + } + } + + latStr = QString::fromLatin1("%1%2 %3' %4\"") + .arg(QString::number(int(absLat))) + .arg(symbol) + .arg(QString::number(int(latMin))) + .arg(QString::number(latSec, 'f', 1)); + longStr = QString::fromLatin1("%1%2 %3' %4\"") + .arg(QString::number(int(absLng))) + .arg(symbol) + .arg(QString::number(int(lngMin))) + .arg(QString::number(lngSec, 'f', 1)); + break; + } + } + + // now add the "-" to the start, or append the hemisphere char + switch (format) { + case Degrees: + case DegreesMinutes: + case DegreesMinutesSeconds: { + if (d->lat < 0) + latStr.insert(0, QStringLiteral("-")); + if (d->lng < 0) + longStr.insert(0, QStringLiteral("-")); + break; + } + case DegreesWithHemisphere: + case DegreesMinutesWithHemisphere: + case DegreesMinutesSecondsWithHemisphere: { + if (d->lat < 0) + latStr.append(QString::fromLatin1(" S")); + else if (d->lat > 0) + latStr.append(QString::fromLatin1(" N")); + if (d->lng < 0) + longStr.append(QString::fromLatin1(" W")); + else if (d->lng > 0) + longStr.append(QString::fromLatin1(" E")); + break; + } + } + + if (qIsNaN(d->alt)) + return QString::fromLatin1("%1, %2").arg(latStr, longStr); + return QString::fromLatin1("%1, %2, %3m").arg(latStr, longStr, QString::number(d->alt)); +} + +QGeoCoordinate::QGeoCoordinate(QGeoCoordinatePrivate &dd): + d(&dd) +{ +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGeoCoordinate &coord) +{ + QDebugStateSaver saver(dbg); + double lat = coord.latitude(); + double lng = coord.longitude(); + + QTextStreamManipulator tsm = qSetRealNumberPrecision(11); + dbg << tsm; + dbg.nospace() << "QGeoCoordinate("; + if (qIsNaN(lat)) + dbg << '?'; + else + dbg << lat; + dbg << ", "; + if (qIsNaN(lng)) + dbg << '?'; + else + dbg << lng; + if (coord.type() == QGeoCoordinate::Coordinate3D) { + dbg << ", "; + dbg << coord.altitude(); + } + dbg << ')'; + return dbg; +} +#endif + +#ifndef QT_NO_DATASTREAM +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate) + + \relates QGeoCoordinate + + Writes the given \a coordinate to the specified \a stream. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate) +{ + stream << coordinate.latitude(); + stream << coordinate.longitude(); + stream << coordinate.altitude(); + return stream; +} +#endif + +#ifndef QT_NO_DATASTREAM +/*! + \fn QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate) + \relates QGeoCoordinate + + Reads a coordinate from the specified \a stream into the given + \a coordinate. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate) +{ + double value; + stream >> value; + coordinate.setLatitude(value); + stream >> value; + coordinate.setLongitude(value); + stream >> value; + coordinate.setAltitude(value); + return stream; +} +#endif + +/*! \fn uint qHash(const QGeoCoordinate &coordinate, uint seed = 0) + \relates QHash + \since Qt 5.7 + + Returns a hash value for \a coordinate, using \a seed to seed the calculation. +*/ +uint qHash(const QGeoCoordinate &coordinate, uint seed) +{ + QtPrivate::QHashCombine hash; + // north and south pole are geographically equivalent (no matter the longitude) + if (coordinate.latitude() != 90.0 && coordinate.latitude() != -90.0) + seed = hash(seed, coordinate.longitude()); + seed = hash(seed, coordinate.latitude()); + seed = hash(seed, coordinate.altitude()); + return seed; +} + +QT_END_NAMESPACE diff --git a/src/positioning/qgeocoordinate.h b/src/positioning/qgeocoordinate.h new file mode 100644 index 0000000..ddb6274 --- /dev/null +++ b/src/positioning/qgeocoordinate.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCOORDINATE_H +#define QGEOCOORDINATE_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QDebug; +class QDataStream; + +class QGeoCoordinatePrivate; +class Q_POSITIONING_EXPORT QGeoCoordinate +{ + Q_GADGET + + Q_PROPERTY(double latitude READ latitude WRITE setLatitude) + Q_PROPERTY(double longitude READ longitude WRITE setLongitude) + Q_PROPERTY(double altitude READ altitude WRITE setAltitude) + Q_PROPERTY(bool isValid READ isValid) + +public: + + enum CoordinateType { + InvalidCoordinate, + Coordinate2D, + Coordinate3D + }; + + enum CoordinateFormat { + Degrees, + DegreesWithHemisphere, + DegreesMinutes, + DegreesMinutesWithHemisphere, + DegreesMinutesSeconds, + DegreesMinutesSecondsWithHemisphere + }; + + QGeoCoordinate(); + QGeoCoordinate(double latitude, double longitude); + QGeoCoordinate(double latitude, double longitude, double altitude); + QGeoCoordinate(const QGeoCoordinate &other); + ~QGeoCoordinate(); + + QGeoCoordinate &operator=(const QGeoCoordinate &other); + + bool operator==(const QGeoCoordinate &other) const; + inline bool operator!=(const QGeoCoordinate &other) const { + return !operator==(other); + } + + bool isValid() const; + CoordinateType type() const; + + void setLatitude(double latitude); + double latitude() const; + + void setLongitude(double longitude); + double longitude() const; + + void setAltitude(double altitude); + double altitude() const; + + Q_INVOKABLE qreal distanceTo(const QGeoCoordinate &other) const; + Q_INVOKABLE qreal azimuthTo(const QGeoCoordinate &other) const; + + Q_INVOKABLE QGeoCoordinate atDistanceAndAzimuth(qreal distance, qreal azimuth, qreal distanceUp = 0.0) const; + + Q_INVOKABLE QString toString(CoordinateFormat format = DegreesMinutesSecondsWithHemisphere) const; + +private: + QGeoCoordinate(QGeoCoordinatePrivate &dd); + QSharedDataPointer d; + friend class QGeoCoordinatePrivate; + friend class QQuickGeoCoordinateAnimation; +}; + +Q_DECLARE_TYPEINFO(QGeoCoordinate, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_EXPORT QDebug operator<<(QDebug, const QGeoCoordinate &); +#endif + +Q_POSITIONING_EXPORT uint qHash(const QGeoCoordinate &coordinate, uint seed = 0); + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, const QGeoCoordinate &coordinate); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoCoordinate &coordinate); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoCoordinate) + +#endif diff --git a/src/positioning/qgeocoordinate_p.h b/src/positioning/qgeocoordinate_p.h new file mode 100644 index 0000000..e00cbfa --- /dev/null +++ b/src/positioning/qgeocoordinate_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCOORDINATE_P_H +#define QGEOCOORDINATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qgeocoordinate.h" + +QT_BEGIN_NAMESPACE + +class QGeoCoordinatePrivate : public QSharedData +{ +public: + QGeoCoordinatePrivate(); + QGeoCoordinatePrivate(const QGeoCoordinatePrivate &other); + ~QGeoCoordinatePrivate(); + + double lat; + double lng; + double alt; + + static void atDistanceAndAzimuth(const QGeoCoordinate &coord, + qreal distance, qreal azimuth, + double *lon, double *lat); + static const QGeoCoordinatePrivate *get(const QGeoCoordinate *c) { + return c->d.constData(); + } +}; + +class Q_POSITIONING_EXPORT QGeoMercatorCoordinatePrivate : public QGeoCoordinatePrivate +{ +public: + QGeoMercatorCoordinatePrivate(); + QGeoMercatorCoordinatePrivate(const QGeoMercatorCoordinatePrivate &other); + ~QGeoMercatorCoordinatePrivate(); + + double m_mercatorX; + double m_mercatorY; +}; + + +QT_END_NAMESPACE + +#endif // QGEOCOORDINATE_P_H diff --git a/src/positioning/qgeocoordinateobject.cpp b/src/positioning/qgeocoordinateobject.cpp new file mode 100644 index 0000000..7900e57 --- /dev/null +++ b/src/positioning/qgeocoordinateobject.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeocoordinateobject_p.h" + +QT_BEGIN_NAMESPACE + +/* + + Note: This class only purpose is to enable conversion between QGeoCoordinate and QDeclarativeGeoWaypoint. + Since QGeoCoordinate lives in the QtPositioning module, this class acts as a base for QDeclarativeGeoWaypoint, + and contains the bare minimum to convert/compare to a QGeoCoordinate + +*/ + +QGeoCoordinateObject::QGeoCoordinateObject(QObject *parent) : QObject(parent) +{ +} + +QGeoCoordinateObject::QGeoCoordinateObject(const QGeoCoordinate &c, QObject *parent) : QObject(parent) +{ + setCoordinate(c); +} + +QGeoCoordinateObject::~QGeoCoordinateObject() +{ + +} + +bool QGeoCoordinateObject::operator==(const QGeoCoordinateObject &other) const +{ + return m_coordinate == other.m_coordinate; +} + +bool QGeoCoordinateObject::operator==(const QGeoCoordinate &other) const +{ + return m_coordinate == other; +} + +QGeoCoordinate QGeoCoordinateObject::coordinate() const +{ + return m_coordinate; +} + +void QGeoCoordinateObject::setCoordinate(const QGeoCoordinate &c) +{ + if (c == m_coordinate) + return; + + m_coordinate = c; + emit coordinateChanged(); +} + +QT_END_NAMESPACE + + diff --git a/src/positioning/qgeocoordinateobject_p.h b/src/positioning/qgeocoordinateobject_p.h new file mode 100644 index 0000000..b1d794b --- /dev/null +++ b/src/positioning/qgeocoordinateobject_p.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCOORDINATEOBJECT_P_H +#define QGEOCOORDINATEOBJECT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_PRIVATE_EXPORT QGeoCoordinateObject : public QObject +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) + +public: + QGeoCoordinateObject(QObject *parent = 0); + QGeoCoordinateObject(const QGeoCoordinate &c, QObject *parent = 0); + virtual ~QGeoCoordinateObject(); + + bool operator==(const QGeoCoordinate &other) const; + bool operator==(const QGeoCoordinateObject &other) const; + inline bool operator!=(const QGeoCoordinate &other) const { + return !operator==(other); + } + inline bool operator!=(const QGeoCoordinateObject &other) const { + return !operator==(other); + } + + QGeoCoordinate coordinate() const; + void setCoordinate(const QGeoCoordinate &c); + +Q_SIGNALS: + void coordinateChanged(); + +protected: + QGeoCoordinate m_coordinate; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoCoordinateObject*) + +#endif // QGEOCOORDINATEOBJECT_P_H diff --git a/src/positioning/qgeolocation.cpp b/src/positioning/qgeolocation.cpp new file mode 100644 index 0000000..62e1a6a --- /dev/null +++ b/src/positioning/qgeolocation.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeolocation.h" +#include "qgeolocation_p.h" + +QT_USE_NAMESPACE + +QGeoLocationPrivate::QGeoLocationPrivate() + : QSharedData() +{ +} + +QGeoLocationPrivate::QGeoLocationPrivate(const QGeoLocationPrivate &other) + : QSharedData() +{ + this->address = other.address; + this->coordinate = other.coordinate; + this->viewport = other.viewport; +} + +QGeoLocationPrivate::~QGeoLocationPrivate() +{ +} + +bool QGeoLocationPrivate::operator==(const QGeoLocationPrivate &other) const +{ + return (this->address == other.address + && this->coordinate == other.coordinate + && this->viewport == other.viewport); + +} + +bool QGeoLocationPrivate::isEmpty() const +{ + return (address.isEmpty() + && !coordinate.isValid() + && viewport.isEmpty() + ); +} + +/*! + \class QGeoLocation + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \ingroup QtLocation-places + \ingroup QtLocation-places-data + \since 5.2 + + \brief The QGeoLocation class represents basic information about a location. + + A QGeoLocation consists of a coordinate and corresponding address, along with an optional + bounding box which is the recommended region to be displayed when viewing the location. +*/ + +/*! + \fn bool QGeoLocation::operator!=(const QGeoLocation &other) const + + Returns true if this location is not equal to \a other, otherwise returns false. +*/ + +/*! + Constructs an new location object. +*/ +QGeoLocation::QGeoLocation() + : d(new QGeoLocationPrivate) +{ +} + +/*! + Constructs a copy of \a other +*/ +QGeoLocation::QGeoLocation(const QGeoLocation &other) + :d(other.d) +{ +} + +/*! + Destroys the location object. +*/ +QGeoLocation::~QGeoLocation() +{ +} + +/*! + Assigns \a other to this location and returns a reference to this location. +*/ +QGeoLocation &QGeoLocation::operator =(const QGeoLocation &other) +{ + if (this == &other) + return *this; + + d = other.d; + return *this; +} + +/*! + Returns true if this location is equal to \a other, + otherwise returns false. +*/ +bool QGeoLocation::operator==(const QGeoLocation &other) const +{ + return (*(d.constData()) == *(other.d.constData())); +} + +/*! + Returns the address of the location. +*/ +QGeoAddress QGeoLocation::address() const +{ + return d->address; +} + +/*! + Sets the \a address of the location. +*/ +void QGeoLocation::setAddress(const QGeoAddress &address) +{ + d->address = address; +} + +/*! + Returns the coordinate of the location. +*/ +QGeoCoordinate QGeoLocation::coordinate() const +{ + return d->coordinate; +} + +/*! + Sets the \a coordinate of the location. +*/ +void QGeoLocation::setCoordinate(const QGeoCoordinate &coordinate) +{ + d->coordinate = coordinate; +} + +/*! + Returns a bounding box which represents the recommended region + to display when viewing this location. + + For example, a building's location may have a region centered around the building, + but the region is large enough to show it's immediate surrounding geographical + context. +*/ +QGeoRectangle QGeoLocation::boundingBox() const +{ + return d->viewport; +} + +/*! + Sets the \a boundingBox of the location. +*/ +void QGeoLocation::setBoundingBox(const QGeoRectangle &boundingBox) +{ + d->viewport = boundingBox; +} + +/*! + Returns true if all fields of the location are 0; otherwise returns false. +*/ +bool QGeoLocation::isEmpty() const +{ + return d->isEmpty(); +} diff --git a/src/positioning/qgeolocation.h b/src/positioning/qgeolocation.h new file mode 100644 index 0000000..580b2fb --- /dev/null +++ b/src/positioning/qgeolocation.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOLOCATION_H +#define QGEOLOCATION_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoAddress; +class QGeoCoordinate; +class QGeoRectangle; +class QGeoLocationPrivate; + +class Q_POSITIONING_EXPORT QGeoLocation +{ +public: + QGeoLocation(); + QGeoLocation(const QGeoLocation &other); + + ~QGeoLocation(); + + QGeoLocation &operator=(const QGeoLocation &other); + + bool operator==(const QGeoLocation &other) const; + bool operator!=(const QGeoLocation &other) const { + return !(other == *this); + } + + QGeoAddress address() const; + void setAddress(const QGeoAddress &address); + QGeoCoordinate coordinate() const; + void setCoordinate(const QGeoCoordinate &position); + QGeoRectangle boundingBox() const; + void setBoundingBox(const QGeoRectangle &box); + + bool isEmpty() const; + +private: + QSharedDataPointer d; +}; + +Q_DECLARE_TYPEINFO(QGeoLocation, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoLocation) + +#endif diff --git a/src/positioning/qgeolocation_p.h b/src/positioning/qgeolocation_p.h new file mode 100644 index 0000000..a12e4cb --- /dev/null +++ b/src/positioning/qgeolocation_p.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOLOCATION_P_H +#define QGEOLOCATION_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoLocationPrivate : public QSharedData +{ +public: + QGeoLocationPrivate(); + QGeoLocationPrivate(const QGeoLocationPrivate &other); + + ~QGeoLocationPrivate(); + + bool operator==(const QGeoLocationPrivate &other) const; + + bool isEmpty() const; + + QGeoAddress address; + QGeoCoordinate coordinate; + QGeoRectangle viewport; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeopath.cpp b/src/positioning/qgeopath.cpp new file mode 100644 index 0000000..d86f6d0 --- /dev/null +++ b/src/positioning/qgeopath.cpp @@ -0,0 +1,788 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopath.h" +#include "qgeopath_p.h" + +#include "qgeocoordinate.h" +#include "qnumeric.h" +#include "qlocationutils_p.h" +#include "qwebmercator_p.h" + +#include "qdoublevector2d_p.h" +#include "qdoublevector3d_p.h" +QT_BEGIN_NAMESPACE + +/*! + \class QGeoPath + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.9 + + \brief The QGeoPath class defines a geographic path. + + The path is defined by an ordered list of QGeoCoordinates. + + Each two adjacent elements in the path are intended to be connected + together by the shortest line segment of constant bearing passing + through both elements. + This type of connection can cross the dateline in the longitudinal direction, + but never crosses the poles. + + This is relevant for the calculation of the bounding box returned by + \l QGeoShape::boundingGeoRectangle() for this shape, which will have the latitude of + the top left corner set to the maximum latitude in the path point set. + Similarly, the latitude of the bottom right corner will be the minimum latitude + in the path point set. + + This class is a \l Q_GADGET. + It can be \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. + + A QGeoPath is both invalid and empty if it contains no coordinate. + + \note A default constructed QGeoPath is both invalid and empty as it does not contain any coordinates. +*/ + +/*! + \property QGeoPath::path + \brief This property holds the list of coordinates for the geo path. + + \note The coordinates cannot be processed in place. To change the value + of this property, retrieve the complete list of coordinates, process them, + and assign the new value to the property. +*/ + +inline QGeoPathPrivate *QGeoPath::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QGeoPathPrivate *QGeoPath::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +struct PathVariantConversions +{ + PathVariantConversions() + { + QMetaType::registerConverter(); + QMetaType::registerConverter(); + } +}; + +Q_GLOBAL_STATIC(PathVariantConversions, initPathConversions) + +/*! + Constructs a new, empty geo path. +*/ +QGeoPath::QGeoPath() +: QGeoShape(new QGeoPathPrivate(QGeoShape::PathType)) +{ + initPathConversions(); +} + +/*! + Constructs a new geo path from a list of coordinates + (\a path and \a width). +*/ +QGeoPath::QGeoPath(const QList &path, const qreal &width) +: QGeoShape(new QGeoPathPrivate(QGeoShape::PathType, path, width)) +{ + initPathConversions(); +} + +/*! + Constructs a new geo path from the contents of \a other. +*/ +QGeoPath::QGeoPath(const QGeoPath &other) +: QGeoShape(other) +{ + initPathConversions(); +} + +/*! + Constructs a new geo path from the contents of \a other. +*/ +QGeoPath::QGeoPath(const QGeoShape &other) +: QGeoShape(other) +{ + initPathConversions(); + if (type() != QGeoShape::PathType) + d_ptr = new QGeoPathPrivate(QGeoShape::PathType); +} + +/*! + Destroys this path. +*/ +QGeoPath::~QGeoPath() {} + +/*! + Assigns \a other to this geo path and returns a reference to this geo path. +*/ +QGeoPath &QGeoPath::operator=(const QGeoPath &other) +{ + QGeoShape::operator=(other); + return *this; +} + +/*! + Returns whether this geo path is equal to \a other. +*/ +bool QGeoPath::operator==(const QGeoPath &other) const +{ + Q_D(const QGeoPath); + return *d == *other.d_func(); +} + +/*! + Returns whether this geo path is not equal to \a other. +*/ +bool QGeoPath::operator!=(const QGeoPath &other) const +{ + Q_D(const QGeoPath); + return !(*d == *other.d_func()); +} + +/*! + Sets all the elements of the \a path. +*/ +void QGeoPath::setPath(const QList &path) +{ + Q_D(QGeoPath); + return d->setPath(path); +} + +/*! + Returns all the elements of the path. +*/ +const QList &QGeoPath::path() const +{ + Q_D(const QGeoPath); + return d->path(); +} +/*! + Sets all the elements of the path. + + \internal +*/ +void QGeoPath::setVariantPath(const QVariantList &path) +{ + Q_D(QGeoPath); + QList p; + for (const auto &c: path) { + if (c.canConvert()) + p << c.value(); + } + d->setPath(p); +} +/*! + Returns all the elements of the path. + + \internal +*/ +QVariantList QGeoPath::variantPath() const +{ + Q_D(const QGeoPath); + QVariantList p; + for (const auto &c: d->path()) + p << QVariant::fromValue(c); + return p; +} + + +/*! + \property QGeoPath::width + + \brief the width of the path in meters. +*/ +void QGeoPath::setWidth(const qreal &width) +{ + Q_D(QGeoPath); + d->setWidth(width); +} + +/*! + Returns the width of the path, in meters. This information is used in the \l contains method. + The default value is 0. +*/ +qreal QGeoPath::width() const +{ + Q_D(const QGeoPath); + return d->width(); +} + +/*! + Translates this geo path by \a degreesLatitude northwards and \a degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. +*/ +void QGeoPath::translate(double degreesLatitude, double degreesLongitude) +{ + Q_D(QGeoPath); + d->translate(degreesLatitude, degreesLongitude); +} + +/*! + Returns a copy of this geo path translated by \a degreesLatitude northwards and + \a degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. + + \sa translate() +*/ +QGeoPath QGeoPath::translated(double degreesLatitude, double degreesLongitude) const +{ + QGeoPath result(*this); + result.translate(degreesLatitude, degreesLongitude); + return result; +} + +/*! + Returns the length of the path, in meters, from the element \a indexFrom to the element \a indexTo. + The length is intended to be the sum of the shortest distances for each pair of adjacent points. +*/ +double QGeoPath::length(int indexFrom, int indexTo) const +{ + Q_D(const QGeoPath); + return d->length(indexFrom, indexTo); +} + +/*! + Returns the number of elements in the path. + + \since 5.10 +*/ +int QGeoPath::size() const +{ + Q_D(const QGeoPath); + return d->size(); +} + +/*! + Appends \a coordinate to the path. +*/ +void QGeoPath::addCoordinate(const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPath); + d->addCoordinate(coordinate); +} + +/*! + Inserts \a coordinate at the specified \a index. +*/ +void QGeoPath::insertCoordinate(int index, const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPath); + d->insertCoordinate(index, coordinate); +} + +/*! + Replaces the path element at the specified \a index with \a coordinate. +*/ +void QGeoPath::replaceCoordinate(int index, const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPath); + d->replaceCoordinate(index, coordinate); +} + +/*! + Returns the coordinate at \a index . +*/ +QGeoCoordinate QGeoPath::coordinateAt(int index) const +{ + Q_D(const QGeoPath); + return d->coordinateAt(index); +} + +/*! + Returns true if the path contains \a coordinate as one of the elements. +*/ +bool QGeoPath::containsCoordinate(const QGeoCoordinate &coordinate) const +{ + Q_D(const QGeoPath); + return d->containsCoordinate(coordinate); +} + +/*! + Removes the last occurrence of \a coordinate from the path. +*/ +void QGeoPath::removeCoordinate(const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPath); + d->removeCoordinate(coordinate); +} + +/*! + Removes element at position \a index from the path. +*/ +void QGeoPath::removeCoordinate(int index) +{ + Q_D(QGeoPath); + d->removeCoordinate(index); +} + +/*! + Returns the geo path properties as a string. +*/ +QString QGeoPath::toString() const +{ + if (type() != QGeoShape::PathType) { + qWarning("Not a path"); + return QStringLiteral("QGeoPath(not a path)"); + } + + QString pathString; + for (const auto &p : path()) + pathString += p.toString() + QLatin1Char(','); + + return QStringLiteral("QGeoPath([ %1 ])").arg(pathString); +} + +/******************************************************************************* + * QGeoPathPrivate +*******************************************************************************/ + +QGeoPathPrivate::QGeoPathPrivate(QGeoShape::ShapeType type) +: QGeoShapePrivate(type), m_width(0), m_clipperDirty(true) +{ +} + +QGeoPathPrivate::QGeoPathPrivate(QGeoShape::ShapeType type, const QList &path, const qreal width) +: QGeoShapePrivate(type), m_width(0), m_clipperDirty(true) +{ + setPath(path); + setWidth(width); +} + +QGeoPathPrivate::QGeoPathPrivate(const QGeoPathPrivate &other) +: QGeoShapePrivate(other.type), m_path(other.m_path), + m_deltaXs(other.m_deltaXs), m_minX(other.m_minX), m_maxX(other.m_maxX), m_minLati(other.m_minLati), + m_maxLati(other.m_maxLati), m_bbox(other.m_bbox), m_width(other.m_width), m_clipperDirty(true) +{ +} + +QGeoPathPrivate::~QGeoPathPrivate() {} + +QGeoShapePrivate *QGeoPathPrivate::clone() const +{ + return new QGeoPathPrivate(*this); +} + +bool QGeoPathPrivate::operator==(const QGeoShapePrivate &other) const +{ + if (!QGeoShapePrivate::operator==(other)) + return false; + + const QGeoPathPrivate &otherPath = static_cast(other); + if (m_path.size() != otherPath.m_path.size()) + return false; + + if (type == QGeoShape::PathType) + return m_width == otherPath.m_width && m_path == otherPath.m_path; + else + return m_path == otherPath.m_path; +} + +bool QGeoPathPrivate::isValid() const +{ + if (type == QGeoShape::PathType) + return !isEmpty(); + else + return m_path.size() > 2; + +} + +bool QGeoPathPrivate::isEmpty() const +{ + return m_path.isEmpty(); // this should perhaps return geometric emptiness, less than 2 points for line, or empty polygon for polygons +} + +const QList &QGeoPathPrivate::path() const +{ + return m_path; +} + +void QGeoPathPrivate::setPath(const QList &path) +{ + for (const QGeoCoordinate &c: path) + if (!c.isValid()) + return; + m_path = path; + computeBoundingBox(); +} + +qreal QGeoPathPrivate::width() const +{ + return m_width; +} + +void QGeoPathPrivate::setWidth(const qreal &width) +{ + if (qIsNaN(width) || width < 0.0) + return; + m_width = width; +} + +double QGeoPathPrivate::length(int indexFrom, int indexTo) const +{ + if (path().isEmpty()) + return 0.0; + + bool wrap = indexTo == -1; + if (indexTo < 0 || indexTo >= path().size()) + indexTo = path().size() - 1; + double len = 0.0; + // TODO: consider calculating the length of the actual rhumb line segments + // instead of the shortest path from A to B. + for (int i = indexFrom; i < indexTo; i++) + len += m_path[i].distanceTo(m_path[i+1]); + if (wrap) + len += m_path.last().distanceTo(m_path.first()); + return len; +} + +int QGeoPathPrivate::size() const +{ + return m_path.size(); +} + +/*! + Returns true if coordinate is present in m_path. +*/ +bool QGeoPathPrivate::contains(const QGeoCoordinate &coordinate) const +{ + if (type == QGeoShape::PathType) + return lineContains(coordinate); + else + return polygonContains(coordinate); +} + +bool QGeoPathPrivate::lineContains(const QGeoCoordinate &coordinate) const +{ + // Unoptimized approach: + // - consider each segment of the path + // - project it into mercator space (rhumb lines are straight in mercator space) + // - find closest point to coordinate + // - unproject the closest point + // - calculate coordinate to closest point distance with distanceTo() + // - if not within lineRadius, advance + // + // To keep wrapping into the equation: + // If the mercator x value of a coordinate of the line, or the coordinate parameter, is less + // than mercator(m_bbox).x, add that to the conversion. + + double lineRadius = qMax(width() * 0.5, 0.2); // minimum radius: 20cm + + if (!m_path.size()) + return false; + else if (m_path.size() == 1) + return (m_path[0].distanceTo(coordinate) <= lineRadius); + + double leftBoundMercator = QWebMercator::coordToMercator(m_bbox.topLeft()).x(); + + QDoubleVector2D p = QWebMercator::coordToMercator(coordinate); + if (p.x() < leftBoundMercator) + p.setX(p.x() + leftBoundMercator); // unwrap X + + QDoubleVector2D a; + QDoubleVector2D b; + if (m_path.size()) { + a = QWebMercator::coordToMercator(m_path[0]); + if (a.x() < leftBoundMercator) + a.setX(a.x() + leftBoundMercator); // unwrap X + } + for (int i = 1; i < m_path.size(); i++) { + b = QWebMercator::coordToMercator(m_path[i]); + if (b.x() < leftBoundMercator) + b.setX(b.x() + leftBoundMercator); // unwrap X + if (b == a) + continue; + + double u = ((p.x() - a.x()) * (b.x() - a.x()) + (p.y() - a.y()) * (b.y() - a.y()) ) / (b - a).lengthSquared(); + QDoubleVector2D intersection(a.x() + u * (b.x() - a.x()) , a.y() + u * (b.y() - a.y()) ); + + QDoubleVector2D candidate = ( (p-a).length() < (p-b).length() ) ? a : b; + + if (u > 0 && u < 1 + && (p-intersection).length() < (p-candidate).length() ) // And it falls in the segment + candidate = intersection; + + + if (candidate.x() > 1.0) + candidate.setX(candidate.x() - leftBoundMercator); // wrap X + + QGeoCoordinate closest = QWebMercator::mercatorToCoord(candidate); + + double distanceMeters = coordinate.distanceTo(closest); + if (distanceMeters <= lineRadius) + return true; + + // swap + a = b; + } + + // Last check if the coordinate is on the left of leftBoundMercator, but close enough to + // m_path[0] + return (m_path[0].distanceTo(coordinate) <= lineRadius); +} + +bool QGeoPathPrivate::polygonContains(const QGeoCoordinate &coordinate) const +{ + if (m_clipperDirty) + const_cast(this)->updateClipperPath(); + + QDoubleVector2D coord = QWebMercator::coordToMercator(coordinate); + double tlx = QWebMercator::coordToMercator(m_bbox.topLeft()).x(); + if (coord.x() < tlx) + coord.setX(coord.x() + 1.0); + + IntPoint intCoord = QClipperUtils::toIntPoint(coord); + return c2t::clip2tri::pointInPolygon(intCoord, m_clipperPath) != 0; +} + +QGeoCoordinate QGeoPathPrivate::center() const +{ + return boundingGeoRectangle().center(); +} + +QGeoRectangle QGeoPathPrivate::boundingGeoRectangle() const +{ + return m_bbox; +} + +void QGeoPathPrivate::extendShape(const QGeoCoordinate &coordinate) +{ + if (!coordinate.isValid() || contains(coordinate)) + return; + addCoordinate(coordinate); +} + +void QGeoPathPrivate::translate(double degreesLatitude, double degreesLongitude) +{ + if (degreesLatitude > 0.0) + degreesLatitude = qMin(degreesLatitude, 90.0 - m_maxLati); + else + degreesLatitude = qMax(degreesLatitude, -90.0 - m_minLati); + for (QGeoCoordinate &p: m_path) { + p.setLatitude(p.latitude() + degreesLatitude); + p.setLongitude(QLocationUtils::wrapLong(p.longitude() + degreesLongitude)); + } + m_bbox.translate(degreesLatitude, degreesLongitude); + m_minLati += degreesLatitude; + m_maxLati += degreesLatitude; +} + +void QGeoPathPrivate::addCoordinate(const QGeoCoordinate &coordinate) +{ + if (!coordinate.isValid()) + return; + m_path.append(coordinate); + updateBoundingBox(); +} + +void QGeoPathPrivate::insertCoordinate(int index, const QGeoCoordinate &coordinate) +{ + if (index < 0 || index > m_path.size() || !coordinate.isValid()) + return; + + m_path.insert(index, coordinate); + computeBoundingBox(); +} + +void QGeoPathPrivate::replaceCoordinate(int index, const QGeoCoordinate &coordinate) +{ + if (index < 0 || index >= m_path.size() || !coordinate.isValid()) + return; + + m_path[index] = coordinate; + computeBoundingBox(); +} + +QGeoCoordinate QGeoPathPrivate::coordinateAt(int index) const +{ + if (index < 0 || index >= m_path.size()) + return QGeoCoordinate(); + + return m_path.at(index); +} + +bool QGeoPathPrivate::containsCoordinate(const QGeoCoordinate &coordinate) const +{ + return m_path.indexOf(coordinate) > -1; +} + +void QGeoPathPrivate::removeCoordinate(const QGeoCoordinate &coordinate) +{ + int index = m_path.lastIndexOf(coordinate); + removeCoordinate(index); +} + +void QGeoPathPrivate::removeCoordinate(int index) +{ + if (index < 0 || index >= m_path.size()) + return; + + m_path.removeAt(index); + computeBoundingBox(); +} + +void QGeoPathPrivate::computeBoundingBox() +{ + m_clipperDirty = true; + if (m_path.isEmpty()) { + m_deltaXs.clear(); + m_minX = qInf(); + m_maxX = -qInf(); + m_minLati = qInf(); + m_maxLati = -qInf(); + m_bbox = QGeoRectangle(); + return; + } + + m_minLati = m_maxLati = m_path.at(0).latitude(); + int minId = 0; + int maxId = 0; + m_deltaXs.resize(m_path.size()); + m_deltaXs[0] = m_minX = m_maxX = 0.0; + + for (int i = 1; i < m_path.size(); i++) { + const QGeoCoordinate &geoFrom = m_path.at(i-1); + const QGeoCoordinate &geoTo = m_path.at(i); + double longiFrom = geoFrom.longitude(); + double longiTo = geoTo.longitude(); + double deltaLongi = longiTo - longiFrom; + if (qAbs(deltaLongi) > 180.0) { + if (longiTo > 0.0) + longiTo -= 360.0; + else + longiTo += 360.0; + deltaLongi = longiTo - longiFrom; + } + m_deltaXs[i] = m_deltaXs[i-1] + deltaLongi; + if (m_deltaXs[i] < m_minX) { + m_minX = m_deltaXs[i]; + minId = i; + } + if (m_deltaXs[i] > m_maxX) { + m_maxX = m_deltaXs[i]; + maxId = i; + } + if (geoTo.latitude() > m_maxLati) + m_maxLati = geoTo.latitude(); + if (geoTo.latitude() < m_minLati) + m_minLati = geoTo.latitude(); + } + + m_bbox = QGeoRectangle(QGeoCoordinate(m_maxLati, m_path.at(minId).longitude()), + QGeoCoordinate(m_minLati, m_path.at(maxId).longitude())); +} + +void QGeoPathPrivate::updateBoundingBox() +{ + m_clipperDirty = true; + if (m_path.isEmpty()) { + m_deltaXs.clear(); + m_minX = qInf(); + m_maxX = -qInf(); + m_minLati = qInf(); + m_maxLati = -qInf(); + m_bbox = QGeoRectangle(); + return; + } else if (m_path.size() == 1) { // was 0 now is 1 + m_deltaXs.resize(1); + m_deltaXs[0] = m_minX = m_maxX = 0.0; + m_minLati = m_maxLati = m_path.at(0).latitude(); + m_bbox = QGeoRectangle(QGeoCoordinate(m_maxLati, m_path.at(0).longitude()), + QGeoCoordinate(m_minLati, m_path.at(0).longitude())); + return; + } else if ( m_path.size() != m_deltaXs.size() + 1 ) { // this case should not happen + computeBoundingBox(); // something went wrong + return; + } + + const QGeoCoordinate &geoFrom = m_path.at(m_path.size()-2); + const QGeoCoordinate &geoTo = m_path.last(); + double longiFrom = geoFrom.longitude(); + double longiTo = geoTo.longitude(); + double deltaLongi = longiTo - longiFrom; + if (qAbs(deltaLongi) > 180.0) { + if (longiTo > 0.0) + longiTo -= 360.0; + else + longiTo += 360.0; + deltaLongi = longiTo - longiFrom; + } + + m_deltaXs.push_back(m_deltaXs.last() + deltaLongi); + double currentMinLongi = m_bbox.topLeft().longitude(); + double currentMaxLongi = m_bbox.bottomRight().longitude(); + if (m_deltaXs.last() < m_minX) { + m_minX = m_deltaXs.last(); + currentMinLongi = geoTo.longitude(); + } + if (m_deltaXs.last() > m_maxX) { + m_maxX = m_deltaXs.last(); + currentMaxLongi = geoTo.longitude(); + } + if (geoTo.latitude() > m_maxLati) + m_maxLati = geoTo.latitude(); + if (geoTo.latitude() < m_minLati) + m_minLati = geoTo.latitude(); + m_bbox = QGeoRectangle(QGeoCoordinate(m_maxLati, currentMinLongi), + QGeoCoordinate(m_minLati, currentMaxLongi)); +} + +void QGeoPathPrivate::updateClipperPath() +{ + m_clipperDirty = false; + double tlx = QWebMercator::coordToMercator(m_bbox.topLeft()).x(); + QList preservedPath; + for (const QGeoCoordinate &c : m_path) { + QDoubleVector2D crd = QWebMercator::coordToMercator(c); + if (crd.x() < tlx) + crd.setX(crd.x() + 1.0); + preservedPath << crd; + } + m_clipperPath = QClipperUtils::qListToPath(preservedPath); +} + +QT_END_NAMESPACE diff --git a/src/positioning/qgeopath.h b/src/positioning/qgeopath.h new file mode 100644 index 0000000..178ac3b --- /dev/null +++ b/src/positioning/qgeopath.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPATH_H +#define QGEOPATH_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoCoordinate; +class QGeoPathPrivate; + +class Q_POSITIONING_EXPORT QGeoPath : public QGeoShape +{ + Q_GADGET + Q_PROPERTY(QVariantList path READ variantPath WRITE setVariantPath) + Q_PROPERTY(qreal width READ width WRITE setWidth) + +public: + QGeoPath(); + QGeoPath(const QList &path, const qreal &width = 0.0); + QGeoPath(const QGeoPath &other); + QGeoPath(const QGeoShape &other); + + ~QGeoPath(); + + QGeoPath &operator=(const QGeoPath &other); + + using QGeoShape::operator==; + bool operator==(const QGeoPath &other) const; + + using QGeoShape::operator!=; + bool operator!=(const QGeoPath &other) const; + + void setPath(const QList &path); + const QList &path() const; + void setVariantPath(const QVariantList &path); + QVariantList variantPath() const; + + void setWidth(const qreal &width); + qreal width() const; + + Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude); + Q_INVOKABLE QGeoPath translated(double degreesLatitude, double degreesLongitude) const; + Q_INVOKABLE double length(int indexFrom = 0, int indexTo = -1) const; + Q_INVOKABLE int size() const; + Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void insertCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE void replaceCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE QGeoCoordinate coordinateAt(int index) const; + Q_INVOKABLE bool containsCoordinate(const QGeoCoordinate &coordinate) const; + Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(int index); + + Q_INVOKABLE QString toString() const; + +private: + inline QGeoPathPrivate *d_func(); + inline const QGeoPathPrivate *d_func() const; +}; + +Q_DECLARE_TYPEINFO(QGeoPath, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoPath) + +#endif // QGEOPATH_H diff --git a/src/positioning/qgeopath_p.h b/src/positioning/qgeopath_p.h new file mode 100644 index 0000000..ecba2dc --- /dev/null +++ b/src/positioning/qgeopath_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPATH_P_H +#define QGEOPATH_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeoshape_p.h" +#include "qgeocoordinate.h" +#include "qlocationutils_p.h" +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoPathPrivate : public QGeoShapePrivate +{ +public: + QGeoPathPrivate(QGeoShape::ShapeType type); + QGeoPathPrivate(QGeoShape::ShapeType type, const QList &path, const qreal width = 0.0); + QGeoPathPrivate(const QGeoPathPrivate &other); + ~QGeoPathPrivate(); + + bool isValid() const override; + bool isEmpty() const override; + bool contains(const QGeoCoordinate &coordinate) const override; + bool lineContains(const QGeoCoordinate &coordinate) const; + bool polygonContains(const QGeoCoordinate &coordinate) const; + + QGeoCoordinate center() const override; + QGeoRectangle boundingGeoRectangle() const override; + void extendShape(const QGeoCoordinate &coordinate) override; + void translate(double degreesLatitude, double degreesLongitude); + + QGeoShapePrivate *clone() const override; + + bool operator==(const QGeoShapePrivate &other) const override; + + const QList &path() const; + void setPath(const QList &path); + qreal width() const; + void setWidth(const qreal &width); + double length(int indexFrom, int indexTo) const; + int size() const; + void addCoordinate(const QGeoCoordinate &coordinate); + void insertCoordinate(int index, const QGeoCoordinate &coordinate); + void replaceCoordinate(int index, const QGeoCoordinate &coordinate); + QGeoCoordinate coordinateAt(int index) const; + bool containsCoordinate(const QGeoCoordinate &coordinate) const; + void removeCoordinate(const QGeoCoordinate &coordinate); + void removeCoordinate(int index); + void computeBoundingBox(); + void updateBoundingBox(); + void updateClipperPath(); + + QList m_path; + QVector m_deltaXs; // longitude deltas from m_path[0] + double m_minX; // minimum value inside deltaXs + double m_maxX; // maximum value inside deltaXs + double m_minLati; // minimum latitude. paths do not wrap around through the poles + double m_maxLati; // minimum latitude. paths do not wrap around through the poles + QGeoRectangle m_bbox; + qreal m_width; + bool m_clipperDirty; + QtClipperLib::Path m_clipperPath; +}; + +QT_END_NAMESPACE + +#endif // QGEOPATH_P_H diff --git a/src/positioning/qgeopolygon.cpp b/src/positioning/qgeopolygon.cpp new file mode 100644 index 0000000..1dafd1e --- /dev/null +++ b/src/positioning/qgeopolygon.cpp @@ -0,0 +1,327 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopolygon.h" +#include "qgeopath_p.h" + +#include "qgeocoordinate.h" +#include "qnumeric.h" +#include "qlocationutils_p.h" +#include "qwebmercator_p.h" + +#include "qdoublevector2d_p.h" +#include "qdoublevector3d_p.h" +#include "qwebmercator_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoPolygon + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.10 + + \brief The QGeoPolygon class defines a geographic polygon. + + The polygon is defined by an ordered list of QGeoCoordinates representing its perimeter. + + Each two adjacent elements in this list are intended to be connected + together by the shortest line segment of constant bearing passing + through both elements. + This type of connection can cross the date line in the longitudinal direction, + but never crosses the poles. + + This is relevant for the calculation of the bounding box returned by + \l QGeoShape::boundingGeoRectangle() for this shape, which will have the latitude of + the top left corner set to the maximum latitude in the path point set. + Similarly, the latitude of the bottom right corner will be the minimum latitude + in the path point set. + + This class is a \l Q_GADGET. + It can be \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. +*/ + +/* + \property QGeoPolygon::path + \brief This property holds the list of coordinates for the geo polygon. + + The polygon is both invalid and empty if it contains no coordinate. + + A default constructed QGeoPolygon is therefore invalid. +*/ + +inline QGeoPolygonPrivate *QGeoPolygon::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QGeoPolygonPrivate *QGeoPolygon::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +struct PolygonVariantConversions +{ + PolygonVariantConversions() + { + QMetaType::registerConverter(); + QMetaType::registerConverter(); + } +}; + +Q_GLOBAL_STATIC(PolygonVariantConversions, initPolygonConversions) + +/*! + Constructs a new, empty geo polygon. +*/ +QGeoPolygon::QGeoPolygon() +: QGeoShape(new QGeoPolygonPrivate(QGeoShape::PolygonType)) +{ + initPolygonConversions(); +} + +/*! + Constructs a new geo \a polygon from a list of coordinates. +*/ +QGeoPolygon::QGeoPolygon(const QList &path) +: QGeoShape(new QGeoPolygonPrivate(QGeoShape::PolygonType, path)) +{ + initPolygonConversions(); +} + +/*! + Constructs a new geo polygon from the contents of \a other. +*/ +QGeoPolygon::QGeoPolygon(const QGeoPolygon &other) +: QGeoShape(other) +{ + initPolygonConversions(); +} + +/*! + Constructs a new geo polygon from the contents of \a other. +*/ +QGeoPolygon::QGeoPolygon(const QGeoShape &other) +: QGeoShape(other) +{ + initPolygonConversions(); + if (type() != QGeoShape::PolygonType) + d_ptr = new QGeoPolygonPrivate(QGeoShape::PolygonType); +} + +/*! + Destroys this polygon. +*/ +QGeoPolygon::~QGeoPolygon() {} + +/*! + Assigns \a other to this geo polygon and returns a reference to this geo polygon. +*/ +QGeoPolygon &QGeoPolygon::operator=(const QGeoPolygon &other) +{ + QGeoShape::operator=(other); + return *this; +} + +/*! + Returns whether this geo polygon is equal to \a other. +*/ +bool QGeoPolygon::operator==(const QGeoPolygon &other) const +{ + Q_D(const QGeoPolygon); + return *d == *other.d_func(); +} + +/*! + Returns whether this geo polygon is not equal to \a other. +*/ +bool QGeoPolygon::operator!=(const QGeoPolygon &other) const +{ + Q_D(const QGeoPolygon); + return !(*d == *other.d_func()); +} + +/*! + Sets the \a polygon from a list of coordinates. +*/ +void QGeoPolygon::setPath(const QList &path) +{ + Q_D(QGeoPolygon); + return d->setPath(path); +} + +/*! + Returns all the elements. +*/ +const QList &QGeoPolygon::path() const +{ + Q_D(const QGeoPolygon); + return d->path(); +} + +/*! + Translates this geo polygon by \a degreesLatitude northwards and \a degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. +*/ +void QGeoPolygon::translate(double degreesLatitude, double degreesLongitude) +{ + Q_D(QGeoPolygon); + d->translate(degreesLatitude, degreesLongitude); +} + +/*! + Returns a copy of this geo polygon translated by \a degreesLatitude northwards and + \a degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. + + \sa translate() +*/ +QGeoPolygon QGeoPolygon::translated(double degreesLatitude, double degreesLongitude) const +{ + QGeoPolygon result(*this); + result.translate(degreesLatitude, degreesLongitude); + return result; +} + +/*! + Returns the length of the polygon's perimeter, in meters, from the element \a indexFrom to the element \a indexTo. + The length is intended to be the sum of the shortest distances for each pair of adjacent points. +*/ +double QGeoPolygon::length(int indexFrom, int indexTo) const +{ + Q_D(const QGeoPolygon); + return d->length(indexFrom, indexTo); +} + +/*! + Returns the number of elements in the polygon. + + \since 5.10 +*/ +int QGeoPolygon::size() const +{ + Q_D(const QGeoPolygon); + return d->size(); +} + +/*! + Appends \a coordinate to the polygon. +*/ +void QGeoPolygon::addCoordinate(const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPolygon); + d->addCoordinate(coordinate); +} + +/*! + Inserts \a coordinate at the specified \a index. +*/ +void QGeoPolygon::insertCoordinate(int index, const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPolygon); + d->insertCoordinate(index, coordinate); +} + +/*! + Replaces the path element at the specified \a index with \a coordinate. +*/ +void QGeoPolygon::replaceCoordinate(int index, const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPolygon); + d->replaceCoordinate(index, coordinate); +} + +/*! + Returns the coordinate at \a index . +*/ +QGeoCoordinate QGeoPolygon::coordinateAt(int index) const +{ + Q_D(const QGeoPolygon); + return d->coordinateAt(index); +} + +/*! + Returns true if the polygon's perimeter contains \a coordinate as one of the elements. +*/ +bool QGeoPolygon::containsCoordinate(const QGeoCoordinate &coordinate) const +{ + Q_D(const QGeoPolygon); + return d->containsCoordinate(coordinate); +} + +/*! + Removes the last occurrence of \a coordinate from the polygon. +*/ +void QGeoPolygon::removeCoordinate(const QGeoCoordinate &coordinate) +{ + Q_D(QGeoPolygon); + d->removeCoordinate(coordinate); +} + +/*! + Removes element at position \a index from the polygon. +*/ +void QGeoPolygon::removeCoordinate(int index) +{ + Q_D(QGeoPolygon); + d->removeCoordinate(index); +} + +/*! + Returns the geo polygon properties as a string. +*/ +QString QGeoPolygon::toString() const +{ + if (type() != QGeoShape::PolygonType) { + qWarning("Not a polygon"); + return QStringLiteral("QGeoPolygon(not a polygon)"); + } + + QString pathString; + for (const auto &p : path()) + pathString += p.toString() + QLatin1Char(','); + + return QStringLiteral("QGeoPolygon([ %1 ])").arg(pathString); +} + +QT_END_NAMESPACE diff --git a/src/positioning/qgeopolygon.h b/src/positioning/qgeopolygon.h new file mode 100644 index 0000000..90886da --- /dev/null +++ b/src/positioning/qgeopolygon.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOLYGON_H +#define QGEOPOLYGON_H + +#include + +QT_BEGIN_NAMESPACE + +class QGeoCoordinate; +class QGeoPathPrivate; +typedef QGeoPathPrivate QGeoPolygonPrivate; + +class Q_POSITIONING_EXPORT QGeoPolygon : public QGeoShape +{ + Q_GADGET + +public: + QGeoPolygon(); + QGeoPolygon(const QList &path); + QGeoPolygon(const QGeoPolygon &other); + QGeoPolygon(const QGeoShape &other); + + ~QGeoPolygon(); + + QGeoPolygon &operator=(const QGeoPolygon &other); + + using QGeoShape::operator==; + bool operator==(const QGeoPolygon &other) const; + + using QGeoShape::operator!=; + bool operator!=(const QGeoPolygon &other) const; + + void setPath(const QList &path); + const QList &path() const; + + Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude); + Q_INVOKABLE QGeoPolygon translated(double degreesLatitude, double degreesLongitude) const; + Q_INVOKABLE double length(int indexFrom = 0, int indexTo = -1) const; + Q_INVOKABLE int size() const; + Q_INVOKABLE void addCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void insertCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE void replaceCoordinate(int index, const QGeoCoordinate &coordinate); + Q_INVOKABLE QGeoCoordinate coordinateAt(int index) const; + Q_INVOKABLE bool containsCoordinate(const QGeoCoordinate &coordinate) const; + Q_INVOKABLE void removeCoordinate(const QGeoCoordinate &coordinate); + Q_INVOKABLE void removeCoordinate(int index); + + Q_INVOKABLE QString toString() const; + +private: + inline QGeoPolygonPrivate *d_func(); + inline const QGeoPolygonPrivate *d_func() const; +}; + +Q_DECLARE_TYPEINFO(QGeoPolygon, Q_MOVABLE_TYPE); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoPolygon) + +#endif // QGEOPOLYGON_H diff --git a/src/positioning/qgeopositioninfo.cpp b/src/positioning/qgeopositioninfo.cpp new file mode 100644 index 0000000..08162ef --- /dev/null +++ b/src/positioning/qgeopositioninfo.cpp @@ -0,0 +1,378 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeopositioninfo.h" +#include "qgeopositioninfo_p.h" +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoPositionInfo + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoPositionInfo class contains information gathered on a global position, direction and velocity at a particular point in time. + + A QGeoPositionInfo contains, at a minimum, a geographical coordinate and + a timestamp. It may also have heading and speed measurements as well as + estimates of the accuracy of the provided data. + + \sa QGeoPositionInfoSource +*/ + +/*! + \enum QGeoPositionInfo::Attribute + Defines the attributes for positional information. + + \value Direction The bearing measured in degrees clockwise from true north to the direction of travel. + \value GroundSpeed The ground speed, in meters/sec. + \value VerticalSpeed The vertical speed, in meters/sec. + \value MagneticVariation The angle between the horizontal component of the magnetic field and true north, in degrees. Also known as magnetic declination. A positive value indicates a clockwise direction from true north and a negative value indicates a counter-clockwise direction. + \value HorizontalAccuracy The accuracy of the provided latitude-longitude value, in meters. + \value VerticalAccuracy The accuracy of the provided altitude value, in meters. +*/ + +/*! + Creates an invalid QGeoPositionInfo object. + + \sa isValid() +*/ +QGeoPositionInfo::QGeoPositionInfo() + : d(new QGeoPositionInfoPrivate) +{ +} + +/*! + Creates a QGeoPositionInfo for the given \a coordinate and \a timestamp. +*/ +QGeoPositionInfo::QGeoPositionInfo(const QGeoCoordinate &coordinate, const QDateTime ×tamp) + : d(new QGeoPositionInfoPrivate) +{ + d->timestamp = timestamp; + d->coord = coordinate; +} + +/*! + Creates a QGeoPositionInfo with the values of \a other. +*/ +QGeoPositionInfo::QGeoPositionInfo(const QGeoPositionInfo &other) + : d(other.d->clone()) +{ +} + +QGeoPositionInfo::QGeoPositionInfo(QGeoPositionInfoPrivate &dd) : d(&dd) +{ +} + +/*! + Destroys a QGeoPositionInfo object. +*/ +QGeoPositionInfo::~QGeoPositionInfo() +{ + delete d; +} + +/*! + Assigns the values from \a other to this QGeoPositionInfo. +*/ +QGeoPositionInfo &QGeoPositionInfo::operator=(const QGeoPositionInfo & other) +{ + if (this == &other) + return *this; + + delete d; + d = other.d->clone(); + +// d->timestamp = other.d->timestamp; +// d->coord = other.d->coord; +// d->doubleAttribs = other.d->doubleAttribs; + + return *this; +} + +/*! + Returns true if all of this object's values are the same as those of + \a other. +*/ +bool QGeoPositionInfo::operator==(const QGeoPositionInfo &other) const +{ + return *d == *other.d; +} + +/*! + \fn bool QGeoPositionInfo::operator!=(const QGeoPositionInfo &other) const + + Returns true if any of this object's values are not the same as those of + \a other. +*/ + +/*! + Returns true if the timestamp() and coordinate() values are both valid. + + \sa QGeoCoordinate::isValid(), QDateTime::isValid() +*/ +bool QGeoPositionInfo::isValid() const +{ + return d->timestamp.isValid() && d->coord.isValid(); +} + +/*! + Sets the date and time at which this position was reported to \a timestamp. + + The \a timestamp must be in UTC time. + + \sa timestamp() +*/ +void QGeoPositionInfo::setTimestamp(const QDateTime ×tamp) +{ + d->timestamp = timestamp; +} + +/*! + Returns the date and time at which this position was reported, in UTC time. + + Returns an invalid QDateTime if no date/time value has been set. + + \sa setTimestamp() +*/ +QDateTime QGeoPositionInfo::timestamp() const +{ + return d->timestamp; +} + +/*! + Sets the coordinate for this position to \a coordinate. + + \sa coordinate() +*/ +void QGeoPositionInfo::setCoordinate(const QGeoCoordinate &coordinate) +{ + d->coord = coordinate; +} + +/*! + Returns the coordinate for this position. + + Returns an invalid coordinate if no coordinate has been set. + + \sa setCoordinate() +*/ +QGeoCoordinate QGeoPositionInfo::coordinate() const +{ + return d->coord; +} + +/*! + Sets the value for \a attribute to \a value. + + \sa attribute() +*/ +void QGeoPositionInfo::setAttribute(Attribute attribute, qreal value) +{ + d->doubleAttribs[attribute] = value; +} + +/*! + Returns the value of the specified \a attribute as a qreal value. + + Returns NaN if the value has not been set. + + The function hasAttribute() should be used to determine whether or + not a value has been set for an attribute. + + \sa hasAttribute(), setAttribute() +*/ +qreal QGeoPositionInfo::attribute(Attribute attribute) const +{ + if (d->doubleAttribs.contains(attribute)) + return d->doubleAttribs[attribute]; + return qQNaN(); +} + +/*! + Removes the specified \a attribute and its value. +*/ +void QGeoPositionInfo::removeAttribute(Attribute attribute) +{ + d->doubleAttribs.remove(attribute); +} + +/*! + Returns true if the specified \a attribute is present for this + QGeoPositionInfo object. +*/ +bool QGeoPositionInfo::hasAttribute(Attribute attribute) const +{ + return d->doubleAttribs.contains(attribute); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGeoPositionInfo &info) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QGeoPositionInfo(" << info.d->timestamp; + dbg.nospace() << ", "; // timestamp force dbg.space() -> reverting here + dbg << info.d->coord; + + QList attribs = info.d->doubleAttribs.keys(); + std::stable_sort(attribs.begin(), attribs.end()); // Output a sorted list from an unsorted hash. + for (int i = 0; i < attribs.count(); ++i) { + dbg << ", "; + switch (attribs[i]) { + case QGeoPositionInfo::Direction: + dbg << "Direction="; + break; + case QGeoPositionInfo::GroundSpeed: + dbg << "GroundSpeed="; + break; + case QGeoPositionInfo::VerticalSpeed: + dbg << "VerticalSpeed="; + break; + case QGeoPositionInfo::MagneticVariation: + dbg << "MagneticVariation="; + break; + case QGeoPositionInfo::HorizontalAccuracy: + dbg << "HorizontalAccuracy="; + break; + case QGeoPositionInfo::VerticalAccuracy: + dbg << "VerticalAccuracy="; + break; + } + dbg << info.d->doubleAttribs[attribs[i]]; + } + dbg << ')'; + return dbg; +} +#endif + + +#ifndef QT_NO_DATASTREAM +/*! + \relates QGeoPositionInfo + + Writes the given \a attr enumeration to the specified \a stream. + + \sa {Serializing Qt Data Types} +*/ +QDataStream &operator<<(QDataStream &stream, QGeoPositionInfo::Attribute attr) +{ + return stream << int(attr); +} + +/*! + \relates QGeoPositionInfo + + Reads an attribute enumeration from the specified \a stream info the given \a attr. + + \sa {Serializing Qt Data Types} +*/ +QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo::Attribute &attr) +{ + int a; + stream >> a; + attr = static_cast(a); + return stream; +} + +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QGeoPositionInfo &info) + \relates QGeoPositionInfo + + Writes the given \a info to the specified \a stream. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator<<(QDataStream &stream, const QGeoPositionInfo &info) +{ + stream << info.d->timestamp; + stream << info.d->coord; + stream << info.d->doubleAttribs; + return stream; +} + +/*! + \fn QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo &info) + \relates QGeoPositionInfo + + Reads a coordinate from the specified \a stream into the given + \a info. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo &info) +{ + stream >> info.d->timestamp; + stream >> info.d->coord; + stream >> info.d->doubleAttribs; + return stream; +} +#endif + +QGeoPositionInfoPrivate::~QGeoPositionInfoPrivate() +{ + +} + +QGeoPositionInfoPrivate *QGeoPositionInfoPrivate::clone() const +{ + return new QGeoPositionInfoPrivate(*this); +} + +bool QGeoPositionInfoPrivate::operator==(const QGeoPositionInfoPrivate &other) const +{ + return timestamp == other.timestamp + && coord == other.coord + && doubleAttribs == other.doubleAttribs; +} + +QGeoPositionInfoPrivate *QGeoPositionInfoPrivate::get(const QGeoPositionInfo &info) +{ + return info.d; +} + +QT_END_NAMESPACE + diff --git a/src/positioning/qgeopositioninfo.h b/src/positioning/qgeopositioninfo.h new file mode 100644 index 0000000..78203b6 --- /dev/null +++ b/src/positioning/qgeopositioninfo.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOPOSITIONINFO_H +#define QGEOPOSITIONINFO_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QDebug; +class QDataStream; + +class QGeoPositionInfoPrivate; +class Q_POSITIONING_EXPORT QGeoPositionInfo +{ +public: + enum Attribute { + Direction, + GroundSpeed, + VerticalSpeed, + MagneticVariation, + HorizontalAccuracy, + VerticalAccuracy + }; + + QGeoPositionInfo(); + QGeoPositionInfo(const QGeoCoordinate &coordinate, const QDateTime &updateTime); + QGeoPositionInfo(const QGeoPositionInfo &other); + QGeoPositionInfo(QGeoPositionInfoPrivate &dd); + ~QGeoPositionInfo(); + + QGeoPositionInfo &operator=(const QGeoPositionInfo &other); + + bool operator==(const QGeoPositionInfo &other) const; + inline bool operator!=(const QGeoPositionInfo &other) const { + return !operator==(other); + } + + bool isValid() const; + + void setTimestamp(const QDateTime ×tamp); + QDateTime timestamp() const; + + void setCoordinate(const QGeoCoordinate &coordinate); + QGeoCoordinate coordinate() const; + + void setAttribute(Attribute attribute, qreal value); + qreal attribute(Attribute attribute) const; + void removeAttribute(Attribute attribute); + bool hasAttribute(Attribute attribute) const; + +private: +#ifndef QT_NO_DEBUG_STREAM + friend Q_POSITIONING_EXPORT QDebug operator<<(QDebug dbg, const QGeoPositionInfo &info); +#endif +#ifndef QT_NO_DATASTREAM + friend Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, const QGeoPositionInfo &info); + friend Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo &info); +#endif + QGeoPositionInfoPrivate *d; + friend class QGeoPositionInfoPrivate; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_EXPORT QDebug operator<<(QDebug dbg, const QGeoPositionInfo &info); +#endif + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, QGeoPositionInfo::Attribute attr); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo::Attribute &attr); +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, const QGeoPositionInfo &info); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoPositionInfo &info); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoPositionInfo) + +#endif diff --git a/src/positioning/qgeopositioninfo_p.h b/src/positioning/qgeopositioninfo_p.h new file mode 100644 index 0000000..b452264 --- /dev/null +++ b/src/positioning/qgeopositioninfo_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOPOSITIONINFO_P_H +#define QGEOPOSITIONINFO_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include "qgeopositioninfo.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_PRIVATE_EXPORT QGeoPositionInfoPrivate +{ +public: + virtual ~QGeoPositionInfoPrivate(); + virtual QGeoPositionInfoPrivate *clone() const; + + virtual bool operator==(const QGeoPositionInfoPrivate &other) const; + + QDateTime timestamp; + QGeoCoordinate coord; + QHash doubleAttribs; + + static QGeoPositionInfoPrivate *get(const QGeoPositionInfo &info); +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFO_P_H diff --git a/src/positioning/qgeopositioninfosource.cpp b/src/positioning/qgeopositioninfosource.cpp new file mode 100644 index 0000000..02aa1aa --- /dev/null +++ b/src/positioning/qgeopositioninfosource.cpp @@ -0,0 +1,479 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include "qgeopositioninfosource_p.h" +#include "qgeopositioninfosourcefactory.h" + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + ("org.qt-project.qt.position.sourcefactory/5.0", + QLatin1String("/position"))) + +/*! + \class QGeoPositionInfoSource + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoPositionInfoSource class is an abstract base class for the distribution of positional updates. + + The static function QGeoPositionInfoSource::createDefaultSource() creates a default + position source that is appropriate for the platform, if one is available. + Otherwise, QGeoPositionInfoSource will check for available plugins that + implement the QGeoPositionInfoSourceFactory interface. + + Users of a QGeoPositionInfoSource subclass can request the current position using + requestUpdate(), or start and stop regular position updates using + startUpdates() and stopUpdates(). When an update is available, + positionUpdated() is emitted. The last known position can be accessed with + lastKnownPosition(). + + If regular position updates are required, setUpdateInterval() can be used + to specify how often these updates should be emitted. If no interval is + specified, updates are simply provided whenever they are available. + For example: + + \code + // Emit updates every 10 seconds if available + QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource(0); + if (source) + source->setUpdateInterval(10000); + \endcode + + To remove an update interval that was previously set, call + setUpdateInterval() with a value of 0. + + Note that the position source may have a minimum value requirement for + update intervals, as returned by minimumUpdateInterval(). +*/ + +/*! + \enum QGeoPositionInfoSource::PositioningMethod + Defines the types of positioning methods. + + \value NoPositioningMethods None of the positioning methods. + \value SatellitePositioningMethods Satellite-based positioning methods such as GPS or GLONASS. + \value NonSatellitePositioningMethods Other positioning methods such as 3GPP cell identifier or WiFi based positioning. + \value AllPositioningMethods Satellite-based positioning methods as soon as available. Otherwise non-satellite based methods. +*/ + +void QGeoPositionInfoSourcePrivate::loadMeta() +{ + metaData = plugins().value(providerName); +} + +void QGeoPositionInfoSourcePrivate::loadPlugin() +{ + int idx = int(metaData.value(QStringLiteral("index")).toDouble()); + if (idx < 0) + return; + factory = qobject_cast(loader()->instance(idx)); +} + +QHash QGeoPositionInfoSourcePrivate::plugins(bool reload) +{ + static QHash plugins; + static bool alreadyDiscovered = false; + + if (reload == true) + alreadyDiscovered = false; + + if (!alreadyDiscovered) { + loadPluginMetadata(plugins); + alreadyDiscovered = true; + } + return plugins; +} + +static bool pluginComparator(const QJsonObject &p1, const QJsonObject &p2) +{ + const QString prio = QStringLiteral("Priority"); + if (p1.contains(prio) && !p2.contains(prio)) + return true; + if (!p1.contains(prio) && p2.contains(prio)) + return false; + if (p1.value(prio).isDouble() && !p2.value(prio).isDouble()) + return true; + if (!p1.value(prio).isDouble() && p2.value(prio).isDouble()) + return false; + return (p1.value(prio).toDouble() > p2.value(prio).toDouble()); +} + +QList QGeoPositionInfoSourcePrivate::pluginsSorted() +{ + QList list = plugins().values(); + std::stable_sort(list.begin(), list.end(), pluginComparator); + return list; +} + +void QGeoPositionInfoSourcePrivate::loadPluginMetadata(QHash &plugins) +{ + QFactoryLoader *l = loader(); + QList meta = l->metaData(); + for (int i = 0; i < meta.size(); ++i) { + QJsonObject obj = meta.at(i).value(QStringLiteral("MetaData")).toObject(); + const QString testableKey = QStringLiteral("Testable"); + if (obj.contains(testableKey) && !obj.value(testableKey).toBool()) { + static bool inTest = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING"); + if (inTest) + continue; + } + obj.insert(QStringLiteral("index"), i); + plugins.insertMulti(obj.value(QStringLiteral("Provider")).toString(), obj); + } +} + +/*! + Creates a position source with the specified \a parent. +*/ + +QGeoPositionInfoSource::QGeoPositionInfoSource(QObject *parent) + : QObject(parent), + d(new QGeoPositionInfoSourcePrivate) +{ + qRegisterMetaType(); + d->interval = 0; + d->methods = 0; +} + +/*! + Destroys the position source. +*/ +QGeoPositionInfoSource::~QGeoPositionInfoSource() +{ + delete d; +} + +/*! + \property QGeoPositionInfoSource::sourceName + \brief This property holds the unique name of the position source + implementation in use. + + This is the same name that can be passed to createSource() in order to + create a new instance of a particular position source implementation. +*/ +QString QGeoPositionInfoSource::sourceName() const +{ + return d->metaData.value(QStringLiteral("Provider")).toString(); +} + +/*! + \property QGeoPositionInfoSource::updateInterval + \brief This property holds the requested interval in milliseconds between each update. + + If the update interval is not set (or is set to 0) the + source will provide updates as often as necessary. + + If the update interval is set, the source will provide updates at an + interval as close to the requested interval as possible. If the requested + interval is less than the minimumUpdateInterval(), + the minimum interval is used instead. + + Changes to the update interval will happen as soon as is practical, however the + time the change takes may vary between implementations. Whether or not the elapsed + time from the previous interval is counted as part of the new interval is also + implementation dependent. + + The default value for this property is 0. + + Note: Subclass implementations must call the base implementation of + setUpdateInterval() so that updateInterval() returns the correct value. +*/ +void QGeoPositionInfoSource::setUpdateInterval(int msec) +{ + d->interval = msec; +} + +int QGeoPositionInfoSource::updateInterval() const +{ + return d->interval; +} + +/*! + Sets the preferred positioning methods for this source to \a methods. + + If \a methods includes a method that is not supported by the source, the + unsupported method will be ignored. + + If \a methods does not include any methods supported by the source, the + preferred methods will be set to the set of methods which the source supports. + + \b {Note:} When reimplementing this method, subclasses must call the + base method implementation to ensure preferredPositioningMethods() returns the correct value. + + \sa supportedPositioningMethods() +*/ +void QGeoPositionInfoSource::setPreferredPositioningMethods(PositioningMethods methods) +{ + d->methods = methods & supportedPositioningMethods(); + if (d->methods == 0) { + d->methods = supportedPositioningMethods(); + } +} + +/*! + Returns the positioning methods set by setPreferredPositioningMethods(). +*/ +QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSource::preferredPositioningMethods() const +{ + return d->methods; +} + +/*! + Creates and returns a position source with the given \a parent that + reads from the system's default sources of location data, or the plugin + with the highest available priority. + + Returns 0 if the system has no default position source, no valid plugins + could be found or the user does not have the permission to access the current position. +*/ +QGeoPositionInfoSource *QGeoPositionInfoSource::createDefaultSource(QObject *parent) +{ + QList plugins = QGeoPositionInfoSourcePrivate::pluginsSorted(); + foreach (const QJsonObject &obj, plugins) { + if (obj.value(QStringLiteral("Position")).isBool() + && obj.value(QStringLiteral("Position")).toBool()) + { + QGeoPositionInfoSourcePrivate d; + d.metaData = obj; + d.loadPlugin(); + QGeoPositionInfoSource *s = 0; + if (d.factory) + s = d.factory->positionInfoSource(parent); + if (s) { + s->d->metaData = d.metaData; + return s; + } + } + } + return 0; +} + + +/*! + Creates and returns a position source with the given \a parent, + by loading the plugin named \a sourceName. + + Returns 0 if the plugin cannot be found. +*/ +QGeoPositionInfoSource *QGeoPositionInfoSource::createSource(const QString &sourceName, QObject *parent) +{ + QHash plugins = QGeoPositionInfoSourcePrivate::plugins(); + if (plugins.contains(sourceName)) + { + QGeoPositionInfoSourcePrivate d; + d.metaData = plugins.value(sourceName); + d.loadPlugin(); + QGeoPositionInfoSource *src = 0; + if (d.factory) + src = d.factory->positionInfoSource(parent); + if (src) + { + src->d->metaData = d.metaData; + return src; + } + } + return 0; +} + + +/*! + Returns a list of available source plugins. This includes any default backend + plugin for the current platform. +*/ +QStringList QGeoPositionInfoSource::availableSources() +{ + QStringList plugins; + const QHash meta = QGeoPositionInfoSourcePrivate::plugins(); + for (auto it = meta.cbegin(), end = meta.cend(); it != end; ++it) { + if (it.value().value(QStringLiteral("Position")).isBool() + && it.value().value(QStringLiteral("Position")).toBool()) { + plugins << it.key(); + } + } + + return plugins; +} + +/*! + \fn QGeoPositionInfo QGeoPositionInfoSource::lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const = 0; + + Returns an update containing the last known position, or a null update + if none is available. + + If \a fromSatellitePositioningMethodsOnly is true, this returns the last + known position received from a satellite positioning method; if none + is available, a null update is returned. +*/ + +/*! + \fn virtual PositioningMethods QGeoPositionInfoSource::supportedPositioningMethods() const = 0; + + Returns the positioning methods available to this source. + + \sa setPreferredPositioningMethods() +*/ + + +/*! + \property QGeoPositionInfoSource::minimumUpdateInterval + \brief This property holds the minimum time (in milliseconds) required to retrieve a position update. + + This is the minimum value accepted by setUpdateInterval() and + requestUpdate(). +*/ + + +/*! + \fn virtual void QGeoPositionInfoSource::startUpdates() = 0; + + Starts emitting updates at regular intervals as specified by setUpdateInterval(). + + If setUpdateInterval() has not been called, the source will emit updates + as soon as they become available. + + An updateTimeout() signal will be emitted if this QGeoPositionInfoSource subclass determines + that it will not be able to provide regular updates. This could happen if a satellite fix is + lost or if a hardware error is detected. Position updates will recommence if the data becomes + available later on. The updateTimeout() signal will not be emitted again until after the + periodic updates resume. + + On iOS, starting from version 8, Core Location framework requires additional + entries in the application's Info.plist with keys NSLocationAlwaysUsageDescription or + NSLocationWhenInUseUsageDescription and a string to be displayed in the authorization prompt. + The key NSLocationWhenInUseUsageDescription is used when requesting permission + to use location services while the app is in the foreground. + The key NSLocationAlwaysUsageDescription is used when requesting permission + to use location services whenever the app is running (both the foreground and the background). + If both entries are defined, NSLocationWhenInUseUsageDescription has a priority in the + foreground mode. +*/ + +/*! + \fn virtual void QGeoPositionInfoSource::stopUpdates() = 0; + + Stops emitting updates at regular intervals. +*/ + +/*! + \fn virtual void QGeoPositionInfoSource::requestUpdate(int timeout = 0); + + Attempts to get the current position and emit positionUpdated() with + this information. If the current position cannot be found within the given \a timeout + (in milliseconds) or if \a timeout is less than the value returned by + minimumUpdateInterval(), updateTimeout() is emitted. + + If the timeout is zero, the timeout defaults to a reasonable timeout + period as appropriate for the source. + + This does nothing if another update request is in progress. However + it can be called even if startUpdates() has already been called and + regular updates are in progress. + + If the source uses multiple positioning methods, it tries to get the + current position from the most accurate positioning method within the + given timeout. +*/ + +/*! + \fn virtual QGeoPositionInfoSource::Error QGeoPositionInfoSource::error() const; + + Returns the type of error that last occurred. + +*/ + +/*! + \fn void QGeoPositionInfoSource::positionUpdated(const QGeoPositionInfo &update); + + If startUpdates() or requestUpdate() is called, this signal is emitted + when an update becomes available. + + The \a update value holds the value of the new update. +*/ + +/*! + \fn void QGeoPositionInfoSource::updateTimeout(); + + If requestUpdate() was called, this signal will be emitted if the current position could not + be retrieved within the specified timeout. + + If startUpdates() has been called, this signal will be emitted if this QGeoPositionInfoSource + subclass determines that it will not be able to provide further regular updates. This signal + will not be emitted again until after the regular updates resume. + + While the triggering of this signal may be considered an error condition, it does not + imply the emission of the \c error() signal. Only the emission of \c updateTimeout() is required + to indicate a timeout. +*/ + +/*! + \fn void QGeoPositionInfoSource::error(QGeoPositionInfoSource::Error positioningError) + + This signal is emitted after an error occurred. The \a positioningError + parameter describes the type of error that occurred. + + This signal is not emitted when an updateTimeout() has occurred. + +*/ + +/*! + \enum QGeoPositionInfoSource::Error + + The Error enumeration represents the errors which can occur. + + \value AccessError The connection setup to the remote positioning backend failed because the + application lacked the required privileges. + \value ClosedError The remote positioning backend closed the connection, which happens for example in case + the user is switching location services to off. As soon as the location service is re-enabled + regular updates will resume. + \value NoError No error has occurred. + \value UnknownSourceError An unidentified error occurred. + */ + +QT_END_NAMESPACE diff --git a/src/positioning/qgeopositioninfosource.h b/src/positioning/qgeopositioninfosource.h new file mode 100644 index 0000000..2478446 --- /dev/null +++ b/src/positioning/qgeopositioninfosource.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOPOSITIONINFOSOURCE_H +#define QGEOPOSITIONINFOSOURCE_H + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QGeoPositionInfoSourcePrivate; +class Q_POSITIONING_EXPORT QGeoPositionInfoSource : public QObject +{ + Q_OBJECT + Q_PROPERTY(int updateInterval READ updateInterval WRITE setUpdateInterval) + Q_PROPERTY(int minimumUpdateInterval READ minimumUpdateInterval) + Q_PROPERTY(QString sourceName READ sourceName) + +public: + enum Error { + AccessError = 0, + ClosedError = 1, + UnknownSourceError = 2, + NoError = 3 + }; + Q_ENUMS(Error) + + enum PositioningMethod { + NoPositioningMethods = 0x00000000, + SatellitePositioningMethods = 0x000000ff, + NonSatellitePositioningMethods = 0xffffff00, + AllPositioningMethods = 0xffffffff + }; + Q_DECLARE_FLAGS(PositioningMethods, PositioningMethod) + + explicit QGeoPositionInfoSource(QObject *parent); + virtual ~QGeoPositionInfoSource(); + + virtual void setUpdateInterval(int msec); + int updateInterval() const; + + virtual void setPreferredPositioningMethods(PositioningMethods methods); + PositioningMethods preferredPositioningMethods() const; + + virtual QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const = 0; + + virtual PositioningMethods supportedPositioningMethods() const = 0; + virtual int minimumUpdateInterval() const = 0; + + QString sourceName() const; + + static QGeoPositionInfoSource *createDefaultSource(QObject *parent); + static QGeoPositionInfoSource *createSource(const QString &sourceName, QObject *parent); + static QStringList availableSources(); + virtual Error error() const = 0; + +public Q_SLOTS: + virtual void startUpdates() = 0; + virtual void stopUpdates() = 0; + + virtual void requestUpdate(int timeout = 0) = 0; + +Q_SIGNALS: + void positionUpdated(const QGeoPositionInfo &update); + void updateTimeout(); + void error(QGeoPositionInfoSource::Error); + +private: + Q_DISABLE_COPY(QGeoPositionInfoSource) + QGeoPositionInfoSourcePrivate *d; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGeoPositionInfoSource::PositioningMethods) + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeopositioninfosource_p.h b/src/positioning/qgeopositioninfosource_p.h new file mode 100644 index 0000000..32fd23e --- /dev/null +++ b/src/positioning/qgeopositioninfosource_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCE_P_H +#define QGEOPOSITIONINFOSOURCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeopositioninfosource.h" +#include "qgeopositioninfosourcefactory.h" +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoPositionInfoSourcePrivate +{ +public: + int interval; + QGeoPositionInfoSource::PositioningMethods methods; + QJsonObject metaData; + QGeoPositionInfoSourceFactory *factory; + QString providerName; + + void loadMeta(); + void loadPlugin(); + + static QHash plugins(bool reload = false); + static void loadPluginMetadata(QHash &list); + static QList pluginsSorted(); +}; + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCE_P_H diff --git a/src/positioning/qgeopositioninfosourcefactory.cpp b/src/positioning/qgeopositioninfosourcefactory.cpp new file mode 100644 index 0000000..6c6e9c7 --- /dev/null +++ b/src/positioning/qgeopositioninfosourcefactory.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeopositioninfosourcefactory.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoPositionInfoSourceFactory + \inmodule QtPositioning + \since 5.2 + + \brief The QGeoPositionInfoSourceFactory class is a factory class used + as the plugin interface for external providers of positioning data. + + The other functions must be overridden by all plugins, other than + sourcePriority() which defaults to returning 0. Higher values of + priority will be preferred to lower ones. +*/ + +/*! + \fn QGeoPositionInfoSource *QGeoPositionInfoSourceFactory::positionInfoSource(QObject *parent) + + Returns a new QGeoPositionInfoSource associated with this plugin + with parent \a parent. Can also return 0, in which case the plugin + loader will use the factory with the next highest priority. + */ + +/*! + \fn QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactory::satelliteInfoSource(QObject *parent) + + Returns a new QGeoSatelliteInfoSource associated with this plugin + with parent \a parent. Can also return 0, in which case the plugin + loader will use the factory with the next highest priority. + */ + +/*! + \fn QGeoAreaMonitorSource *QGeoPositionInfoSourceFactory::areaMonitor(QObject *parent); + + Returns a new QGeoAreaMonitorSource associated with this plugin with parent \a parent. + Can also return 0, in which case the plugin loader will use the factory with the + next highest priority. + */ + +/*! + Destroys the position info source factory. +*/ +QGeoPositionInfoSourceFactory::~QGeoPositionInfoSourceFactory() +{} + +QT_END_NAMESPACE diff --git a/src/positioning/qgeopositioninfosourcefactory.h b/src/positioning/qgeopositioninfosourcefactory.h new file mode 100644 index 0000000..b30aaf7 --- /dev/null +++ b/src/positioning/qgeopositioninfosourcefactory.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFOSOURCEFACTORY_H +#define QGEOPOSITIONINFOSOURCEFACTORY_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONING_EXPORT QGeoPositionInfoSourceFactory +{ +public: + virtual ~QGeoPositionInfoSourceFactory(); + + virtual QGeoPositionInfoSource *positionInfoSource(QObject *parent) = 0; + virtual QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent) = 0; + virtual QGeoAreaMonitorSource *areaMonitor(QObject *parent) = 0; +}; + +#define QT_POSITION_SOURCE_INTERFACE +Q_DECLARE_INTERFACE(QGeoPositionInfoSourceFactory, + "org.qt-project.qt.position.sourcefactory/5.0") + +QT_END_NAMESPACE + +#endif // QGEOPOSITIONINFOSOURCEFACTORY_H diff --git a/src/positioning/qgeorectangle.cpp b/src/positioning/qgeorectangle.cpp new file mode 100644 index 0000000..337b4c7 --- /dev/null +++ b/src/positioning/qgeorectangle.cpp @@ -0,0 +1,1040 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeorectangle.h" +#include "qgeorectangle_p.h" + +#include "qwebmercator_p.h" +#include "qdoublevector2d_p.h" +#include "qgeocoordinate.h" +#include "qnumeric.h" +#include "qlocationutils_p.h" +#include +QT_BEGIN_NAMESPACE + +/*! + \class QGeoRectangle + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoRectangle class defines a rectangular geographic area. + + The rectangle is defined in terms of a QGeoCoordinate which specifies the + top left coordinate of the rectangle and a QGeoCoordinate which specifies + the bottom right coordinate of the rectangle. + + A geo rectangle is considered invalid if the top left or bottom right + coordinates are invalid or if the top left coordinate is south of the + bottom right coordinate. + + Geo rectangles can never cross the poles. + + Several methods behave as though the geo rectangle is defined in terms of a + center coordinate, the width of the geo rectangle in degrees and the height + of the geo rectangle in degrees. + + If the height or center of a geo rectangle is adjusted such that it would + cross one of the poles the height is modified such that the geo rectangle + touches but does not cross the pole and that the center coordinate is still + in the center of the geo rectangle. + + This class is a \l Q_GADGET since Qt 5.5. It can be + \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. +*/ + +/*! + \property QGeoRectangle::bottomLeft + \brief This property holds the bottom left coorindate of this geo rectangle. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoRectangle::bottomRight + \brief This property holds the bottom right coordinate of this geo rectangle. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoRectangle::topLeft + \brief This property holds the top left coordinate of this geo rectangle. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoRectangle::topRight + \brief This property holds the top right coordinate of this geo rectangle. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoRectangle::center + \brief This property holds the center of this geo rectangle. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \sa QGeoShape::center + + \since 5.5 +*/ + +/*! + \property QGeoRectangle::width + \brief This property holds the width of this geo rectangle in degrees. + + The property value is undefined if this geo rectangle is invalid. + + If the new width is less than 0.0 or if this geo rectangle is invalid, this + function does nothing. To set up the values of an invalid + geo rectangle based on the center, width, and height, you should use + \l setCenter() first to make the geo rectangle valid. + + 360.0 is the width used only if the new width is equal or greater than 360. + In such cases the leftmost longitude of the geo rectangle is set to -180.0 + degrees and the rightmost longitude of the geo rectangle is set to 180.0 + degrees. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoRectangle::height + \brief This property holds the height of this geo rectangle in degrees. + + The property value is undefined if this geo rectangle is invalid. + + If the new height is less than 0.0 or if this geo rectangle is invalid, + the property is not changed. To set up the values of an invalid + geo rectangle based on the center, width, and height, you should use + \l setCenter() first to make the geo rectangle valid. + + If the change in height would cause the geo rectangle to cross a pole, + the height is adjusted such that the geo rectangle only touches the pole. + + This change is done such that the center coordinate is still at the + center of the geo rectangle, which may result in a geo rectangle with + a smaller height than expected. + + 180.0 is the height used only if the new height is greater or equal than 180. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +inline QGeoRectanglePrivate *QGeoRectangle::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QGeoRectanglePrivate *QGeoRectangle::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +struct RectangleVariantConversions +{ + RectangleVariantConversions() + { + QMetaType::registerConverter(); + QMetaType::registerConverter(); + } +}; + + +Q_GLOBAL_STATIC(RectangleVariantConversions, initRectangleConversions) + +/*! + Constructs a new, invalid geo rectangle. +*/ +QGeoRectangle::QGeoRectangle() +: QGeoShape(new QGeoRectanglePrivate) +{ + initRectangleConversions(); +} + +/*! + Constructs a new geo rectangle centered at \a center with a + width in degrees of \a degreesWidth and a height in degrees of \a degreesHeight. + + If \a degreesHeight would take the geo rectangle beyond one of the poles, + the height of the geo rectangle will be truncated such that the geo rectangle + only extends up to the pole. The center of the geo rectangle will be + unchanged, and the height will be adjusted such that the center point is at + the center of the truncated geo rectangle. +*/ +QGeoRectangle::QGeoRectangle(const QGeoCoordinate ¢er, double degreesWidth, double degreesHeight) +{ + initRectangleConversions(); + d_ptr = new QGeoRectanglePrivate(center, center); + setWidth(degreesWidth); + setHeight(degreesHeight); +} + +/*! + Constructs a new geo rectangle with a top left coordinate \a topLeft and a bottom right + coordinate \a bottomRight. +*/ +QGeoRectangle::QGeoRectangle(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight) +{ + initRectangleConversions(); + d_ptr = new QGeoRectanglePrivate(topLeft, bottomRight); +} + +/*! + Constructs a new geo rectangle, of minimum size, containing all of the \a coordinates. +*/ +QGeoRectangle::QGeoRectangle(const QList &coordinates) +{ + initRectangleConversions(); + if (coordinates.isEmpty()) { + d_ptr = new QGeoRectanglePrivate; + } else { + const QGeoCoordinate &startCoordinate = coordinates.first(); + d_ptr = new QGeoRectanglePrivate(startCoordinate, startCoordinate); + + foreach (const QGeoCoordinate &coordinate, coordinates) { + d_ptr->extendShape(coordinate); + } + } +} + +/*! + Constructs a geo rectangle from the contents of \a other. +*/ +QGeoRectangle::QGeoRectangle(const QGeoRectangle &other) +: QGeoShape(other) +{ + initRectangleConversions(); +} + +/*! + Constructs a geo rectangle from the contents of \a other. +*/ +QGeoRectangle::QGeoRectangle(const QGeoShape &other) +: QGeoShape(other) +{ + initRectangleConversions(); + if (type() != QGeoShape::RectangleType) + d_ptr = new QGeoRectanglePrivate; +} + +/*! + Destroys this geo rectangle. +*/ +QGeoRectangle::~QGeoRectangle() +{ +} + +/*! + Assigns \a other to this geo rectangle and returns a reference to this geo rectangle. +*/ +QGeoRectangle &QGeoRectangle::operator=(const QGeoRectangle &other) +{ + QGeoShape::operator=(other); + return *this; +} + +/*! + Returns whether this geo rectangle is equal to \a other. +*/ +bool QGeoRectangle::operator==(const QGeoRectangle &other) const +{ + Q_D(const QGeoRectangle); + + return *d == *other.d_func(); +} + +/*! + Returns whether this geo rectangle is not equal to \a other. +*/ +bool QGeoRectangle::operator!=(const QGeoRectangle &other) const +{ + Q_D(const QGeoRectangle); + + return !(*d == *other.d_func()); +} + +bool QGeoRectanglePrivate::isValid() const +{ + return topLeft.isValid() && bottomRight.isValid() && + topLeft.latitude() >= bottomRight.latitude(); +} + +bool QGeoRectanglePrivate::isEmpty() const +{ + if (!isValid()) + return true; + + return topLeft.latitude() == bottomRight.latitude() || + topLeft.longitude() == bottomRight.longitude(); +} + +/*! + Sets the top left coordinate of this geo rectangle to \a topLeft. +*/ +void QGeoRectangle::setTopLeft(const QGeoCoordinate &topLeft) +{ + Q_D(QGeoRectangle); + + d->topLeft = topLeft; +} + +/*! + Returns the top left coordinate of this geo rectangle. +*/ +QGeoCoordinate QGeoRectangle::topLeft() const +{ + Q_D(const QGeoRectangle); + + return d->topLeft; +} + +/*! + Sets the top right coordinate of this geo rectangle to \a topRight. +*/ +void QGeoRectangle::setTopRight(const QGeoCoordinate &topRight) +{ + Q_D(QGeoRectangle); + + d->topLeft.setLatitude(topRight.latitude()); + d->bottomRight.setLongitude(topRight.longitude()); +} + +/*! + Returns the top right coordinate of this geo rectangle. +*/ +QGeoCoordinate QGeoRectangle::topRight() const +{ + // TODO remove? + if (!isValid()) + return QGeoCoordinate(); + + Q_D(const QGeoRectangle); + + return QGeoCoordinate(d->topLeft.latitude(), d->bottomRight.longitude()); +} + +/*! + Sets the bottom left coordinate of this geo rectangle to \a bottomLeft. +*/ +void QGeoRectangle::setBottomLeft(const QGeoCoordinate &bottomLeft) +{ + Q_D(QGeoRectangle); + + d->bottomRight.setLatitude(bottomLeft.latitude()); + d->topLeft.setLongitude(bottomLeft.longitude()); +} + +/*! + Returns the bottom left coordinate of this geo rectangle. +*/ +QGeoCoordinate QGeoRectangle::bottomLeft() const +{ + // TODO remove? + if (!isValid()) + return QGeoCoordinate(); + + Q_D(const QGeoRectangle); + + return QGeoCoordinate(d->bottomRight.latitude(), d->topLeft.longitude()); +} + +/*! + Sets the bottom right coordinate of this geo rectangle to \a bottomRight. +*/ +void QGeoRectangle::setBottomRight(const QGeoCoordinate &bottomRight) +{ + Q_D(QGeoRectangle); + + d->bottomRight = bottomRight; +} + +/*! + Returns the bottom right coordinate of this geo rectangle. +*/ +QGeoCoordinate QGeoRectangle::bottomRight() const +{ + Q_D(const QGeoRectangle); + + return d->bottomRight; +} + +/*! + Sets the center of this geo rectangle to \a center. + + If this causes the geo rectangle to cross on of the poles the height of the + geo rectangle will be truncated such that the geo rectangle only extends up + to the pole. The center of the geo rectangle will be unchanged, and the + height will be adjusted such that the center point is at the center of the + truncated geo rectangle. + +*/ +void QGeoRectangle::setCenter(const QGeoCoordinate ¢er) +{ + Q_D(QGeoRectangle); + + if (!isValid()) { + d->topLeft = center; + d->bottomRight = center; + return; + } + double width = this->width(); + double height = this->height(); + + double tlLat = center.latitude() + height / 2.0; + double tlLon = center.longitude() - width / 2.0; + double brLat = center.latitude() - height / 2.0; + double brLon = center.longitude() + width / 2.0; + tlLon = QLocationUtils::wrapLong(tlLon); + brLon = QLocationUtils::wrapLong(brLon); + + if (tlLat > 90.0) { + brLat = 2 * center.latitude() - 90.0; + tlLat = 90.0; + } + + if (tlLat < -90.0) { + brLat = -90.0; + tlLat = -90.0; + } + + if (brLat > 90.0) { + tlLat = 90.0; + brLat = 90.0; + } + + if (brLat < -90.0) { + tlLat = 2 * center.latitude() + 90.0; + brLat = -90.0; + } + + if (width == 360.0) { + tlLon = -180.0; + brLon = 180.0; + } + + d->topLeft = QGeoCoordinate(tlLat, tlLon); + d->bottomRight = QGeoCoordinate(brLat, brLon); +} + +/*! + Returns the center of this geo rectangle. Equivalent to QGeoShape::center(). +*/ +QGeoCoordinate QGeoRectangle::center() const +{ + Q_D(const QGeoRectangle); + + return d->center(); +} + +/*! + Sets the width of this geo rectangle in degrees to \a degreesWidth. +*/ +void QGeoRectangle::setWidth(double degreesWidth) +{ + if (!isValid()) + return; + + if (degreesWidth < 0.0) + return; + + Q_D(QGeoRectangle); + + if (degreesWidth >= 360.0) { + d->topLeft.setLongitude(-180.0); + d->bottomRight.setLongitude(180.0); + return; + } + + double tlLat = d->topLeft.latitude(); + double brLat = d->bottomRight.latitude(); + + QGeoCoordinate c = center(); + + double tlLon = c.longitude() - degreesWidth / 2.0; + tlLon = QLocationUtils::wrapLong(tlLon); + + double brLon = c.longitude() + degreesWidth / 2.0; + brLon = QLocationUtils::wrapLong(brLon); + + d->topLeft = QGeoCoordinate(tlLat, tlLon); + d->bottomRight = QGeoCoordinate(brLat, brLon); +} + +/*! + Returns the width of this geo rectangle in degrees. + + The return value is undefined if this geo rectangle is invalid. +*/ +double QGeoRectangle::width() const +{ + if (!isValid()) + return qQNaN(); + + Q_D(const QGeoRectangle); + + double result = d->bottomRight.longitude() - d->topLeft.longitude(); + if (result < 0.0) + result += 360.0; + if (result > 360.0) + result -= 360.0; + + return result; +} + +/*! + Sets the height of this geo rectangle in degrees to \a degreesHeight. +*/ +void QGeoRectangle::setHeight(double degreesHeight) +{ + if (!isValid()) + return; + + if (degreesHeight < 0.0) + return; + + if (degreesHeight >= 180.0) { + degreesHeight = 180.0; + } + + Q_D(QGeoRectangle); + + double tlLon = d->topLeft.longitude(); + double brLon = d->bottomRight.longitude(); + + QGeoCoordinate c = center(); + + double tlLat = c.latitude() + degreesHeight / 2.0; + double brLat = c.latitude() - degreesHeight / 2.0; + + if (tlLat > 90.0) { + brLat = 2* c.latitude() - 90.0; + tlLat = 90.0; + } + + if (tlLat < -90.0) { + brLat = -90.0; + tlLat = -90.0; + } + + if (brLat > 90.0) { + tlLat = 90.0; + brLat = 90.0; + } + + if (brLat < -90.0) { + tlLat = 2 * c.latitude() + 90.0; + brLat = -90.0; + } + + d->topLeft = QGeoCoordinate(tlLat, tlLon); + d->bottomRight = QGeoCoordinate(brLat, brLon); +} + +/*! + Returns the height of this geo rectangle in degrees. + + The return value is undefined if this geo rectangle is invalid. +*/ +double QGeoRectangle::height() const +{ + if (!isValid()) + return qQNaN(); + + Q_D(const QGeoRectangle); + + return d->topLeft.latitude() - d->bottomRight.latitude(); +} + +bool QGeoRectanglePrivate::contains(const QGeoCoordinate &coordinate) const +{ + if (!isValid() || !coordinate.isValid()) + return false; + + double left = topLeft.longitude(); + double right = bottomRight.longitude(); + double top = topLeft.latitude(); + double bottom = bottomRight.latitude(); + + double lon = coordinate.longitude(); + double lat = coordinate.latitude(); + + if (lat > top) + return false; + if (lat < bottom) + return false; + + if ((lat == 90.0) && (top == 90.0)) + return true; + + if ((lat == -90.0) && (bottom == -90.0)) + return true; + + if (left <= right) { + if ((lon < left) || (lon > right)) + return false; + } else { + if ((lon < left) && (lon > right)) + return false; + } + + return true; +} + +QGeoCoordinate QGeoRectanglePrivate::center() const +{ + if (!isValid()) + return QGeoCoordinate(); + + double cLat = (topLeft.latitude() + bottomRight.latitude()) / 2.0; + double cLon = (bottomRight.longitude() + topLeft.longitude()) / 2.0; + + if (topLeft.longitude() > bottomRight.longitude()) + cLon = cLon - 180.0; + + cLon = QLocationUtils::wrapLong(cLon); + return QGeoCoordinate(cLat, cLon); +} + +QGeoRectangle QGeoRectanglePrivate::boundingGeoRectangle() const +{ + return QGeoRectangle(topLeft, bottomRight); +} + +/*! + Returns whether the geo rectangle \a rectangle is contained within this + geo rectangle. +*/ +bool QGeoRectangle::contains(const QGeoRectangle &rectangle) const +{ + Q_D(const QGeoRectangle); + + return (d->contains(rectangle.topLeft()) + && d->contains(rectangle.topRight()) + && d->contains(rectangle.bottomLeft()) + && d->contains(rectangle.bottomRight())); +} + +/*! + Returns whether the geo rectangle \a rectangle intersects this geo rectangle. + + If the top or bottom edges of both geo rectangles are at one of the poles + the geo rectangles are considered to be intersecting, since the longitude + is irrelevant when the edges are at the pole. +*/ +bool QGeoRectangle::intersects(const QGeoRectangle &rectangle) const +{ + Q_D(const QGeoRectangle); + + double left1 = d->topLeft.longitude(); + double right1 = d->bottomRight.longitude(); + double top1 = d->topLeft.latitude(); + double bottom1 = d->bottomRight.latitude(); + + double left2 = rectangle.d_func()->topLeft.longitude(); + double right2 = rectangle.d_func()->bottomRight.longitude(); + double top2 = rectangle.d_func()->topLeft.latitude(); + double bottom2 = rectangle.d_func()->bottomRight.latitude(); + + if (top1 < bottom2) + return false; + + if (bottom1 > top2) + return false; + + if ((top1 == 90.0) && (top1 == top2)) + return true; + + if ((bottom1 == -90.0) && (bottom1 == bottom2)) + return true; + + if (left1 < right1) { + if (left2 < right2) { + if ((left1 > right2) || (right1 < left2)) + return false; + } else { + if ((left1 > right2) && (right1 < left2)) + return false; + } + } else { + if (left2 < right2) { + if ((left2 > right1) && (right2 < left1)) + return false; + } else { + // if both wrap then they have to intersect + } + } + + return true; +} + +/*! + Translates this geo rectangle by \a degreesLatitude northwards and \a + degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. + + If the translation would have caused the geo rectangle to cross a pole the + geo rectangle will be translated until the top or bottom edge of the geo rectangle + touches the pole but not further. +*/ +void QGeoRectangle::translate(double degreesLatitude, double degreesLongitude) +{ + // TODO handle dlat, dlon larger than 360 degrees + + Q_D(QGeoRectangle); + + double tlLat = d->topLeft.latitude(); + double tlLon = d->topLeft.longitude(); + double brLat = d->bottomRight.latitude(); + double brLon = d->bottomRight.longitude(); + + if (degreesLatitude >= 0.0) + degreesLatitude = qMin(degreesLatitude, 90.0 - tlLat); + else + degreesLatitude = qMax(degreesLatitude, -90.0 - brLat); + + if ( (tlLon != -180.0) || (brLon != 180.0) ) { + tlLon = QLocationUtils::wrapLong(tlLon + degreesLongitude); + brLon = QLocationUtils::wrapLong(brLon + degreesLongitude); + } + + tlLat += degreesLatitude; + brLat += degreesLatitude; + + d->topLeft = QGeoCoordinate(tlLat, tlLon); + d->bottomRight = QGeoCoordinate(brLat, brLon); +} + +/*! + Returns a copy of this geo rectangle translated by \a degreesLatitude northwards and \a + degreesLongitude eastwards. + + Negative values of \a degreesLatitude and \a degreesLongitude correspond to + southward and westward translation respectively. + + \sa translate() +*/ +QGeoRectangle QGeoRectangle::translated(double degreesLatitude, double degreesLongitude) const +{ + QGeoRectangle result(*this); + result.translate(degreesLatitude, degreesLongitude); + return result; +} + +/*! + Extends the geo rectangle to also cover the coordinate \a coordinate + + \since 5.9 +*/ +void QGeoRectangle::extendRectangle(const QGeoCoordinate &coordinate) +{ + Q_D(QGeoRectangle); + d->extendShape(coordinate); +} + +/*! + Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle. + + If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the + width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the + rightmost longitude set to 180.0 degrees. This is done to ensure that the result is + independent of the order of the operands. + +*/ +QGeoRectangle QGeoRectangle::united(const QGeoRectangle &rectangle) const +{ + QGeoRectangle result(*this); + result |= rectangle; + return result; +} + +/*! + Extends the rectangle in the smallest possible way to include \a coordinate in + the shape. + + Both the rectangle and coordinate needs to be valid. If the rectangle already covers + the coordinate noting happens. + +*/ +void QGeoRectanglePrivate::extendShape(const QGeoCoordinate &coordinate) +{ + if (!isValid() || !coordinate.isValid() || contains(coordinate)) + return; + + double left = topLeft.longitude(); + double right = bottomRight.longitude(); + double top = topLeft.latitude(); + double bottom = bottomRight.latitude(); + + double inputLat = coordinate.latitude(); + double inputLon = coordinate.longitude(); + + top = qMax(top, inputLat); + bottom = qMin(bottom, inputLat); + + bool wrap = left > right; + + if (wrap && inputLon > right && inputLon < left) { + if (qAbs(left - inputLon) < qAbs(right - inputLon)) + left = inputLon; + else + right = inputLon; + } else if (!wrap) { + if (inputLon < left) { + if (360 - (right - inputLon) < left - inputLon) + right = inputLon; + else + left = inputLon; + } else if (inputLon > right) { + if (360 - (inputLon - left) < inputLon - right) + left = inputLon; + else + right = inputLon; + } + } + topLeft = QGeoCoordinate(top, left); + bottomRight = QGeoCoordinate(bottom, right); +} + +/*! + \fn QGeoRectangle QGeoRectangle::operator|(const QGeoRectangle &rectangle) const + + Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle. + + If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the + width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the + rightmost longitude set to 180.0 degrees. This is done to ensure that the result is + independent of the order of the operands. + +*/ + +/*! + Returns the smallest geo rectangle which contains both this geo rectangle and \a rectangle. + + If the centers of the two geo rectangles are separated by exactly 180.0 degrees then the + width is set to 360.0 degrees with the leftmost longitude set to -180.0 degrees and the + rightmost longitude set to 180.0 degrees. This is done to ensure that the result is + independent of the order of the operands. + +*/ +QGeoRectangle &QGeoRectangle::operator|=(const QGeoRectangle &rectangle) +{ + // If non-intersecting goes for most narrow box + + Q_D(QGeoRectangle); + + double left1 = d->topLeft.longitude(); + double right1 = d->bottomRight.longitude(); + double top1 = d->topLeft.latitude(); + double bottom1 = d->bottomRight.latitude(); + + double left2 = rectangle.d_func()->topLeft.longitude(); + double right2 = rectangle.d_func()->bottomRight.longitude(); + double top2 = rectangle.d_func()->topLeft.latitude(); + double bottom2 = rectangle.d_func()->bottomRight.latitude(); + + double top = qMax(top1, top2); + double bottom = qMin(bottom1, bottom2); + + double left = 0.0; + double right = 0.0; + + bool wrap1 = (left1 > right1); + bool wrap2 = (left2 > right2); + + if ((wrap1 && wrap2) || (!wrap1 && !wrap2)) { + + double w = qAbs((left1 + right1 - left2 - right2) / 2.0); + + if (w < 180.0) { + left = qMin(left1, left2); + right = qMax(right1, right2); + } else if (w > 180.0) { + left = qMax(left1, left2); + right = qMin(right1, right2); + } else { + left = -180.0; + right = 180.0; + } + + } else { + double wrapLeft = 0.0; + double wrapRight = 0.0; + double nonWrapLeft = 0.0; + double nonWrapRight = 0.0; + + if (wrap1) { + wrapLeft = left1; + wrapRight = right1; + nonWrapLeft = left2; + nonWrapRight = right2; + } else { + wrapLeft = left2; + wrapRight = right2; + nonWrapLeft = left1; + nonWrapRight = right1; + } + + bool joinWrapLeft = (nonWrapRight >= wrapLeft); + bool joinWrapRight = (nonWrapLeft <= wrapRight); + + if (joinWrapLeft) { + if (joinWrapRight) { + left = -180.0; + right = 180.0; + } else { + left = nonWrapLeft; + right = wrapRight; + } + } else { + if (joinWrapRight) { + left = wrapLeft; + right = nonWrapRight; + } else { + double wrapRightDistance = nonWrapLeft - wrapRight; + double wrapLeftDistance = wrapLeft - nonWrapRight; + + if (wrapLeftDistance == wrapRightDistance) { + left = -180.0; + right = 180.0; + } else if (wrapLeftDistance < wrapRightDistance) { + left = nonWrapLeft; + right = wrapRight; + } else { + left = wrapLeft; + right = nonWrapRight; + } + } + } + } + + if (((left1 == -180) && (right1 == 180.0)) + || ((left2 == -180) && (right2 == 180.0))) { + left = -180; + right = 180; + } + + d->topLeft = QGeoCoordinate(top, left); + d->bottomRight = QGeoCoordinate(bottom, right); + + return *this; +} + +/*! + Returns the geo rectangle properties as a string. + + \since 5.5 +*/ +QString QGeoRectangle::toString() const +{ + if (type() != QGeoShape::RectangleType) { + qWarning("Not a rectangle a %d\n", type()); + return QStringLiteral("QGeoRectangle(not a rectangle)"); + } + + return QStringLiteral("QGeoRectangle({%1, %2}, {%3, %4})") + .arg(topLeft().latitude()) + .arg(topLeft().longitude()) + .arg(bottomRight().latitude()) + .arg(bottomRight().longitude()); +} + +/******************************************************************************* +*******************************************************************************/ + +QGeoRectanglePrivate::QGeoRectanglePrivate() +: QGeoShapePrivate(QGeoShape::RectangleType) +{ +} + +QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoCoordinate &topLeft, + const QGeoCoordinate &bottomRight) +: QGeoShapePrivate(QGeoShape::RectangleType), topLeft(topLeft), bottomRight(bottomRight) +{ +} + +QGeoRectanglePrivate::QGeoRectanglePrivate(const QGeoRectanglePrivate &other) +: QGeoShapePrivate(QGeoShape::RectangleType), topLeft(other.topLeft), + bottomRight(other.bottomRight) +{ +} + +QGeoRectanglePrivate::~QGeoRectanglePrivate() {} + +QGeoShapePrivate *QGeoRectanglePrivate::clone() const +{ + return new QGeoRectanglePrivate(*this); +} + +bool QGeoRectanglePrivate::operator==(const QGeoShapePrivate &other) const +{ + if (!QGeoShapePrivate::operator==(other)) + return false; + + const QGeoRectanglePrivate &otherBox = static_cast(other); + + return topLeft == otherBox.topLeft && bottomRight == otherBox.bottomRight; +} + +QT_END_NAMESPACE + diff --git a/src/positioning/qgeorectangle.h b/src/positioning/qgeorectangle.h new file mode 100644 index 0000000..82afd72 --- /dev/null +++ b/src/positioning/qgeorectangle.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEORECTANGLE_H +#define QGEORECTANGLE_H + +#include + +QT_BEGIN_NAMESPACE + +class QGeoRectanglePrivate; + +class Q_POSITIONING_EXPORT QGeoRectangle : public QGeoShape +{ + Q_GADGET + Q_PROPERTY(QGeoCoordinate bottomLeft READ bottomLeft WRITE setBottomLeft) + Q_PROPERTY(QGeoCoordinate bottomRight READ bottomRight WRITE setBottomRight) + Q_PROPERTY(QGeoCoordinate topLeft READ topLeft WRITE setTopLeft) + Q_PROPERTY(QGeoCoordinate topRight READ topRight WRITE setTopRight) + Q_PROPERTY(QGeoCoordinate center READ center WRITE setCenter) + Q_PROPERTY(double height READ height WRITE setHeight) + Q_PROPERTY(double width READ width WRITE setWidth) + +public: + QGeoRectangle(); + QGeoRectangle(const QGeoCoordinate ¢er, double degreesWidth, double degreesHeight); + QGeoRectangle(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight); + QGeoRectangle(const QList &coordinates); + QGeoRectangle(const QGeoRectangle &other); + QGeoRectangle(const QGeoShape &other); + + ~QGeoRectangle(); + + QGeoRectangle &operator=(const QGeoRectangle &other); + + using QGeoShape::operator==; + bool operator==(const QGeoRectangle &other) const; + + using QGeoShape::operator!=; + bool operator!=(const QGeoRectangle &other) const; + + void setTopLeft(const QGeoCoordinate &topLeft); + QGeoCoordinate topLeft() const; + + void setTopRight(const QGeoCoordinate &topRight); + QGeoCoordinate topRight() const; + + void setBottomLeft(const QGeoCoordinate &bottomLeft); + QGeoCoordinate bottomLeft() const; + + void setBottomRight(const QGeoCoordinate &bottomRight); + QGeoCoordinate bottomRight() const; + + void setCenter(const QGeoCoordinate ¢er); + QGeoCoordinate center() const; + + void setWidth(double degreesWidth); + double width() const; + + void setHeight(double degreesHeight); + double height() const; + + using QGeoShape::contains; + bool contains(const QGeoRectangle &rectangle) const; + Q_INVOKABLE bool intersects(const QGeoRectangle &rectangle) const; + + Q_INVOKABLE void translate(double degreesLatitude, double degreesLongitude); + Q_INVOKABLE QGeoRectangle translated(double degreesLatitude, double degreesLongitude) const; + Q_INVOKABLE void extendRectangle(const QGeoCoordinate &coordinate); + + Q_INVOKABLE QGeoRectangle united(const QGeoRectangle &rectangle) const; + QGeoRectangle operator|(const QGeoRectangle &rectangle) const; + QGeoRectangle &operator|=(const QGeoRectangle &rectangle); + + Q_INVOKABLE QString toString() const; + +private: + inline QGeoRectanglePrivate *d_func(); + inline const QGeoRectanglePrivate *d_func() const; +}; + +Q_DECLARE_TYPEINFO(QGeoRectangle, Q_MOVABLE_TYPE); + +inline QGeoRectangle QGeoRectangle::operator|(const QGeoRectangle &rectangle) const +{ + return united(rectangle); +} + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoRectangle) + +#endif diff --git a/src/positioning/qgeorectangle_p.h b/src/positioning/qgeorectangle_p.h new file mode 100644 index 0000000..ec3a2b5 --- /dev/null +++ b/src/positioning/qgeorectangle_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEORECTANGLE_P_H +#define QGEORECTANGLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgeoshape_p.h" +#include "qgeocoordinate.h" + +QT_BEGIN_NAMESPACE + +class QGeoRectanglePrivate : public QGeoShapePrivate +{ +public: + QGeoRectanglePrivate(); + QGeoRectanglePrivate(const QGeoCoordinate &topLeft, const QGeoCoordinate &bottomRight); + QGeoRectanglePrivate(const QGeoRectanglePrivate &other); + ~QGeoRectanglePrivate(); + + bool isValid() const override; + bool isEmpty() const override; + bool contains(const QGeoCoordinate &coordinate) const override; + + QGeoCoordinate center() const override; + + QGeoRectangle boundingGeoRectangle() const override; + + void extendShape(const QGeoCoordinate &coordinate) override; + + QGeoShapePrivate *clone() const override; + + bool operator==(const QGeoShapePrivate &other) const override; + + QGeoCoordinate topLeft; + QGeoCoordinate bottomRight; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeosatelliteinfo.cpp b/src/positioning/qgeosatelliteinfo.cpp new file mode 100644 index 0000000..e62bd16 --- /dev/null +++ b/src/positioning/qgeosatelliteinfo.cpp @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qgeosatelliteinfo.h" + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoSatelliteInfoPrivate +{ +public: + int signal; + int satId; + QGeoSatelliteInfo::SatelliteSystem system; + QHash doubleAttribs; +}; + + +/*! + \class QGeoSatelliteInfo + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoSatelliteInfo class contains basic information about a satellite. + + \sa QGeoSatelliteInfoSource +*/ + +/*! + \enum QGeoSatelliteInfo::Attribute + Defines the attributes for the satellite information. + \value Elevation The elevation of the satellite, in degrees. + \value Azimuth The azimuth to true north, in degrees. +*/ + +/*! + \enum QGeoSatelliteInfo::SatelliteSystem + Defines the GNSS system of the satellite. + \value Undefined Not defined. + \value GPS Global Positioning System (USA). + \value GLONASS Global Positioning System (Russia). + +*/ + + +/*! + Creates a satellite information object. +*/ +QGeoSatelliteInfo::QGeoSatelliteInfo() + : d(new QGeoSatelliteInfoPrivate) +{ + d->signal = -1; + d->satId = -1; + d->system = QGeoSatelliteInfo::Undefined; +} + +/*! + Creates a satellite information object with the values of \a other. +*/ + +QGeoSatelliteInfo::QGeoSatelliteInfo(const QGeoSatelliteInfo &other) + : d(new QGeoSatelliteInfoPrivate) +{ + operator=(other); +} + +/*! + Destroys a satellite information object. +*/ +QGeoSatelliteInfo::~QGeoSatelliteInfo() +{ + delete d; +} + +/*! + Assigns the values from \a other to this object. +*/ +QGeoSatelliteInfo &QGeoSatelliteInfo::operator=(const QGeoSatelliteInfo & other) +{ + if (this == &other) + return *this; + + d->signal = other.d->signal; + d->satId = other.d->satId; + d->system = other.d->system; + d->doubleAttribs = other.d->doubleAttribs; + return *this; +} + +/*! + Returns true if all the information for this satellite + are the same as those of \a other. +*/ +bool QGeoSatelliteInfo::operator==(const QGeoSatelliteInfo &other) const +{ + return d->signal == other.d->signal + && d->satId == other.d->satId + && d->system == other.d->system + && d->doubleAttribs == other.d->doubleAttribs; +} + +/*! + \fn bool QGeoSatelliteInfo::operator!=(const QGeoSatelliteInfo &other) const; + + Returns true if any of the information for this satellite + are not the same as those of \a other. +*/ + + +/*! + Sets the Satellite System (GPS, GLONASS, ...) to \a system. +*/ +void QGeoSatelliteInfo::setSatelliteSystem(SatelliteSystem system) +{ + d->system = system; +} + +/*! + Returns the Satellite System (GPS, GLONASS, ...) +*/ +QGeoSatelliteInfo::SatelliteSystem QGeoSatelliteInfo::satelliteSystem() const +{ + return d->system; +} + +/*! + Sets the satellite identifier number to \a satId. + + The satellite identifier number can be used to identify a satellite inside the satellite system. + For satellite system GPS the satellite identifier number represents the PRN (Pseudo-random noise) number. + For satellite system GLONASS the satellite identifier number represents the slot number. +*/ +void QGeoSatelliteInfo::setSatelliteIdentifier(int satId) +{ + d->satId = satId; +} + +/*! + Returns the satellite identifier number. + + The satellite identifier number can be used to identify a satellite inside the satellite system. + For satellite system GPS the satellite identifier number represents the PRN (Pseudo-random noise) number. + For satellite system GLONASS the satellite identifier number represents the slot number. +*/ +int QGeoSatelliteInfo::satelliteIdentifier() const +{ + return d->satId; +} + +/*! + Sets the signal strength to \a signalStrength, in decibels. +*/ +void QGeoSatelliteInfo::setSignalStrength(int signalStrength) +{ + d->signal = signalStrength; +} + +/*! + Returns the signal strength, or -1 if the value has not been set. +*/ +int QGeoSatelliteInfo::signalStrength() const +{ + return d->signal; +} + +/*! + Sets the value for \a attribute to \a value. +*/ +void QGeoSatelliteInfo::setAttribute(Attribute attribute, qreal value) +{ + d->doubleAttribs[int(attribute)] = value; +} + +/*! + Returns the value of the specified \a attribute as a qreal value. + + Returns -1 if the value has not been set. + + \sa hasAttribute(), setAttribute() +*/ +qreal QGeoSatelliteInfo::attribute(Attribute attribute) const +{ + if (d->doubleAttribs.contains(int(attribute))) + return d->doubleAttribs[int(attribute)]; + return -1; +} + +/*! + Removes the specified \a attribute and its value. +*/ +void QGeoSatelliteInfo::removeAttribute(Attribute attribute) +{ + d->doubleAttribs.remove(int(attribute)); +} + +/*! + Returns true if the specified \a attribute is present in this update. +*/ +bool QGeoSatelliteInfo::hasAttribute(Attribute attribute) const +{ + return d->doubleAttribs.contains(int(attribute)); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGeoSatelliteInfo &info) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QGeoSatelliteInfo(system=" << info.d->system; + dbg << ", satId=" << info.d->satId; + dbg << ", signal-strength=" << info.d->signal; + + + QList attribs = info.d->doubleAttribs.keys(); + for (int i = 0; i < attribs.count(); ++i) { + dbg << ", "; + switch (attribs[i]) { + case QGeoSatelliteInfo::Elevation: + dbg << "Elevation="; + break; + case QGeoSatelliteInfo::Azimuth: + dbg << "Azimuth="; + break; + } + dbg << info.d->doubleAttribs[attribs[i]]; + } + dbg << ')'; + return dbg; +} +#endif + +#ifndef QT_NO_DATASTREAM +/*! + \fn QDataStream &operator<<(QDataStream &stream, const QGeoSatelliteInfo &info) + \relates QGeoSatelliteInfo + + Writes the given \a info to the specified \a stream. + + \sa {Serializing Qt Data Types} + +*/ + +QDataStream &operator<<(QDataStream &stream, const QGeoSatelliteInfo &info) +{ + stream << info.d->signal; + stream << info.d->doubleAttribs; + stream << info.d->satId; + stream << int(info.d->system); + return stream; +} +#endif + +#ifndef QT_NO_DATASTREAM +/*! + \fn QDataStream &operator>>(QDataStream &stream, QGeoSatelliteInfo &info) + \relates QGeoSatelliteInfo + + Reads satellite information from the specified \a stream into the given + \a info. + + \sa {Serializing Qt Data Types} +*/ + +QDataStream &operator>>(QDataStream &stream, QGeoSatelliteInfo &info) +{ + int system; + stream >> info.d->signal; + stream >> info.d->doubleAttribs; + stream >> info.d->satId; + stream >> system; + info.d->system = (QGeoSatelliteInfo::SatelliteSystem)system; + return stream; +} +#endif + +QT_END_NAMESPACE diff --git a/src/positioning/qgeosatelliteinfo.h b/src/positioning/qgeosatelliteinfo.h new file mode 100644 index 0000000..e68d8d9 --- /dev/null +++ b/src/positioning/qgeosatelliteinfo.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOSATELLITEINFO_H +#define QGEOSATELLITEINFO_H + +#include + +QT_BEGIN_NAMESPACE + +class QDebug; +class QDataStream; + +class QGeoSatelliteInfoPrivate; +class Q_POSITIONING_EXPORT QGeoSatelliteInfo +{ +public: + enum Attribute { + Elevation, + Azimuth + }; + + enum SatelliteSystem { + Undefined = 0x00, + GPS = 0x01, + GLONASS = 0x02 + }; + + QGeoSatelliteInfo(); + QGeoSatelliteInfo(const QGeoSatelliteInfo &other); + ~QGeoSatelliteInfo(); + + QGeoSatelliteInfo &operator=(const QGeoSatelliteInfo &other); + + bool operator==(const QGeoSatelliteInfo &other) const; + inline bool operator!=(const QGeoSatelliteInfo &other) const { + return !operator==(other); + } + + void setSatelliteSystem(SatelliteSystem system); + SatelliteSystem satelliteSystem() const; + + void setSatelliteIdentifier(int satId); + int satelliteIdentifier() const; + + void setSignalStrength(int signalStrength); + int signalStrength() const; + + void setAttribute(Attribute attribute, qreal value); + qreal attribute(Attribute attribute) const; + void removeAttribute(Attribute attribute); + + bool hasAttribute(Attribute attribute) const; + +private: +#ifndef QT_NO_DEBUG_STREAM + friend Q_POSITIONING_EXPORT QDebug operator<<(QDebug dbg, const QGeoSatelliteInfo &info); +#endif +#ifndef QT_NO_DATASTREAM + friend Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, const QGeoSatelliteInfo &info); + friend Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoSatelliteInfo &info); +#endif + QGeoSatelliteInfoPrivate *d; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_EXPORT QDebug operator<<(QDebug dbg, const QGeoSatelliteInfo &info); +#endif + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, const QGeoSatelliteInfo &info); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoSatelliteInfo &info); +#endif + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeosatelliteinfosource.cpp b/src/positioning/qgeosatelliteinfosource.cpp new file mode 100644 index 0000000..c55c36d --- /dev/null +++ b/src/positioning/qgeosatelliteinfosource.cpp @@ -0,0 +1,348 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include "qgeopositioninfosourcefactory.h" +#include "qgeopositioninfosource_p.h" +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \class QGeoSatelliteInfoSource + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoSatelliteInfoSource class is an abstract base class for the distribution of satellite information updates. + + The static function QGeoSatelliteInfoSource::createDefaultSource() creates a default + satellite data source that is appropriate for the platform, if one is + available. Otherwise, available QGeoPositionInfoSourceFactory plugins will + be checked for one that has a satellite data source available. + + Call startUpdates() and stopUpdates() to start and stop regular updates, + or requestUpdate() to request a single update. + When an update is available, satellitesInViewUpdated() and/or + satellitesInUseUpdated() will be emitted. + + If regular satellite updates are required, setUpdateInterval() can be used + to specify how often these updates should be emitted. If no interval is + specified, updates are simply provided whenever they are available. + For example: + + \code + // Emit updates every 10 seconds if available + QGeoSatelliteInfoSource *source = QGeoSatelliteInfoSource::createDefaultSource(0); + if (source) + source->setUpdateInterval(10000); + \endcode + + To remove an update interval that was previously set, call + setUpdateInterval() with a value of 0. + + Note that the satellite source may have a minimum value requirement for + update intervals, as returned by minimumUpdateInterval(). +*/ + +class QGeoSatelliteInfoSourcePrivate +{ +public: + int interval; + QString providerName; +}; + +/*! + Creates a satellite source with the specified \a parent. +*/ +QGeoSatelliteInfoSource::QGeoSatelliteInfoSource(QObject *parent) + : QObject(parent), + d(new QGeoSatelliteInfoSourcePrivate) +{ + d->interval = 0; +} + +/*! + Destroys the satellite source. +*/ +QGeoSatelliteInfoSource::~QGeoSatelliteInfoSource() +{ + delete d; +} + +/*! + Returns the unique name of the satellite source implementation in use. + + This is the same name that can be passed to createSource() in order to + create a new instance of a particular satellite source implementation. +*/ +QString QGeoSatelliteInfoSource::sourceName() const +{ + return d->providerName; +} + + +/*! + \property QGeoSatelliteInfoSource::updateInterval + \brief This property holds the requested interval in milliseconds between each update. + + If the update interval is not set (or is set to 0) the + source will provide updates as often as necessary. + + If the update interval is set, the source will provide updates at an + interval as close to the requested interval as possible. If the requested + interval is less than the minimumUpdateInterval(), + the minimum interval is used instead. + + Changes to the update interval will happen as soon as is practical, however the + time the change takes may vary between implementations. Whether or not the elapsed + time from the previous interval is counted as part of the new interval is also + implementation dependent. + + The default value for this property is 0. + + Note: Subclass implementations must call the base implementation of + setUpdateInterval() so that updateInterval() returns the correct value. +*/ +void QGeoSatelliteInfoSource::setUpdateInterval(int msec) +{ + d->interval = msec; +} + +int QGeoSatelliteInfoSource::updateInterval() const +{ + return d->interval; +} + + + +/*! + Creates and returns a source with the specified \a parent that reads + from the system's default source of satellite update information, or the + highest priority available plugin. + + Returns 0 if the system has no default satellite source, no valid plugins + could be found or the user does not have the permission to access the satellite data. +*/ +QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createDefaultSource(QObject *parent) +{ + QList plugins = QGeoPositionInfoSourcePrivate::pluginsSorted(); + foreach (const QJsonObject &obj, plugins) { + if (obj.value(QStringLiteral("Satellite")).isBool() + && obj.value(QStringLiteral("Satellite")).toBool()) + { + const QString testableKey = QStringLiteral("Testable"); + if (obj.contains(testableKey) && !obj.value(testableKey).toBool()) { + static bool inTest = qEnvironmentVariableIsSet("QT_QTESTLIB_RUNNING"); + if (inTest) + continue; + } + QGeoPositionInfoSourcePrivate d; + d.metaData = obj; + d.loadPlugin(); + QGeoSatelliteInfoSource *s = 0; + if (d.factory) + s = d.factory->satelliteInfoSource(parent); + if (s) + s->d->providerName = d.metaData.value(QStringLiteral("Provider")).toString(); + return s; + } + } + + return 0; +} + +/*! + Creates and returns a source with the given \a parent, + by loading the plugin named \a sourceName. + + Returns 0 if the plugin cannot be found. +*/ +QGeoSatelliteInfoSource *QGeoSatelliteInfoSource::createSource(const QString &sourceName, QObject *parent) +{ + QHash plugins = QGeoPositionInfoSourcePrivate::plugins(); + if (plugins.contains(sourceName)) { + QGeoPositionInfoSourcePrivate d; + d.metaData = plugins.value(sourceName); + d.loadPlugin(); + QGeoSatelliteInfoSource *src = 0; + if (d.factory) + src = d.factory->satelliteInfoSource(parent); + if (src) + src->d->providerName = d.metaData.value(QStringLiteral("Provider")).toString(); + return src; + } + + return 0; +} + +/*! + Returns a list of available source plugins, including the default system + backend if one is available. +*/ +QStringList QGeoSatelliteInfoSource::availableSources() +{ + QStringList plugins; + const QHash meta = QGeoPositionInfoSourcePrivate::plugins(); + for (auto it = meta.cbegin(), end = meta.cend(); it != end; ++it) { + if (it.value().value(QStringLiteral("Satellite")).isBool() + && it.value().value(QStringLiteral("Satellite")).toBool()) { + plugins << it.key(); + } + } + + return plugins; +} + +/*! + \fn void QGeoSatelliteInfoSource::satellitesInViewUpdated(const QList &satellites); + + If startUpdates() or requestUpdate() is called, this signal is emitted + when an update is available on the satellites that are + currently in view. + + The \a satellites parameter holds the satellites currently in view. +*/ + +/*! + \fn void QGeoSatelliteInfoSource::satellitesInUseUpdated(const QList &satellites); + + If startUpdates() or requestUpdate() is called, this signal is emitted + when an update is available on the number of satellites that are + currently in use. + + These are the satellites that are used to get a "fix" - that + is, those used to determine the current position. + + The \a satellites parameter holds the satellites currently in use. +*/ + +/*! + \property QGeoSatelliteInfoSource::minimumUpdateInterval + \brief This property holds the minimum time (in milliseconds) required to retrieve a satellite update. + + This is the minimum value accepted by setUpdateInterval() and + requestUpdate(). +*/ + + +/*! + \fn virtual void QGeoSatelliteInfoSource::startUpdates() = 0; + + Starts emitting updates at regular intervals. The updates will be + provided whenever new satellite information becomes available. + + If satellite information cannot be retrieved or some other + form of timeout has occurred the satellitesInViewUpdated() + and satellitesInUseUpdated() signals may be emitted with + empty parameter lists. + + \sa satellitesInViewUpdated(), satellitesInUseUpdated() +*/ + +/*! + \fn virtual void QGeoSatelliteInfoSource::stopUpdates() = 0; + + Stops emitting updates at regular intervals. +*/ + +/*! + \fn virtual void QGeoSatelliteInfoSource::requestUpdate(int timeout = 0); + + Attempts to get the current satellite information and emit + satellitesInViewUpdated() and satellitesInUseUpdated() with this + information. If the current satellite information cannot be found + within the given \a timeout (in milliseconds) or if \a timeout is less than the value returned by + minimumUpdateInterval(), requestTimeout() is + emitted. + + If the timeout is zero, the timeout defaults to a reasonable timeout + period as appropriate for the source. + + This does nothing if another update request is in progress. However + it can be called even if startUpdates() has already been called and + regular updates are in progress. +*/ + +/*! + \fn void QGeoSatelliteInfoSource::requestTimeout(); + + Emitted if requestUpdate() was called and the current satellite + information could not be retrieved within the specified timeout. + + While the triggering of this signal may be considered an error condition, + it does not imply the emission of the \c error() signal. Only the emission of + \c requestTimeout() is required to indicate a timeout. +*/ + +/*! + \fn QGeoSatelliteInfoSource::Error QGeoSatelliteInfoSource::error() const = 0 + + Returns the last error that occurred. + + This signal is not emitted when a requestTimeout() has occurred. +*/ + +/*! + \fn void QGeoSatelliteInfoSource::error(QGeoSatelliteInfoSource::Error satelliteError) + + This signal is emitted after an error occurred. The \a satelliteError + parameter describes the type of error that occurred. + +*/ + +/*! + \enum QGeoSatelliteInfoSource::Error + + The Error enumeration represents the errors which can occur. + + \value AccessError The connection setup to the satellite backend failed because the + application lacked the required privileges. + \value ClosedError The satellite backend closed the connection, which happens for example in case + the user is switching location services to off. This object becomes invalid and should be deleted. + A new satellite source can be created by calling createDefaultSource() later on. + \value NoError No error has occurred. + \value UnknownSourceError An unidentified error occurred. + */ + + +QT_END_NAMESPACE diff --git a/src/positioning/qgeosatelliteinfosource.h b/src/positioning/qgeosatelliteinfosource.h new file mode 100644 index 0000000..391eefc --- /dev/null +++ b/src/positioning/qgeosatelliteinfosource.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QGEOSATELLITEINFOSOURCE_H +#define QGEOSATELLITEINFOSOURCE_H + +#include + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGeoSatelliteInfoSourcePrivate; +class Q_POSITIONING_EXPORT QGeoSatelliteInfoSource : public QObject +{ + Q_OBJECT + Q_PROPERTY(int updateInterval READ updateInterval WRITE setUpdateInterval) + Q_PROPERTY(int minimumUpdateInterval READ minimumUpdateInterval) + +public: + enum Error { + AccessError = 0, + ClosedError = 1, + NoError = 2, + UnknownSourceError = -1 + }; + Q_ENUMS(Error) + + explicit QGeoSatelliteInfoSource(QObject *parent); + virtual ~QGeoSatelliteInfoSource(); + + static QGeoSatelliteInfoSource *createDefaultSource(QObject *parent); + static QGeoSatelliteInfoSource *createSource(const QString &sourceName, QObject *parent); + static QStringList availableSources(); + + QString sourceName() const; + + virtual void setUpdateInterval(int msec); + int updateInterval() const; + virtual int minimumUpdateInterval() const = 0; + virtual Error error() const = 0; + +public Q_SLOTS: + virtual void startUpdates() = 0; + virtual void stopUpdates() = 0; + + virtual void requestUpdate(int timeout = 0) = 0; + +Q_SIGNALS: + void satellitesInViewUpdated(const QList &satellites); + void satellitesInUseUpdated(const QList &satellites); + void requestTimeout(); + void error(QGeoSatelliteInfoSource::Error); + +private: + Q_DISABLE_COPY(QGeoSatelliteInfoSource) + QGeoSatelliteInfoSourcePrivate *d; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qgeoshape.cpp b/src/positioning/qgeoshape.cpp new file mode 100644 index 0000000..d17f9ee --- /dev/null +++ b/src/positioning/qgeoshape.cpp @@ -0,0 +1,454 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoshape.h" +#include "qgeoshape_p.h" +#include "qgeorectangle.h" +#include "qgeocircle.h" +#include "qgeopath.h" +#include "qgeopolygon.h" + + +#ifndef QT_NO_DEBUG_STREAM +#include +#endif + +#ifndef QT_NO_DATASTREAM +#include +#endif + +QT_BEGIN_NAMESPACE + +QGeoShapePrivate::QGeoShapePrivate(QGeoShape::ShapeType type) +: type(type) +{ +} + +QGeoShapePrivate::~QGeoShapePrivate() +{ +} + +bool QGeoShapePrivate::operator==(const QGeoShapePrivate &other) const +{ + return type == other.type; +} + +/*! + \class QGeoShape + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QGeoShape class defines a geographic area. + + This class is the base class for classes which specify a geographic + area. + + For the sake of consistency, subclasses should describe the specific + details of the associated areas in terms of QGeoCoordinate instances + and distances in meters. + + This class is a \l Q_GADGET since Qt 5.5. It can be + \l{Cpp_value_integration_positioning}{directly used from C++ and QML}. +*/ + +/*! + \enum QGeoShape::ShapeType + + Describes the type of the shape. + + \value UnknownType A shape of unknown type + \value RectangleType A rectangular shape + \value CircleType A circular shape + \value PathType A path type + \value PolygonType A polygon type +*/ + +/*! + \property QGeoShape::type + \brief This property holds the type of this geo shape. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoShape::isValid + \brief This property holds the validity of the geo shape. + + A geo shape is considered to be invalid if some of the data that is required to + unambiguously describe the geo shape has not been set or has been set to an + unsuitable value depending on the subclass of this object. The default constructed + objects of this type are invalid. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ + +/*! + \property QGeoShape::isEmpty + \brief This property defines whether this geo shape is empty. + + An empty geo shape is a region which has a geometrical area of 0. + + While this property is introduced in Qt 5.5, the related accessor functions + exist since the first version of this class. + + \since 5.5 +*/ +inline QGeoShapePrivate *QGeoShape::d_func() +{ + return static_cast(d_ptr.data()); +} + +inline const QGeoShapePrivate *QGeoShape::d_func() const +{ + return static_cast(d_ptr.constData()); +} + +/*! + Constructs a new invalid geo shape of \l UnknownType. +*/ +QGeoShape::QGeoShape() +{ +} + +/*! + Constructs a new geo shape which is a copy of \a other. +*/ +QGeoShape::QGeoShape(const QGeoShape &other) +: d_ptr(other.d_ptr) +{ +} + +/*! + \internal +*/ +QGeoShape::QGeoShape(QGeoShapePrivate *d) +: d_ptr(d) +{ +} + +/*! + Destroys this geo shape. +*/ +QGeoShape::~QGeoShape() +{ +} + +/*! + Returns the type of this geo shape. +*/ +QGeoShape::ShapeType QGeoShape::type() const +{ + Q_D(const QGeoShape); + + if (d) + return d->type; + else + return UnknownType; +} + +/*! + Returns whether this geo shape is valid. + +*/ +bool QGeoShape::isValid() const +{ + Q_D(const QGeoShape); + + if (d) + return d->isValid(); + else + return false; +} + +/*! + Returns whether this geo shape is empty. + + An empty geo shape is a region which has a geometrical area of 0. +*/ +bool QGeoShape::isEmpty() const +{ + Q_D(const QGeoShape); + + if (d) + return d->isEmpty(); + else + return true; +} + +/*! + Returns whether the coordinate \a coordinate is contained within this geo shape. +*/ +bool QGeoShape::contains(const QGeoCoordinate &coordinate) const +{ + Q_D(const QGeoShape); + + if (d) + return d->contains(coordinate); + else + return false; +} + +/*! + Returns a QGeoRectangle representing the geographical bounding rectangle of the + geo shape, that defines the latitudinal/longitudinal bounds of the geo shape. + + \since 5.9 +*/ +QGeoRectangle QGeoShape::boundingGeoRectangle() const +{ + Q_D(const QGeoShape); + + if (d) + return d->boundingGeoRectangle(); + else + return QGeoRectangle(); +} + +/*! + Returns the coordinate located at the geometric center of the geo shape. + + \since 5.5 +*/ +QGeoCoordinate QGeoShape::center() const +{ + Q_D(const QGeoShape); + + if (d) + return d->center(); + else + return QGeoCoordinate(); +} + +/*! + \deprecated + + This method used to extend the geo shape to also cover the coordinate \a coordinate. + + It currently only works for \l QGeoCircle and \l QGeoRectangle, on which the functionality remains, + now also accessible through QGeoCircle::extendCircle and QGeoRectangle::extendRectangle. + + This method should therefore not be called on a generic QGeoShape any longer, as the behavior for + other shape types is undefined. + + \sa QGeoRectangle::extendRectangle, QGeoCircle::extendCircle +*/ +void QGeoShape::extendShape(const QGeoCoordinate &coordinate) +{ + Q_D(QGeoShape); + + if (d) + d->extendShape(coordinate); +} + + +/*! + Returns true if the \a other geo shape is equivalent to this geo shape, otherwise returns + false. +*/ +bool QGeoShape::operator==(const QGeoShape &other) const +{ + Q_D(const QGeoShape); + + if (d == other.d_func()) + return true; + + if (!d || !(other.d_func())) + return false; + + return *d == *other.d_func(); +} + +/*! + Returns true if the \a other geo shape is not equivalent to this geo shape, otherwise returns + false. +*/ +bool QGeoShape::operator!=(const QGeoShape &other) const +{ + return !(*this == other); +} + +/*! + Assigns \a other to this geo shape and returns a reference to this geo shape. +*/ +QGeoShape &QGeoShape::operator=(const QGeoShape &other) +{ + if (this == &other) + return *this; + + d_ptr = other.d_ptr; + return *this; +} + +/*! + Returns a string representation of this geo shape. + + \since 5.5 +*/ +QString QGeoShape::toString() const +{ + return QStringLiteral("QGeoShape(%1)").arg(type()); +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QGeoShape &shape) +{ + QDebugStateSaver saver(dbg); + dbg.nospace() << "QGeoShape("; + switch (shape.type()) { + case QGeoShape::UnknownType: + dbg << "Unknown"; + break; + case QGeoShape::RectangleType: + dbg << "Rectangle"; + break; + case QGeoShape::PathType: + dbg << "Path"; + break; + case QGeoShape::PolygonType: + dbg << "Polygon"; + break; + case QGeoShape::CircleType: + dbg << "Circle"; + } + + dbg << ')'; + + return dbg; +} +#endif + +#ifndef QT_NO_DATASTREAM +QDataStream &operator<<(QDataStream &stream, const QGeoShape &shape) +{ + stream << quint32(shape.type()); + switch (shape.type()) { + case QGeoShape::UnknownType: + break; + case QGeoShape::RectangleType: { + QGeoRectangle r = shape; + stream << r.topLeft() << r.bottomRight(); + break; + } + case QGeoShape::CircleType: { + QGeoCircle c = shape; + stream << c.center() << c.radius(); + break; + } + case QGeoShape::PathType: { + QGeoPath p = shape; + stream << p.path().size(); + for (const auto &c: p.path()) + stream << c; + break; + } + case QGeoShape::PolygonType: { + QGeoPolygon p = shape; + stream << p.path().size(); + for (const auto &c: p.path()) + stream << c; + break; + } + } + + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QGeoShape &shape) +{ + quint32 type; + stream >> type; + + switch (type) { + case QGeoShape::UnknownType: + shape = QGeoShape(); + break; + case QGeoShape::RectangleType: { + QGeoCoordinate tl; + QGeoCoordinate br; + stream >> tl >> br; + shape = QGeoRectangle(tl, br); + break; + } + case QGeoShape::CircleType: { + QGeoCoordinate c; + qreal r; + stream >> c >> r; + shape = QGeoCircle(c, r); + break; + } + case QGeoShape::PathType: { + QList l; + QGeoCoordinate c; + int sz; + stream >> sz; + for (int i = 0; i < sz; i++) { + stream >> c; + l.append(c); + } + shape = QGeoPath(l); + break; + } + case QGeoShape::PolygonType: { + QList l; + QGeoCoordinate c; + int sz; + stream >> sz; + for (int i = 0; i < sz; i++) { + stream >> c; + l.append(c); + } + shape = QGeoPolygon(l); + break; + } + } + + return stream; +} +#endif + +#include "moc_qgeoshape.cpp" + +QT_END_NAMESPACE diff --git a/src/positioning/qgeoshape.h b/src/positioning/qgeoshape.h new file mode 100644 index 0000000..c0bc658 --- /dev/null +++ b/src/positioning/qgeoshape.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSHAPE_H +#define QGEOSHAPE_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QDebug; +class QGeoShapePrivate; +class QGeoRectangle; + +class Q_POSITIONING_EXPORT QGeoShape +{ + Q_GADGET + Q_PROPERTY(ShapeType type READ type) + Q_PROPERTY(bool isValid READ isValid) + Q_PROPERTY(bool isEmpty READ isEmpty) + Q_ENUMS(ShapeType) + +public: + QGeoShape(); + QGeoShape(const QGeoShape &other); + ~QGeoShape(); + + enum ShapeType { + UnknownType, + RectangleType, + CircleType, + PathType, + PolygonType + }; + + ShapeType type() const; + + bool isValid() const; + bool isEmpty() const; + Q_INVOKABLE bool contains(const QGeoCoordinate &coordinate) const; + Q_INVOKABLE QGeoRectangle boundingGeoRectangle() const; + Q_INVOKABLE QGeoCoordinate center() const; + + Q_INVOKABLE void extendShape(const QGeoCoordinate &coordinate); + + bool operator==(const QGeoShape &other) const; + bool operator!=(const QGeoShape &other) const; + + QGeoShape &operator=(const QGeoShape &other); + + Q_INVOKABLE QString toString() const; +protected: + QGeoShape(QGeoShapePrivate *d); + + QSharedDataPointer d_ptr; + +private: + inline QGeoShapePrivate *d_func(); + inline const QGeoShapePrivate *d_func() const; +}; + +Q_DECLARE_TYPEINFO(QGeoShape, Q_MOVABLE_TYPE); + +#ifndef QT_NO_DEBUG_STREAM +Q_POSITIONING_EXPORT QDebug operator<<(QDebug, const QGeoShape &); +#endif + +#ifndef QT_NO_DATASTREAM +Q_POSITIONING_EXPORT QDataStream &operator<<(QDataStream &stream, const QGeoShape &shape); +Q_POSITIONING_EXPORT QDataStream &operator>>(QDataStream &stream, QGeoShape &shape); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoShape) + +#endif diff --git a/src/positioning/qgeoshape_p.h b/src/positioning/qgeoshape_p.h new file mode 100644 index 0000000..f838065 --- /dev/null +++ b/src/positioning/qgeoshape_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSHAPE_P_H +#define QGEOSHAPE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +#include "qgeorectangle.h" + +QT_BEGIN_NAMESPACE + +class QGeoShapePrivate : public QSharedData +{ +public: + explicit QGeoShapePrivate(QGeoShape::ShapeType type); + virtual ~QGeoShapePrivate(); + + virtual bool isValid() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const QGeoCoordinate &coordinate) const = 0; + + virtual QGeoCoordinate center() const = 0; + + virtual QGeoRectangle boundingGeoRectangle() const = 0; + + virtual void extendShape(const QGeoCoordinate &coordinate) = 0; + + virtual QGeoShapePrivate *clone() const = 0; + + virtual bool operator==(const QGeoShapePrivate &other) const; + + QGeoShape::ShapeType type; +}; + +// don't use the copy constructor when detaching from a QSharedDataPointer, use virtual clone() +// call instead. +template <> +Q_INLINE_TEMPLATE QGeoShapePrivate *QSharedDataPointer::clone() +{ + return d->clone(); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qlocationdata_simulator.cpp b/src/positioning/qlocationdata_simulator.cpp new file mode 100644 index 0000000..3045b86 --- /dev/null +++ b/src/positioning/qlocationdata_simulator.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocationdata_simulator_p.h" + +#include + +QT_BEGIN_NAMESPACE + +QGeoPositionInfoData::QGeoPositionInfoData() + : latitude(0.0), + longitude(0.0), + altitude(0.0), + direction(0.0), + groundSpeed(0.0), + verticalSpeed(0.0), + magneticVariation(0.0), + horizontalAccuracy(0.0), + verticalAccuracy(0.0), + dateTime(), + minimumInterval(0), + enabled(false) {} + +QGeoSatelliteInfoData::SatelliteInfo::SatelliteInfo() + : azimuth(0.0), + elevation(0.0), + signalStrength(0), + inUse(false), + satelliteSystem(Undefined), + satelliteIdentifier(0) {} + +void qt_registerLocationTypes() +{ + qRegisterMetaTypeStreamOperators("QGeoPositionInfoData"); + qRegisterMetaTypeStreamOperators("QGeoSatelliteInfoData"); + qRegisterMetaTypeStreamOperators("QGeoSatelliteInfoData::SatelliteInfo"); +} + +QDataStream &operator<<(QDataStream &out, const QGeoPositionInfoData &s) +{ + out << s.latitude << s.longitude << s.altitude; + out << s.direction << s.groundSpeed << s.verticalSpeed << s.magneticVariation << s.horizontalAccuracy << s.verticalAccuracy; + out << s.dateTime; + out << s.minimumInterval << s.enabled; + return out; +} + +QDataStream &operator>>(QDataStream &in, QGeoPositionInfoData &s) +{ + in >> s.latitude >> s.longitude >> s.altitude; + in >> s.direction >> s.groundSpeed >> s.verticalSpeed >> s.magneticVariation >> s.horizontalAccuracy >> s.verticalAccuracy; + in >> s.dateTime; + in >> s.minimumInterval >> s.enabled; + return in; +} + +QDataStream &operator<<(QDataStream &out, const QGeoSatelliteInfoData &s) +{ + out << s.satellites; + return out; +} + +QDataStream &operator>>(QDataStream &in, QGeoSatelliteInfoData &s) +{ + in >> s.satellites; + return in; +} + +QDataStream &operator<<(QDataStream &out, const QGeoSatelliteInfoData::SatelliteInfo &s) +{ + out << s.azimuth << s.elevation << s.signalStrength << s.inUse << static_cast(s.satelliteSystem) << s.satelliteIdentifier; + return out; +} + +QDataStream &operator>>(QDataStream &in, QGeoSatelliteInfoData::SatelliteInfo &s) +{ + qint32 satelliteSystem; + in >> s.azimuth >> s.elevation >> s.signalStrength >> s.inUse >> satelliteSystem >> s.satelliteIdentifier; + s.satelliteSystem = static_cast(satelliteSystem); + return in; +} + +QT_END_NAMESPACE diff --git a/src/positioning/qlocationdata_simulator_p.h b/src/positioning/qlocationdata_simulator_p.h new file mode 100644 index 0000000..47f3acf --- /dev/null +++ b/src/positioning/qlocationdata_simulator_p.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOPOSITIONINFODATA_SIMULATOR_P_H +#define QGEOPOSITIONINFODATA_SIMULATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +// +// DO NOT REMOVE +// ------------- +// +// This header file contains structures used to serialize communication between +// simulator's client and server implementations, it is included by simulator +// positioning plugin. + +#include "qpositioningglobal_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +struct Q_POSITIONING_PRIVATE_EXPORT QGeoPositionInfoData +{ + QGeoPositionInfoData(); + + // Coordinate information + double latitude; + double longitude; + double altitude; + + // Attributes + // ### transmit whether attributes are set or not + qreal direction; + qreal groundSpeed; + qreal verticalSpeed; + qreal magneticVariation; + qreal horizontalAccuracy; + qreal verticalAccuracy; + + // DateTime info + QDateTime dateTime; + + int minimumInterval; + bool enabled; +}; + +struct Q_POSITIONING_PRIVATE_EXPORT QGeoSatelliteInfoData +{ + struct SatelliteInfo + { + SatelliteInfo(); + + // This enum duplicates the SatelliteSystem enum defined in qgeosatelliteinfo.h, which cannot be + // included as this file must compile with Qt4 (it is used by Qt Simulator) + enum SatelliteSystem + { + Undefined = 0x00, + GPS = 0x01, + GLONASS = 0x02 + }; + + qreal azimuth; + qreal elevation; + int signalStrength; + bool inUse; + SatelliteSystem satelliteSystem; + int satelliteIdentifier; + }; + + QList satellites; +}; + +Q_POSITIONING_PRIVATE_EXPORT void qt_registerLocationTypes(); +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &out, const QGeoPositionInfoData &s); +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &in, QGeoPositionInfoData &s); +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &out, const QGeoSatelliteInfoData &s); +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &in, QGeoSatelliteInfoData &s); +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator<<(QDataStream &out, const QGeoSatelliteInfoData::SatelliteInfo &s); +Q_POSITIONING_PRIVATE_EXPORT QDataStream &operator>>(QDataStream &in, QGeoSatelliteInfoData::SatelliteInfo &s); + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGeoPositionInfoData) +Q_DECLARE_METATYPE(QGeoSatelliteInfoData) +Q_DECLARE_METATYPE(QGeoSatelliteInfoData::SatelliteInfo) + +#endif // QGEOPOSITIONINFODATA_SIMULATOR_P_H diff --git a/src/positioning/qlocationutils.cpp b/src/positioning/qlocationutils.cpp new file mode 100644 index 0000000..f5062eb --- /dev/null +++ b/src/positioning/qlocationutils.cpp @@ -0,0 +1,416 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qlocationutils_p.h" +#include "qgeopositioninfo.h" + +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +// converts e.g. 15306.0235 from NMEA sentence to 153.100392 +static double qlocationutils_nmeaDegreesToDecimal(double nmeaDegrees) +{ + double deg; + double min = 100.0 * modf(nmeaDegrees / 100.0, °); + return deg + (min / 60.0); +} + +static void qlocationutils_readGga(const char *data, int size, QGeoPositionInfo *info, double uere, + bool *hasFix) +{ + QByteArray sentence(data, size); + QList parts = sentence.split(','); + QGeoCoordinate coord; + + if (hasFix && parts.count() > 6 && parts[6].count() > 0) + *hasFix = parts[6].toInt() > 0; + + if (parts.count() > 1 && parts[1].count() > 0) { + QTime time; + if (QLocationUtils::getNmeaTime(parts[1], &time)) + info->setTimestamp(QDateTime(QDate(), time, Qt::UTC)); + } + + if (parts.count() > 5 && parts[3].count() == 1 && parts[5].count() == 1) { + double lat; + double lng; + if (QLocationUtils::getNmeaLatLong(parts[2], parts[3][0], parts[4], parts[5][0], &lat, &lng)) { + coord.setLatitude(lat); + coord.setLongitude(lng); + } + } + + if (parts.count() > 8 && !parts[8].isEmpty()) { + bool hasHdop = false; + double hdop = parts[8].toDouble(&hasHdop); + if (hasHdop) + info->setAttribute(QGeoPositionInfo::HorizontalAccuracy, 2 * hdop * uere); + } + + if (parts.count() > 9 && parts[9].count() > 0) { + bool hasAlt = false; + double alt = parts[9].toDouble(&hasAlt); + if (hasAlt) + coord.setAltitude(alt); + } + + if (coord.type() != QGeoCoordinate::InvalidCoordinate) + info->setCoordinate(coord); +} + +static void qlocationutils_readGsa(const char *data, int size, QGeoPositionInfo *info, double uere, + bool *hasFix) +{ + QList parts = QByteArray::fromRawData(data, size).split(','); + + if (hasFix && parts.count() > 2 && !parts[2].isEmpty()) + *hasFix = parts[2].toInt() > 0; + + if (parts.count() > 16 && !parts[16].isEmpty()) { + bool hasHdop = false; + double hdop = parts[16].toDouble(&hasHdop); + if (hasHdop) + info->setAttribute(QGeoPositionInfo::HorizontalAccuracy, 2 * hdop * uere); + } + + if (parts.count() > 17 && !parts[17].isEmpty()) { + bool hasVdop = false; + double vdop = parts[17].toDouble(&hasVdop); + if (hasVdop) + info->setAttribute(QGeoPositionInfo::VerticalAccuracy, 2 * vdop * uere); + } +} + +static void qlocationutils_readGll(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) +{ + QByteArray sentence(data, size); + QList parts = sentence.split(','); + QGeoCoordinate coord; + + if (hasFix && parts.count() > 6 && parts[6].count() > 0) + *hasFix = (parts[6][0] == 'A'); + + if (parts.count() > 5 && parts[5].count() > 0) { + QTime time; + if (QLocationUtils::getNmeaTime(parts[5], &time)) + info->setTimestamp(QDateTime(QDate(), time, Qt::UTC)); + } + + if (parts.count() > 4 && parts[2].count() == 1 && parts[4].count() == 1) { + double lat; + double lng; + if (QLocationUtils::getNmeaLatLong(parts[1], parts[2][0], parts[3], parts[4][0], &lat, &lng)) { + coord.setLatitude(lat); + coord.setLongitude(lng); + } + } + + if (coord.type() != QGeoCoordinate::InvalidCoordinate) + info->setCoordinate(coord); +} + +static void qlocationutils_readRmc(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) +{ + QByteArray sentence(data, size); + QList parts = sentence.split(','); + QGeoCoordinate coord; + QDate date; + QTime time; + + if (hasFix && parts.count() > 2 && parts[2].count() > 0) + *hasFix = (parts[2][0] == 'A'); + + if (parts.count() > 9 && parts[9].count() == 6) { + date = QDate::fromString(QString::fromLatin1(parts[9]), QStringLiteral("ddMMyy")); + if (date.isValid()) + date = date.addYears(100); // otherwise starts from 1900 + else + date = QDate(); + } + + if (parts.count() > 1 && parts[1].count() > 0) + QLocationUtils::getNmeaTime(parts[1], &time); + + if (parts.count() > 6 && parts[4].count() == 1 && parts[6].count() == 1) { + double lat; + double lng; + if (QLocationUtils::getNmeaLatLong(parts[3], parts[4][0], parts[5], parts[6][0], &lat, &lng)) { + coord.setLatitude(lat); + coord.setLongitude(lng); + } + } + + bool parsed = false; + double value = 0.0; + if (parts.count() > 7 && parts[7].count() > 0) { + value = parts[7].toDouble(&parsed); + if (parsed) + info->setAttribute(QGeoPositionInfo::GroundSpeed, qreal(value * 1.852 / 3.6)); // knots -> m/s + } + if (parts.count() > 8 && parts[8].count() > 0) { + value = parts[8].toDouble(&parsed); + if (parsed) + info->setAttribute(QGeoPositionInfo::Direction, qreal(value)); + } + if (parts.count() > 11 && parts[11].count() == 1 + && (parts[11][0] == 'E' || parts[11][0] == 'W')) { + value = parts[10].toDouble(&parsed); + if (parsed) { + if (parts[11][0] == 'W') + value *= -1; + info->setAttribute(QGeoPositionInfo::MagneticVariation, qreal(value)); + } + } + + if (coord.type() != QGeoCoordinate::InvalidCoordinate) + info->setCoordinate(coord); + + info->setTimestamp(QDateTime(date, time, Qt::UTC)); +} + +static void qlocationutils_readVtg(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) +{ + if (hasFix) + *hasFix = false; + + QByteArray sentence(data, size); + QList parts = sentence.split(','); + + bool parsed = false; + double value = 0.0; + if (parts.count() > 1 && parts[1].count() > 0) { + value = parts[1].toDouble(&parsed); + if (parsed) + info->setAttribute(QGeoPositionInfo::Direction, qreal(value)); + } + if (parts.count() > 7 && parts[7].count() > 0) { + value = parts[7].toDouble(&parsed); + if (parsed) + info->setAttribute(QGeoPositionInfo::GroundSpeed, qreal(value / 3.6)); // km/h -> m/s + } +} + +static void qlocationutils_readZda(const char *data, int size, QGeoPositionInfo *info, bool *hasFix) +{ + if (hasFix) + *hasFix = false; + + QByteArray sentence(data, size); + QList parts = sentence.split(','); + QDate date; + QTime time; + + if (parts.count() > 1 && parts[1].count() > 0) + QLocationUtils::getNmeaTime(parts[1], &time); + + if (parts.count() > 4 && parts[2].count() > 0 && parts[3].count() > 0 + && parts[4].count() == 4) { // must be full 4-digit year + int day = parts[2].toUInt(); + int month = parts[3].toUInt(); + int year = parts[4].toUInt(); + if (day > 0 && month > 0 && year > 0) + date.setDate(year, month, day); + } + + info->setTimestamp(QDateTime(date, time, Qt::UTC)); +} + +QLocationUtils::NmeaSentence QLocationUtils::getNmeaSentenceType(const char *data, int size) +{ + if (size < 6 || data[0] != '$' || !hasValidNmeaChecksum(data, size)) + return NmeaSentenceInvalid; + + if (data[3] == 'G' && data[4] == 'G' && data[5] == 'A') + return NmeaSentenceGGA; + + if (data[3] == 'G' && data[4] == 'S' && data[5] == 'A') + return NmeaSentenceGSA; + + if (data[3] == 'G' && data[4] == 'L' && data[5] == 'L') + return NmeaSentenceGLL; + + if (data[3] == 'R' && data[4] == 'M' && data[5] == 'C') + return NmeaSentenceRMC; + + if (data[3] == 'V' && data[4] == 'T' && data[5] == 'G') + return NmeaSentenceVTG; + + if (data[3] == 'Z' && data[4] == 'D' && data[5] == 'A') + return NmeaSentenceZDA; + + return NmeaSentenceInvalid; +} + +bool QLocationUtils::getPosInfoFromNmea(const char *data, int size, QGeoPositionInfo *info, + double uere, bool *hasFix) +{ + if (!info) + return false; + + if (hasFix) + *hasFix = false; + + NmeaSentence nmeaType = getNmeaSentenceType(data, size); + if (nmeaType == NmeaSentenceInvalid) + return false; + + // Adjust size so that * and following characters are not parsed by the following functions. + for (int i = 0; i < size; ++i) { + if (data[i] == '*') { + size = i; + break; + } + } + + switch (nmeaType) { + case NmeaSentenceGGA: + qlocationutils_readGga(data, size, info, uere, hasFix); + return true; + case NmeaSentenceGSA: + qlocationutils_readGsa(data, size, info, uere, hasFix); + return true; + case NmeaSentenceGLL: + qlocationutils_readGll(data, size, info, hasFix); + return true; + case NmeaSentenceRMC: + qlocationutils_readRmc(data, size, info, hasFix); + return true; + case NmeaSentenceVTG: + qlocationutils_readVtg(data, size, info, hasFix); + return true; + case NmeaSentenceZDA: + qlocationutils_readZda(data, size, info, hasFix); + return true; + default: + return false; + } +} + +bool QLocationUtils::hasValidNmeaChecksum(const char *data, int size) +{ + int asteriskIndex = -1; + for (int i = 0; i < size; ++i) { + if (data[i] == '*') { + asteriskIndex = i; + break; + } + } + + const int CSUM_LEN = 2; + if (asteriskIndex < 0 || asteriskIndex + CSUM_LEN >= size) + return false; + + // XOR byte value of all characters between '$' and '*' + int result = 0; + for (int i = 1; i < asteriskIndex; ++i) + result ^= data[i]; + /* + char calc[CSUM_LEN + 1]; + ::snprintf(calc, CSUM_LEN + 1, "%02x", result); + return ::strncmp(calc, &data[asteriskIndex+1], 2) == 0; + */ + + QByteArray checkSumBytes(&data[asteriskIndex + 1], 2); + bool ok = false; + int checksum = checkSumBytes.toInt(&ok,16); + return ok && checksum == result; +} + +bool QLocationUtils::getNmeaTime(const QByteArray &bytes, QTime *time) +{ + int dotIndex = bytes.indexOf('.'); + QTime tempTime; + + if (dotIndex < 0) { + tempTime = QTime::fromString(QString::fromLatin1(bytes.constData()), + QStringLiteral("hhmmss")); + } else { + tempTime = QTime::fromString(QString::fromLatin1(bytes.mid(0, dotIndex)), + QStringLiteral("hhmmss")); + bool hasMsecs = false; + int midLen = qMin(3, bytes.size() - dotIndex - 1); + int msecs = bytes.mid(dotIndex + 1, midLen).toUInt(&hasMsecs); + if (hasMsecs) + tempTime = tempTime.addMSecs(msecs*(midLen == 3 ? 1 : midLen == 2 ? 10 : 100)); + } + + if (tempTime.isValid()) { + *time = tempTime; + return true; + } + return false; +} + +bool QLocationUtils::getNmeaLatLong(const QByteArray &latString, char latDirection, const QByteArray &lngString, char lngDirection, double *lat, double *lng) +{ + if ((latDirection != 'N' && latDirection != 'S') + || (lngDirection != 'E' && lngDirection != 'W')) { + return false; + } + + bool hasLat = false; + bool hasLong = false; + double tempLat = latString.toDouble(&hasLat); + double tempLng = lngString.toDouble(&hasLong); + if (hasLat && hasLong) { + tempLat = qlocationutils_nmeaDegreesToDecimal(tempLat); + if (latDirection == 'S') + tempLat *= -1; + tempLng = qlocationutils_nmeaDegreesToDecimal(tempLng); + if (lngDirection == 'W') + tempLng *= -1; + + if (isValidLat(tempLat) && isValidLong(tempLng)) { + *lat = tempLat; + *lng = tempLng; + return true; + } + } + return false; +} + +QT_END_NAMESPACE + diff --git a/src/positioning/qlocationutils_p.h b/src/positioning/qlocationutils_p.h new file mode 100644 index 0000000..d9e6524 --- /dev/null +++ b/src/positioning/qlocationutils_p.h @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QLOCATIONUTILS_P_H +#define QLOCATIONUTILS_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include // needed for non-std:: versions of functions +#include +#include +#include + +static const double offsetEpsilon = 1e-12; // = 0.000000000001 +static const double leftOffset = -180.0 + offsetEpsilon; +static const double rightOffset = 180.0 - offsetEpsilon; + +QT_BEGIN_NAMESPACE +class QTime; +class QByteArray; + +class QGeoPositionInfo; +class Q_POSITIONING_PRIVATE_EXPORT QLocationUtils +{ +public: + enum CardinalDirection { + CardinalN, + CardinalE, + CardinalS, + CardinalW, + CardinalNE, + CardinalSE, + CardinalSW, + CardinalNW, + CardinalNNE, + CardinalENE, + CardinalESE, + CardinalSSE, + CardinalSSW, + CardinalWSW, + CardinalWNW, + CardinalNNW + }; + + enum NmeaSentence { + NmeaSentenceInvalid, + NmeaSentenceGGA, // Fix information + NmeaSentenceGSA, // Overall Satellite data, such as HDOP and VDOP + NmeaSentenceGLL, // Lat/Lon data + NmeaSentenceRMC, // Recommended minimum data for gps + NmeaSentenceVTG, // Vector track an Speed over the Ground + NmeaSentenceZDA // Date and Time + }; + + inline static bool isValidLat(double lat) { + return lat >= -90.0 && lat <= 90.0; + } + inline static bool isValidLong(double lng) { + return lng >= -180.0 && lng <= 180.0; + } + + inline static double clipLat(double lat, double clipValue = 90.0) { + if (lat > clipValue) + lat = clipValue; + else if (lat < -clipValue) + lat = -clipValue; + return lat; + } + + inline static double wrapLong(double lng) { + if (lng > 180.0) + lng -= 360.0; + else if (lng < -180.0) + lng += 360.0; + return lng; + } + + inline static CardinalDirection azimuthToCardinalDirection4(double azimuth) + { + azimuth = fmod(azimuth, 360.0); + if (azimuth < 45.0 || azimuth > 315.0 ) + return CardinalN; + else if (azimuth < 135.0) + return CardinalE; + else if (azimuth < 225.0) + return CardinalS; + else + return CardinalW; + } + + inline static CardinalDirection azimuthToCardinalDirection8(double azimuth) + { + azimuth = fmod(azimuth, 360.0); + if (azimuth < 22.5 || azimuth > 337.5 ) + return CardinalN; + else if (azimuth < 67.5) + return CardinalNE; + else if (azimuth < 112.5) + return CardinalE; + else if (azimuth < 157.5) + return CardinalSE; + else if (azimuth < 202.5) + return CardinalS; + + else if (azimuth < 247.5) + return CardinalSW; + else if (azimuth < 292.5) + return CardinalW; + else + return CardinalNW; + } + + inline static CardinalDirection azimuthToCardinalDirection16(double azimuth) + { + azimuth = fmod(azimuth, 360.0); + if (azimuth < 11.5 || azimuth > 348.75 ) + return CardinalN; + else if (azimuth < 33.75) + return CardinalNNE; + else if (azimuth < 56.25) + return CardinalNE; + else if (azimuth < 78.75) + return CardinalENE; + else if (azimuth < 101.25) + return CardinalE; + else if (azimuth < 123.75) + return CardinalESE; + else if (azimuth < 146.25) + return CardinalSE; + else if (azimuth < 168.75) + return CardinalSSE; + else if (azimuth < 191.25) + return CardinalS; + + else if (azimuth < 213.75) + return CardinalSSW; + else if (azimuth < 236.25) + return CardinalSW; + else if (azimuth < 258.75) + return CardinalWSW; + else if (azimuth < 281.25) + return CardinalW; + else if (azimuth < 303.75) + return CardinalWNW; + else if (azimuth < 326.25) + return CardinalNW; + else + return CardinalNNW; + } + + // For values exceeding +- 720.0 + inline static double wrapLongExt(double lng) { + double remainder = fmod(lng + 180.0, 360.0); + return fmod(remainder + 360.0, 360.0) - 180.0; + } + + // Mirrors the azimuth against the X axis. Azimuth assumed to be in [0,360[ + inline static double mirrorAzimuthX(double azimuth) { + if (azimuth <= 90.0) + return 180.0 - azimuth; + else + return 180.0 + (360.0 - azimuth); + } + + // Mirrors the azimuth against the Y axis. Azimuth assumed to be in [0,360[ + inline static double mirrorAzimuthY(double azimuth) { + if (azimuth == 0.0) + return 0.0; + return 360.0 - azimuth; + } + + inline static double radians(double degrees) + { + return qDegreesToRadians(degrees); + } + + inline static double degrees(double radians) + { + return qRadiansToDegrees(radians); + } + + inline static double earthMeanRadius() + { + return 6371007.2; + } + + inline static double earthMeanDiameter() + { + return earthMeanRadius() * 2.0 * M_PI; + } + + inline static double mercatorMaxLatitude() + { + return 85.05113; + } + + inline static QGeoCoordinate antipodalPoint(const QGeoCoordinate &p) + { + return QGeoCoordinate(-p.latitude(), wrapLong(p.longitude() + 180.0)); + } + + // Leftmost longitude before wrapping kicks in + inline static double mapLeftLongitude(double centerLongitude) + { + return wrapLong(centerLongitude + leftOffset); + } + + // Rightmost longitude before wrapping kicks in + inline static double mapRightLongitude(double centerLongitude) + { + return wrapLong(centerLongitude - leftOffset); + } + + /* + returns the NMEA sentence type. + */ + static NmeaSentence getNmeaSentenceType(const char *data, int size); + + /* + Creates a QGeoPositionInfo from a GGA, GLL, RMC, VTG or ZDA sentence. + + Note: + - GGA and GLL sentences have time but not date so the update's + QDateTime object will have an invalid date. + - RMC reports date with a two-digit year so in this case the year + is assumed to be after the year 2000. + */ + static bool getPosInfoFromNmea(const char *data, + int size, + QGeoPositionInfo *info, double uere, + bool *hasFix = nullptr); + + /* + Returns true if the given NMEA sentence has a valid checksum. + */ + static bool hasValidNmeaChecksum(const char *data, int size); + + /* + Returns time from a string in hhmmss or hhmmss.z+ format. + */ + static bool getNmeaTime(const QByteArray &bytes, QTime *time); + + /* + Accepts for example ("2734.7964", 'S', "15306.0124", 'E') and returns the + lat-long values. Fails if lat or long fail isValidLat() or isValidLong(). + */ + static bool getNmeaLatLong(const QByteArray &latString, + char latDirection, + const QByteArray &lngString, + char lngDirection, + double *lat, + double *lon); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qnmeapositioninfosource.cpp b/src/positioning/qnmeapositioninfosource.cpp new file mode 100644 index 0000000..0b8c3ed --- /dev/null +++ b/src/positioning/qnmeapositioninfosource.cpp @@ -0,0 +1,961 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qnmeapositioninfosource_p.h" +#include "qgeopositioninfo_p.h" +#include "qlocationutils_p.h" + +#include +#include +#include +#include +#include +#include +#include + + +QT_BEGIN_NAMESPACE + +#define USE_NMEA_PIMPL 0 + +#if USE_NMEA_PIMPL +class QGeoPositionInfoPrivateNmea : public QGeoPositionInfoPrivate +{ +public: + virtual ~QGeoPositionInfoPrivateNmea(); + virtual QGeoPositionInfoPrivate *clone() const; + + QList nmeaSentences; +}; + + +QGeoPositionInfoPrivateNmea::~QGeoPositionInfoPrivateNmea() +{ + +} + +QGeoPositionInfoPrivate *QGeoPositionInfoPrivateNmea::clone() const +{ + return new QGeoPositionInfoPrivateNmea(*this); +} +#else +typedef QGeoPositionInfoPrivate QGeoPositionInfoPrivateNmea; +#endif + +static bool propagateCoordinate(QGeoPositionInfo &dst, const QGeoPositionInfo &src, bool force = true) +{ + bool updated = false; + QGeoCoordinate c = dst.coordinate(); + const QGeoCoordinate & srcCoordinate = src.coordinate(); + if (qIsFinite(src.coordinate().latitude()) + && (!qIsFinite(dst.coordinate().latitude()) || force)) { + updated |= (c.latitude() != srcCoordinate.latitude()); + c.setLatitude(src.coordinate().latitude()); + } + if (qIsFinite(src.coordinate().longitude()) + && (!qIsFinite(dst.coordinate().longitude()) || force)) { + updated |= (c.longitude() != srcCoordinate.longitude()); + c.setLongitude(src.coordinate().longitude()); + } + if (qIsFinite(src.coordinate().altitude()) + && (!qIsFinite(dst.coordinate().altitude()) || force)) { + updated |= (c.altitude() != srcCoordinate.altitude()); + c.setAltitude(src.coordinate().altitude()); + } + dst.setCoordinate(c); + return updated; +} + +static bool propagateDate(QGeoPositionInfo &dst, const QGeoPositionInfo &src) +{ + if (!dst.timestamp().date().isValid() && src.timestamp().isValid()) { // time was supposed to be set/the same already. Date can be overwritten. + dst.setTimestamp(src.timestamp()); + return true; + } + return false; +} + +static bool propagateAttributes(QGeoPositionInfo &dst, const QGeoPositionInfo &src, bool force = true) +{ + bool updated = false; + static Q_DECL_CONSTEXPR std::array attrs { + { QGeoPositionInfo::GroundSpeed + ,QGeoPositionInfo::HorizontalAccuracy + ,QGeoPositionInfo::VerticalAccuracy + ,QGeoPositionInfo::Direction + ,QGeoPositionInfo::VerticalSpeed + ,QGeoPositionInfo::MagneticVariation} }; + for (const auto a: attrs) { + if (src.hasAttribute(a) && (!dst.hasAttribute(a) || force)) { + updated |= (dst.attribute(a) != src.attribute(a)); + dst.setAttribute(a, src.attribute(a)); + } + } + + return updated; +} + +// returns false if src does not contain any additional or different data than dst, +// true otherwise. +static bool mergePositions(QGeoPositionInfo &dst, const QGeoPositionInfo &src, QByteArray nmeaSentence) +{ + bool updated = false; + + updated |= propagateCoordinate(dst, src); + updated |= propagateDate(dst, src); + updated |= propagateAttributes(dst, src); + +#if USE_NMEA_PIMPL + QGeoPositionInfoPrivateNmea *dstPimpl = static_cast(QGeoPositionInfoPrivate::get(dst)); + dstPimpl->nmeaSentences.append(nmeaSentence); +#else + Q_UNUSED(nmeaSentence) +#endif + return updated; +} + +static qint64 msecsTo(const QDateTime &from, const QDateTime &to) +{ + if (!from.time().isValid() || !to.time().isValid()) + return 0; + + if (!from.date().isValid() || !to.date().isValid()) // use only time + return from.time().msecsTo(to.time()); + + return from.msecsTo(to); +} + +QNmeaRealTimeReader::QNmeaRealTimeReader(QNmeaPositionInfoSourcePrivate *sourcePrivate) + : QNmeaReader(sourcePrivate), m_update(*new QGeoPositionInfoPrivateNmea) +{ + // An env var controlling the number of milliseconds to use to withold + // an update and wait for additional data to combine. + // The update will be pushed earlier than this if a newer update will be received. + // The update will be withold longer than this amount of time if additional + // valid data will keep arriving within this time frame. + QByteArray pushDelay = qgetenv("QT_NMEA_PUSH_DELAY"); + if (!pushDelay.isEmpty()) + m_pushDelay = qBound(-1, QString::fromLatin1(pushDelay).toInt(), 1000); + else + m_pushDelay = 20; + + if (m_pushDelay >= 0) { + m_timer.setSingleShot(true); + m_timer.setInterval(m_pushDelay); + m_timer.connect(&m_timer, &QTimer::timeout, [this]() { + this->notifyNewUpdate(); + }); + } +} + +void QNmeaRealTimeReader::readAvailableData() +{ + while (m_proxy->m_device->canReadLine()) { + const QTime infoTime = m_update.timestamp().time(); // if update has been set, time must be valid. + const QDate infoDate = m_update.timestamp().date(); // this one might not be valid, as some sentences do not contain it + + QGeoPositionInfoPrivateNmea *pimpl = new QGeoPositionInfoPrivateNmea; + QGeoPositionInfo pos(*pimpl); + + char buf[1024]; + qint64 size = m_proxy->m_device->readLine(buf, sizeof(buf)); + const bool oldFix = m_hasFix; + bool hasFix; + const bool parsed = m_proxy->parsePosInfoFromNmeaData(buf, size, &pos, &hasFix); + + if (!parsed) { + // got garbage, don't stop the timer + continue; + } + + m_hasFix |= hasFix; + m_updateParsed = true; + + // Date may or may not be valid, as some packets do not have date. + // If date isn't valid, match is performed on time only. + // Hence, make sure that packet blocks are generated with + // the sentences containing the full timestamp (e.g., GPRMC) *first* ! + if (infoTime.isValid()) { + if (pos.timestamp().time().isValid()) { + const bool newerTime = infoTime < pos.timestamp().time(); + const bool newerDate = (infoDate.isValid() // if time is valid but one date or both are not, + && pos.timestamp().date().isValid() + && infoDate < pos.timestamp().date()); + if (newerTime || newerDate) { + // Effectively read data for different update, that is also newer, + // so flush retained update, and copy the new pos into m_update + const QDate updateDate = m_update.timestamp().date(); + const QDate lastPushedDate = m_lastPushedTS.date(); + const bool newerTimestampSinceLastPushed = m_update.timestamp() > m_lastPushedTS; + const bool invalidDate = !(updateDate.isValid() && lastPushedDate.isValid()); + const bool newerTimeSinceLastPushed = m_update.timestamp().time() > m_lastPushedTS.time(); + if ( newerTimestampSinceLastPushed || (invalidDate && newerTimeSinceLastPushed)) { + m_proxy->notifyNewUpdate(&m_update, oldFix); + m_lastPushedTS = m_update.timestamp(); + } + m_timer.stop(); + // next update data + propagateAttributes(pos, m_update, false); + m_update = pos; + m_hasFix = hasFix; + } else { + if (infoTime == pos.timestamp().time()) + // timestamps match -- merge into m_update + if (mergePositions(m_update, pos, QByteArray(buf, size))) { + // Reset the timer only if new info has been received. + // Else the source might be keep repeating outdated info until + // new info become available. + m_timer.stop(); + } + // else discard out of order outdated info. + } + } else { + // no timestamp available in parsed update-- merge into m_update + if (mergePositions(m_update, pos, QByteArray(buf, size))) + m_timer.stop(); + } + } else { + // there was no info with valid TS. Overwrite with whatever is parsed. +#if USE_NMEA_PIMPL + pimpl->nmeaSentences.append(QByteArray(buf, size)); +#endif + propagateAttributes(pos, m_update); + m_update = pos; + m_timer.stop(); + } + } + + if (m_updateParsed) { + if (m_pushDelay < 0) + notifyNewUpdate(); + else + m_timer.start(); + } +} + +void QNmeaRealTimeReader::notifyNewUpdate() +{ + const bool newerTime = m_update.timestamp().time() > m_lastPushedTS.time(); + const bool newerDate = (m_update.timestamp().date().isValid() + && m_lastPushedTS.date().isValid() + && m_update.timestamp().date() > m_lastPushedTS.date()); + if (newerTime || newerDate) { + m_proxy->notifyNewUpdate(&m_update, m_hasFix); + m_lastPushedTS = m_update.timestamp(); + } + m_timer.stop(); +} + + +//============================================================ + +QNmeaSimulatedReader::QNmeaSimulatedReader(QNmeaPositionInfoSourcePrivate *sourcePrivate) + : QNmeaReader(sourcePrivate), + m_currTimerId(-1), + m_hasValidDateTime(false) +{ +} + +QNmeaSimulatedReader::~QNmeaSimulatedReader() +{ + if (m_currTimerId > 0) + killTimer(m_currTimerId); +} + +void QNmeaSimulatedReader::readAvailableData() +{ + if (m_currTimerId > 0) // we are already reading + return; + + if (!m_hasValidDateTime) { // first update + Q_ASSERT(m_proxy->m_device && (m_proxy->m_device->openMode() & QIODevice::ReadOnly)); + + if (!setFirstDateTime()) { + //m_proxy->notifyReachedEndOfFile(); + qWarning("QNmeaPositionInfoSource: cannot find NMEA sentence with valid date & time"); + return; + } + + m_hasValidDateTime = true; + simulatePendingUpdate(); + + } else { + // previously read to EOF, but now new data has arrived + processNextSentence(); + } +} + +static int processSentence(QGeoPositionInfo &info, + QByteArray &m_nextLine, + QNmeaPositionInfoSourcePrivate *m_proxy, + QQueue &m_pendingUpdates, + bool &hasFix) +{ + int timeToNextUpdate = -1; + QDateTime prevTs; + if (m_pendingUpdates.size() > 0) + prevTs = m_pendingUpdates.head().info.timestamp(); + + // find the next update with a valid time (as long as the time is valid, + // we can calculate when the update should be emitted) + while (m_nextLine.size() || (m_proxy->m_device && m_proxy->m_device->bytesAvailable() > 0)) { + char static_buf[1024]; + char *buf = static_buf; + QByteArray nextLine; + qint64 size = 0; + if (m_nextLine.size()) { + // Read something in the previous call, but TS was later. + size = m_nextLine.size(); + nextLine = m_nextLine; + m_nextLine.clear(); + buf = nextLine.data(); + } else { + size = m_proxy->m_device->readLine(buf, sizeof(static_buf)); + } + + if (size <= 0) + continue; + + const QTime infoTime = info.timestamp().time(); // if info has been set, time must be valid. + const QDate infoDate = info.timestamp().date(); // this one might not be valid, as some sentences do not contain it + + /* + Packets containing time information are GGA, RMC, ZDA, GLL: + + GGA : GPS fix data - only time + GLL : geographic latitude and longitude - only time + RMC : recommended minimum FPOS/transit data - date and time + ZDA : only timestamp - date and time + + QLocationUtils is currently also capable of parsing VTG and GSA sentences: + + VTG: containing Track made good and ground speed + GSA: overall satellite data, w. accuracies (ends up into PositionInfo) + + Since these sentences contain no timestamp, their content will be merged with the content + from any prior sentence that had timestamp info, if any is available. + */ + + QGeoPositionInfoPrivateNmea *pimpl = new QGeoPositionInfoPrivateNmea; + QGeoPositionInfo pos(*pimpl); + if (m_proxy->parsePosInfoFromNmeaData(buf, size, &pos, &hasFix)) { + // Date may or may not be valid, as some packets do not have date. + // If date isn't valid, match is performed on time only. + // Hence, make sure that packet blocks are generated with + // the sentences containing the full timestamp (e.g., GPRMC) *first* ! + if (infoTime.isValid()) { + if (pos.timestamp().time().isValid()) { + const bool newerTime = infoTime < pos.timestamp().time(); + const bool newerDate = (infoDate.isValid() // if time is valid but one date or both are not, + && pos.timestamp().date().isValid() + && infoDate < pos.timestamp().date()); + if (newerTime || newerDate) { + // Effectively read data for different update, that is also newer, so copy buf into m_nextLine + m_nextLine = QByteArray(buf, size); + break; + } else { + if (infoTime == pos.timestamp().time()) + // timestamps match -- merge into info + mergePositions(info, pos, QByteArray(buf, size)); + // else discard out of order outdated info. + } + } else { + // no timestamp available -- merge into info + mergePositions(info, pos, QByteArray(buf, size)); + } + } else { + // there was no info with valid TS. Overwrite with whatever is parsed. +#if USE_NMEA_PIMPL + pimpl->nmeaSentences.append(QByteArray(buf, size)); +#endif + info = pos; + } + + if (prevTs.time().isValid()) { + timeToNextUpdate = msecsTo(prevTs, info.timestamp()); + if (timeToNextUpdate < 0) // Somehow parsing expired packets, reset info + info = QGeoPositionInfo(*new QGeoPositionInfoPrivateNmea); + } + } + } + + return timeToNextUpdate; +} + +bool QNmeaSimulatedReader::setFirstDateTime() +{ + // find the first update with valid date and time + QGeoPositionInfo info(*new QGeoPositionInfoPrivateNmea); + bool hasFix = false; + processSentence(info, m_nextLine, m_proxy, m_pendingUpdates, hasFix); + + if (info.timestamp().time().isValid()) { // NMEA may have sentences with only time and no date. These would generate invalid positions + QPendingGeoPositionInfo pending; + pending.info = info; + pending.hasFix = hasFix; + m_pendingUpdates.enqueue(pending); + return true; + } + return false; +} + +void QNmeaSimulatedReader::simulatePendingUpdate() +{ + if (m_pendingUpdates.size() > 0) { + // will be dequeued in processNextSentence() + QPendingGeoPositionInfo &pending = m_pendingUpdates.head(); + m_proxy->notifyNewUpdate(&pending.info, pending.hasFix); + } + + processNextSentence(); +} + +void QNmeaSimulatedReader::timerEvent(QTimerEvent *event) +{ + killTimer(event->timerId()); + m_currTimerId = -1; + simulatePendingUpdate(); +} + +void QNmeaSimulatedReader::processNextSentence() +{ + QGeoPositionInfo info(*new QGeoPositionInfoPrivateNmea); + bool hasFix = false; + + int timeToNextUpdate = processSentence(info, m_nextLine, m_proxy, m_pendingUpdates, hasFix); + if (timeToNextUpdate < 0) + return; + + m_pendingUpdates.dequeue(); + + QPendingGeoPositionInfo pending; + pending.info = info; + pending.hasFix = hasFix; + m_pendingUpdates.enqueue(pending); + m_currTimerId = startTimer(timeToNextUpdate); +} + + +//============================================================ + + +QNmeaPositionInfoSourcePrivate::QNmeaPositionInfoSourcePrivate(QNmeaPositionInfoSource *parent, QNmeaPositionInfoSource::UpdateMode updateMode) + : QObject(parent), + m_updateMode(updateMode), + m_device(0), + m_invokedStart(false), + m_positionError(QGeoPositionInfoSource::UnknownSourceError), + m_userEquivalentRangeError(qQNaN()), + m_source(parent), + m_nmeaReader(0), + m_updateTimer(0), + m_requestTimer(0), + m_horizontalAccuracy(qQNaN()), + m_verticalAccuracy(qQNaN()), + m_noUpdateLastInterval(false), + m_updateTimeoutSent(false), + m_connectedReadyRead(false) +{ +} + +QNmeaPositionInfoSourcePrivate::~QNmeaPositionInfoSourcePrivate() +{ + delete m_nmeaReader; + delete m_updateTimer; +} + +bool QNmeaPositionInfoSourcePrivate::openSourceDevice() +{ + if (!m_device) { + qWarning("QNmeaPositionInfoSource: no QIODevice data source, call setDevice() first"); + return false; + } + + if (!m_device->isOpen() && !m_device->open(QIODevice::ReadOnly)) { + qWarning("QNmeaPositionInfoSource: cannot open QIODevice data source"); + return false; + } + + connect(m_device, SIGNAL(aboutToClose()), SLOT(sourceDataClosed())); + connect(m_device, SIGNAL(readChannelFinished()), SLOT(sourceDataClosed())); + connect(m_device, SIGNAL(destroyed()), SLOT(sourceDataClosed())); + + return true; +} + +void QNmeaPositionInfoSourcePrivate::sourceDataClosed() +{ + if (m_nmeaReader && m_device && m_device->bytesAvailable()) + m_nmeaReader->readAvailableData(); +} + +void QNmeaPositionInfoSourcePrivate::readyRead() +{ + if (m_nmeaReader) + m_nmeaReader->readAvailableData(); +} + +bool QNmeaPositionInfoSourcePrivate::initialize() +{ + if (m_nmeaReader) + return true; + + if (!openSourceDevice()) + return false; + + if (m_updateMode == QNmeaPositionInfoSource::RealTimeMode) + m_nmeaReader = new QNmeaRealTimeReader(this); + else + m_nmeaReader = new QNmeaSimulatedReader(this); + + return true; +} + +void QNmeaPositionInfoSourcePrivate::prepareSourceDevice() +{ + // some data may already be available + if (m_updateMode == QNmeaPositionInfoSource::SimulationMode) { + if (m_nmeaReader && m_device->bytesAvailable()) + m_nmeaReader->readAvailableData(); + } + + if (!m_connectedReadyRead) { + connect(m_device, SIGNAL(readyRead()), SLOT(readyRead())); + m_connectedReadyRead = true; + } +} + +bool QNmeaPositionInfoSourcePrivate::parsePosInfoFromNmeaData(const char *data, int size, + QGeoPositionInfo *posInfo, bool *hasFix) +{ + return m_source->parsePosInfoFromNmeaData(data, size, posInfo, hasFix); +} + +void QNmeaPositionInfoSourcePrivate::startUpdates() +{ + if (m_invokedStart) + return; + + m_invokedStart = true; + m_pendingUpdate = QGeoPositionInfo(); + m_noUpdateLastInterval = false; + + bool initialized = initialize(); + if (!initialized) + return; + + if (m_updateMode == QNmeaPositionInfoSource::RealTimeMode) { + // skip over any buffered data - we only want the newest data. + // Don't do this in requestUpdate. In that case bufferedData is good to have/use. + if (m_device->bytesAvailable()) { + if (m_device->isSequential()) + m_device->readAll(); + else + m_device->seek(m_device->bytesAvailable()); + } + } + + if (m_updateTimer) + m_updateTimer->stop(); + + if (m_source->updateInterval() > 0) { + if (!m_updateTimer) + m_updateTimer = new QBasicTimer; + m_updateTimer->start(m_source->updateInterval(), this); + } + + if (initialized) + prepareSourceDevice(); +} + +void QNmeaPositionInfoSourcePrivate::stopUpdates() +{ + m_invokedStart = false; + if (m_updateTimer) + m_updateTimer->stop(); + m_pendingUpdate = QGeoPositionInfo(); + m_noUpdateLastInterval = false; +} + +void QNmeaPositionInfoSourcePrivate::requestUpdate(int msec) +{ + if (m_requestTimer && m_requestTimer->isActive()) + return; + + if (msec <= 0 || msec < m_source->minimumUpdateInterval()) { + emit m_source->updateTimeout(); + return; + } + + if (!m_requestTimer) { + m_requestTimer = new QTimer(this); + connect(m_requestTimer, SIGNAL(timeout()), SLOT(updateRequestTimeout())); + } + + bool initialized = initialize(); + if (!initialized) { + emit m_source->updateTimeout(); + return; + } + + m_requestTimer->start(msec); + prepareSourceDevice(); +} + +void QNmeaPositionInfoSourcePrivate::updateRequestTimeout() +{ + m_requestTimer->stop(); + emit m_source->updateTimeout(); +} + +void QNmeaPositionInfoSourcePrivate::notifyNewUpdate(QGeoPositionInfo *update, bool hasFix) +{ + // include before uncommenting + //qDebug() << "QNmeaPositionInfoSourcePrivate::notifyNewUpdate()" << update->timestamp() << hasFix << m_invokedStart << (m_requestTimer && m_requestTimer->isActive()); + + QDate date = update->timestamp().date(); + if (date.isValid()) { + m_currentDate = date; + } else { + // some sentence have time but no date + QTime time = update->timestamp().time(); + if (time.isValid() && m_currentDate.isValid()) + update->setTimestamp(QDateTime(m_currentDate, time, Qt::UTC)); + } + + // Some attributes are sent in separate NMEA sentences. Save and restore the accuracy + // measurements. + if (update->hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + m_horizontalAccuracy = update->attribute(QGeoPositionInfo::HorizontalAccuracy); + else if (!qIsNaN(m_horizontalAccuracy)) + update->setAttribute(QGeoPositionInfo::HorizontalAccuracy, m_horizontalAccuracy); + + if (update->hasAttribute(QGeoPositionInfo::VerticalAccuracy)) + m_verticalAccuracy = update->attribute(QGeoPositionInfo::VerticalAccuracy); + else if (!qIsNaN(m_verticalAccuracy)) + update->setAttribute(QGeoPositionInfo::VerticalAccuracy, m_verticalAccuracy); + + if (hasFix && update->isValid()) { + if (m_requestTimer && m_requestTimer->isActive()) { // User called requestUpdate() + m_requestTimer->stop(); + emitUpdated(*update); + } else if (m_invokedStart) { // user called startUpdates() + if (m_updateTimer && m_updateTimer->isActive()) { // update interval > 0 + // for periodic updates, only want the most recent update + m_pendingUpdate = *update; // Set what to send in timerEvent() + if (m_noUpdateLastInterval) { + // if the update was invalid when timerEvent was last called, a valid update + // should be sent ASAP + emitPendingUpdate(); + m_noUpdateLastInterval = false; + } + } else { // update interval <= 0 + emitUpdated(*update); + } + } + m_lastUpdate = *update; // Set in any case, if update is valid. Used in lastKnownPosition(). + } +} + +void QNmeaPositionInfoSourcePrivate::timerEvent(QTimerEvent *) +{ + emitPendingUpdate(); +} + +void QNmeaPositionInfoSourcePrivate::emitPendingUpdate() +{ + if (m_pendingUpdate.isValid()) { + m_updateTimeoutSent = false; + m_noUpdateLastInterval = false; + emitUpdated(m_pendingUpdate); + m_pendingUpdate = QGeoPositionInfo(); + } else { // invalid update + if (m_noUpdateLastInterval && !m_updateTimeoutSent) { + m_updateTimeoutSent = true; + m_pendingUpdate = QGeoPositionInfo(); // Invalid already, but clear just in case. + emit m_source->updateTimeout(); + } + m_noUpdateLastInterval = true; + } +} + +void QNmeaPositionInfoSourcePrivate::emitUpdated(const QGeoPositionInfo &update) +{ + // check for duplication already done in QNmeaRealTimeReader::notifyNewUpdate + // and QNmeaRealTimeReader::readAvailableData + m_lastUpdate = update; + emit m_source->positionUpdated(update); +} + +//========================================================= + +/*! + \class QNmeaPositionInfoSource + \inmodule QtPositioning + \ingroup QtPositioning-positioning + \since 5.2 + + \brief The QNmeaPositionInfoSource class provides positional information using a NMEA data source. + + NMEA is a commonly used protocol for the specification of one's global + position at a certain point in time. The QNmeaPositionInfoSource class reads NMEA + data and uses it to provide positional data in the form of + QGeoPositionInfo objects. + + A QNmeaPositionInfoSource instance operates in either \l {RealTimeMode} or + \l {SimulationMode}. These modes allow NMEA data to be read from either a + live source of positional data, or replayed for simulation purposes from + previously recorded NMEA data. + + The source of NMEA data is set with setDevice(). + + Use startUpdates() to start receiving regular position updates and stopUpdates() to stop these + updates. If you only require updates occasionally, you can call requestUpdate() to request a + single update. + + In both cases the position information is received via the positionUpdated() signal and the + last known position can be accessed with lastKnownPosition(). + + QNmeaPositionInfoSource supports reporting the accuracy of the horizontal and vertical position. + To enable position accuracy reporting an estimate of the User Equivalent Range Error associated + with the NMEA source must be set with setUserEquivalentRangeError(). +*/ + + +/*! + \enum QNmeaPositionInfoSource::UpdateMode + Defines the available update modes. + + \value RealTimeMode Positional data is read and distributed from the data source as it becomes available. Use this mode if you are using a live source of positional data (for example, a GPS hardware device). + \value SimulationMode The data and time information in the NMEA source data is used to provide positional updates at the rate at which the data was originally recorded. Use this mode if the data source contains previously recorded NMEA data and you want to replay the data for simulation purposes. +*/ + + +/*! + Constructs a QNmeaPositionInfoSource instance with the given \a parent + and \a updateMode. +*/ +QNmeaPositionInfoSource::QNmeaPositionInfoSource(UpdateMode updateMode, QObject *parent) + : QGeoPositionInfoSource(parent), + d(new QNmeaPositionInfoSourcePrivate(this, updateMode)) +{ +} + +/*! + Destroys the position source. +*/ +QNmeaPositionInfoSource::~QNmeaPositionInfoSource() +{ + delete d; +} + +/*! + Sets the User Equivalent Range Error (UERE) to \a uere. The UERE is used in calculating an + estimate of the accuracy of the position information reported by the position info source. The + UERE should be set to a value appropriate for the GPS device which generated the NMEA stream. + + The true UERE value is calculated from multiple error sources including errors introduced by + the satellites and signal propogation delays through the atmosphere as well as errors + introduced by the receiving GPS equipment. For details on GPS accuracy see + \l {http://edu-observatory.org/gps/gps_accuracy.html}. + + A typical value for UERE is approximately 5.1. + + \since 5.3 + + \sa userEquivalentRangeError() +*/ +void QNmeaPositionInfoSource::setUserEquivalentRangeError(double uere) +{ + d->m_userEquivalentRangeError = uere; +} + +/*! + Returns the current User Equivalent Range Error (UERE). The UERE is used in calculating an + estimate of the accuracy of the position information reported by the position info source. The + default value is NaN which means no accuracy information will be provided. + + \since 5.3 + + \sa setUserEquivalentRangeError() +*/ +double QNmeaPositionInfoSource::userEquivalentRangeError() const +{ + return d->m_userEquivalentRangeError; +} + +/*! + Parses an NMEA sentence string into a QGeoPositionInfo. + + The default implementation will parse standard NMEA sentences. + This method should be reimplemented in a subclass whenever the need to deal with non-standard + NMEA sentences arises. + + The parser reads \a size bytes from \a data and uses that information to setup \a posInfo and + \a hasFix. If \a hasFix is set to false then \a posInfo may contain only the time or the date + and the time. + + Returns true if the sentence was succsesfully parsed, otherwise returns false and should not + modifiy \a posInfo or \a hasFix. +*/ +bool QNmeaPositionInfoSource::parsePosInfoFromNmeaData(const char *data, int size, + QGeoPositionInfo *posInfo, bool *hasFix) +{ + return QLocationUtils::getPosInfoFromNmea(data, size, posInfo, d->m_userEquivalentRangeError, + hasFix); +} + +/*! + Returns the update mode. +*/ +QNmeaPositionInfoSource::UpdateMode QNmeaPositionInfoSource::updateMode() const +{ + return d->m_updateMode; +} + +/*! + Sets the NMEA data source to \a device. If the device is not open, it + will be opened in QIODevice::ReadOnly mode. + + The source device can only be set once and must be set before calling + startUpdates() or requestUpdate(). + + \b {Note:} The \a device must emit QIODevice::readyRead() for the + source to be notified when data is available for reading. + QNmeaPositionInfoSource does not assume the ownership of the device, + and hence does not deallocate it upon destruction. +*/ +void QNmeaPositionInfoSource::setDevice(QIODevice *device) +{ + if (device != d->m_device) { + if (!d->m_device) + d->m_device = device; + else + qWarning("QNmeaPositionInfoSource: source device has already been set"); + } +} + +/*! + Returns the NMEA data source. +*/ +QIODevice *QNmeaPositionInfoSource::device() const +{ + return d->m_device; +} + +/*! + \reimp +*/ +void QNmeaPositionInfoSource::setUpdateInterval(int msec) +{ + int interval = msec; + if (interval != 0) + interval = qMax(msec, minimumUpdateInterval()); + QGeoPositionInfoSource::setUpdateInterval(interval); + if (d->m_invokedStart) { + d->stopUpdates(); + d->startUpdates(); + } +} + +/*! + \reimp +*/ +void QNmeaPositionInfoSource::startUpdates() +{ + d->startUpdates(); +} + +/*! + \reimp +*/ +void QNmeaPositionInfoSource::stopUpdates() +{ + d->stopUpdates(); +} + +/*! + \reimp +*/ +void QNmeaPositionInfoSource::requestUpdate(int msec) +{ + d->requestUpdate(msec == 0 ? 60000 * 5 : msec); // 5min default timeout +} + +/*! + \reimp +*/ +QGeoPositionInfo QNmeaPositionInfoSource::lastKnownPosition(bool) const +{ + // the bool value does not matter since we only use satellite positioning + return d->m_lastUpdate; +} + +/*! + \reimp +*/ +QGeoPositionInfoSource::PositioningMethods QNmeaPositionInfoSource::supportedPositioningMethods() const +{ + return SatellitePositioningMethods; +} + +/*! + \reimp +*/ +int QNmeaPositionInfoSource::minimumUpdateInterval() const +{ + return 2; // Some chips are capable of over 100 updates per seconds. +} + +/*! + \reimp +*/ +QGeoPositionInfoSource::Error QNmeaPositionInfoSource::error() const +{ + return d->m_positionError; +} + +void QNmeaPositionInfoSource::setError(QGeoPositionInfoSource::Error positionError) +{ + d->m_positionError = positionError; + emit QGeoPositionInfoSource::error(positionError); +} + +QT_END_NAMESPACE diff --git a/src/positioning/qnmeapositioninfosource.h b/src/positioning/qnmeapositioninfosource.h new file mode 100644 index 0000000..6bbc718 --- /dev/null +++ b/src/positioning/qnmeapositioninfosource.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QNMEAPOSITIONINFOSOURCE_H +#define QNMEAPOSITIONINFOSOURCE_H + +#include + +QT_BEGIN_NAMESPACE + +class QIODevice; + +class QNmeaPositionInfoSourcePrivate; +class Q_POSITIONING_EXPORT QNmeaPositionInfoSource : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + enum UpdateMode { + RealTimeMode = 1, + SimulationMode + }; + + explicit QNmeaPositionInfoSource(UpdateMode updateMode, QObject *parent = nullptr); + ~QNmeaPositionInfoSource(); + + void setUserEquivalentRangeError(double uere); + double userEquivalentRangeError() const; + + UpdateMode updateMode() const; + + void setDevice(QIODevice *source); + QIODevice *device() const; + + void setUpdateInterval(int msec); + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + PositioningMethods supportedPositioningMethods() const; + int minimumUpdateInterval() const; + Error error() const; + + +public Q_SLOTS: + void startUpdates(); + void stopUpdates(); + void requestUpdate(int timeout = 0); + +protected: + virtual bool parsePosInfoFromNmeaData(const char *data, + int size, + QGeoPositionInfo *posInfo, + bool *hasFix); + +private: + Q_DISABLE_COPY(QNmeaPositionInfoSource) + friend class QNmeaPositionInfoSourcePrivate; + QNmeaPositionInfoSourcePrivate *d; + void setError(QGeoPositionInfoSource::Error positionError); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qnmeapositioninfosource_p.h b/src/positioning/qnmeapositioninfosource_p.h new file mode 100644 index 0000000..3d2bbb7 --- /dev/null +++ b/src/positioning/qnmeapositioninfosource_p.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QNMEAPOSITIONINFOSOURCE_P_H +#define QNMEAPOSITIONINFOSOURCE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qnmeapositioninfosource.h" +#include "qgeopositioninfo.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QBasicTimer; +class QTimerEvent; +class QTimer; + +class QNmeaReader; +struct QPendingGeoPositionInfo +{ + QGeoPositionInfo info; + bool hasFix; +}; + + +class QNmeaPositionInfoSourcePrivate : public QObject +{ + Q_OBJECT +public: + QNmeaPositionInfoSourcePrivate(QNmeaPositionInfoSource *parent, QNmeaPositionInfoSource::UpdateMode updateMode); + ~QNmeaPositionInfoSourcePrivate(); + + void startUpdates(); + void stopUpdates(); + void requestUpdate(int msec); + + bool parsePosInfoFromNmeaData(const char *data, + int size, + QGeoPositionInfo *posInfo, + bool *hasFix); + + void notifyNewUpdate(QGeoPositionInfo *update, bool fixStatus); + + QNmeaPositionInfoSource::UpdateMode m_updateMode; + QPointer m_device; + QGeoPositionInfo m_lastUpdate; + bool m_invokedStart; + QGeoPositionInfoSource::Error m_positionError; + double m_userEquivalentRangeError; + +public Q_SLOTS: + void readyRead(); + +protected: + void timerEvent(QTimerEvent *event); + +private Q_SLOTS: + void emitPendingUpdate(); + void sourceDataClosed(); + void updateRequestTimeout(); + +private: + bool openSourceDevice(); + bool initialize(); + void prepareSourceDevice(); + void emitUpdated(const QGeoPositionInfo &update); + + QNmeaPositionInfoSource *m_source; + QNmeaReader *m_nmeaReader; + QGeoPositionInfo m_pendingUpdate; + QDate m_currentDate; + QBasicTimer *m_updateTimer; // the timer used in startUpdates() + QTimer *m_requestTimer; // the timer used in requestUpdate() + qreal m_horizontalAccuracy; + qreal m_verticalAccuracy; + bool m_noUpdateLastInterval; + bool m_updateTimeoutSent; + bool m_connectedReadyRead; +}; + + +class QNmeaReader +{ +public: + explicit QNmeaReader(QNmeaPositionInfoSourcePrivate *sourcePrivate) + : m_proxy(sourcePrivate) {} + virtual ~QNmeaReader() {} + + virtual void readAvailableData() = 0; + +protected: + QNmeaPositionInfoSourcePrivate *m_proxy; +}; + + +class QNmeaRealTimeReader : public QNmeaReader +{ +public: + explicit QNmeaRealTimeReader(QNmeaPositionInfoSourcePrivate *sourcePrivate); + virtual void readAvailableData(); + void notifyNewUpdate(); + + // Data members + QGeoPositionInfo m_update; + QDateTime m_lastPushedTS; + bool m_updateParsed = false; + bool m_hasFix = false; + QTimer m_timer; + int m_pushDelay = -1; +}; + + +class QNmeaSimulatedReader : public QObject, public QNmeaReader +{ + Q_OBJECT +public: + explicit QNmeaSimulatedReader(QNmeaPositionInfoSourcePrivate *sourcePrivate); + ~QNmeaSimulatedReader(); + virtual void readAvailableData(); + +protected: + virtual void timerEvent(QTimerEvent *event); + +private Q_SLOTS: + void simulatePendingUpdate(); + +private: + bool setFirstDateTime(); + void processNextSentence(); + + QQueue m_pendingUpdates; + QByteArray m_nextLine; + int m_currTimerId; + bool m_hasValidDateTime; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/positioning/qpositioningglobal.h b/src/positioning/qpositioningglobal.h new file mode 100644 index 0000000..ea4de29 --- /dev/null +++ b/src/positioning/qpositioningglobal.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPOSITIONINGGLOBAL_H +#define QPOSITIONINGGLOBAL_H + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_POSITIONING_LIB) +# define Q_POSITIONING_EXPORT Q_DECL_EXPORT +# else +# define Q_POSITIONING_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_POSITIONING_EXPORT +#endif + + +QT_END_NAMESPACE + +#endif // QPOSITIONINGGLOBAL_H + diff --git a/src/positioning/qpositioningglobal_p.h b/src/positioning/qpositioningglobal_p.h new file mode 100644 index 0000000..747e450 --- /dev/null +++ b/src/positioning/qpositioningglobal_p.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPOSITIONINGGLOBAL_P_H +#define QPOSITIONINGGLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpositioningglobal.h" + +QT_BEGIN_NAMESPACE + +#define Q_POSITIONING_PRIVATE_EXPORT Q_POSITIONING_EXPORT + +QT_END_NAMESPACE + +#endif // QPOSITIONINGGLOBAL_P_H + diff --git a/src/positioning/qwebmercator.cpp b/src/positioning/qwebmercator.cpp new file mode 100644 index 0000000..0aad410 --- /dev/null +++ b/src/positioning/qwebmercator.cpp @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "qwebmercator_p.h" + +#include "qgeocoordinate.h" + +#include +#include + +#include "qdoublevector2d_p.h" +#include "qdoublevector3d_p.h" + +QT_BEGIN_NAMESPACE + +QDoubleVector2D QWebMercator::coordToMercator(const QGeoCoordinate &coord) +{ + const double pi = M_PI; + + double lon = coord.longitude() / 360.0 + 0.5; + + double lat = coord.latitude(); + lat = 0.5 - (std::log(std::tan((pi / 4.0) + (pi / 2.0) * lat / 180.0)) / pi) / 2.0; + lat = qBound(0.0, lat, 1.0); + + return QDoubleVector2D(lon, lat); +} + +double QWebMercator::realmod(const double a, const double b) +{ + quint64 div = static_cast(a / b); + return a - static_cast(div) * b; +} + +QGeoCoordinate QWebMercator::mercatorToCoord(const QDoubleVector2D &mercator) +{ + const double pi = M_PI; + + double fx = mercator.x(); + double fy = mercator.y(); + + if (fy < 0.0) + fy = 0.0; + else if (fy > 1.0) + fy = 1.0; + + double lat; + + if (fy == 0.0) + lat = 90.0; + else if (fy == 1.0) + lat = -90.0; + else + lat = (180.0 / pi) * (2.0 * std::atan(std::exp(pi * (1.0 - 2.0 * fy))) - (pi / 2.0)); + + double lng; + if (fx >= 0) { + lng = realmod(fx, 1.0); + } else { + lng = realmod(1.0 - realmod(-1.0 * fx, 1.0), 1.0); + } + + lng = lng * 360.0 - 180.0; + + return QGeoCoordinate(lat, lng, 0.0); +} + +QGeoCoordinate QWebMercator::coordinateInterpolation(const QGeoCoordinate &from, const QGeoCoordinate &to, qreal progress) +{ + QDoubleVector2D s = QWebMercator::coordToMercator(from); + QDoubleVector2D e = QWebMercator::coordToMercator(to); + + double x = s.x(); + + if (0.5 < qAbs(e.x() - s.x())) { + // handle dateline crossing + double ex = e.x(); + double sx = s.x(); + if (ex < sx) + sx -= 1.0; + else if (sx < ex) + ex -= 1.0; + + x = (1.0 - progress) * sx + progress * ex; + + if (!qFuzzyIsNull(x) && (x < 0.0)) + x += 1.0; + + } else { + x = (1.0 - progress) * s.x() + progress * e.x(); + } + + double y = (1.0 - progress) * s.y() + progress * e.y(); + + QGeoCoordinate result = QWebMercator::mercatorToCoord(QDoubleVector2D(x, y)); + result.setAltitude((1.0 - progress) * from.altitude() + progress * to.altitude()); + + return result; +} + +QT_END_NAMESPACE diff --git a/src/positioning/qwebmercator_p.h b/src/positioning/qwebmercator_p.h new file mode 100644 index 0000000..109f073 --- /dev/null +++ b/src/positioning/qwebmercator_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QWEBMERCATOR_P_H +#define QWEBMERCATOR_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include "qpositioningglobal_p.h" + +QT_BEGIN_NAMESPACE + +class QGeoCoordinate; +class QDoubleVector2D; + +class Q_POSITIONING_PRIVATE_EXPORT QWebMercator +{ +public: + static QDoubleVector2D coordToMercator(const QGeoCoordinate &coord); + static QGeoCoordinate mercatorToCoord(const QDoubleVector2D &mercator); + static QGeoCoordinate coordinateInterpolation(const QGeoCoordinate &from, const QGeoCoordinate &to, qreal progress); + +private: + static double realmod(const double a, const double b); +}; + +QT_END_NAMESPACE + +#endif // QWEBMERCATOR_P_H diff --git a/src/positioningquick/positioningquick.pro b/src/positioningquick/positioningquick.pro new file mode 100644 index 0000000..75bd68a --- /dev/null +++ b/src/positioningquick/positioningquick.pro @@ -0,0 +1,10 @@ +TARGET = QtPositioningQuick +QT = quick-private positioning-private qml-private core-private +CONFIG += simd optimize_full + +INCLUDEPATH += $$PWD + +SOURCES += $$files(*.cpp) +HEADERS += $$files(*.h) + +load(qt_module) diff --git a/src/positioningquick/qdeclarativeposition.cpp b/src/positioningquick/qdeclarativeposition.cpp new file mode 100644 index 0000000..b63aaab --- /dev/null +++ b/src/positioningquick/qdeclarativeposition.cpp @@ -0,0 +1,474 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qdeclarativeposition_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype Position + //! \instantiates QDeclarativePosition + \inqmlmodule QtPositioning + \since 5.2 + + \brief The Position type holds positional data at a particular point in time, + such as coordinate (longitude, latitude, altitude) and speed. + + The Position type holds values related to geographic location such as + a \l coordinate (longitude, latitude, and altitude), the \l timestamp when + the Position was obtained, the \l speed at that time, and the accuracy of + the data. + + Primarily, it is used in the \l{PositionSource::position}{position} property + of a \l{PositionSource}, as the basic unit of data available from the system + location data source. + + Not all properties of a Position object are necessarily valid or available + (for example latitude and longitude may be valid, but speed update has not been + received or set manually). As a result, corresponding "valid" properties + are available (for example \l{coordinate} and \l{longitudeValid}, \l{latitudeValid} + etc) to discern whether the data is available and valid in this position + update. + + Position objects are read-only and can only be produced by a PositionSource. + + \section2 Example Usage + + See the example given for the \l{PositionSource} type, or the + \l{geoflickr}{GeoFlickr} example application. + + \sa PositionSource, coordinate +*/ + +namespace +{ + +bool equalOrNaN(qreal a, qreal b) +{ + return a == b || (qIsNaN(a) && qIsNaN(b)); +} + +bool exclusiveNaN(qreal a, qreal b) +{ + return qIsNaN(a) != qIsNaN(b); +} + +} + +QDeclarativePosition::QDeclarativePosition(QObject *parent) +: QObject(parent) +{ +} + +QDeclarativePosition::~QDeclarativePosition() +{ +} + +void QDeclarativePosition::setPosition(const QGeoPositionInfo &info) +{ + // timestamp + const QDateTime pTimestamp = m_info.timestamp(); + const QDateTime timestamp = info.timestamp(); + bool emitTimestampChanged = pTimestamp != timestamp; + + // coordinate + const QGeoCoordinate pCoordinate = m_info.coordinate(); + const QGeoCoordinate coordinate = info.coordinate(); + bool emitCoordinateChanged = pCoordinate != coordinate; + bool emitLatitudeValidChanged = exclusiveNaN(pCoordinate.latitude(), coordinate.latitude()); + bool emitLongitudeValidChanged = exclusiveNaN(pCoordinate.longitude(), coordinate.longitude()); + bool emitAltitudeValidChanged = exclusiveNaN(pCoordinate.altitude(), coordinate.altitude()); + + // direction + const qreal pDirection = m_info.attribute(QGeoPositionInfo::Direction); + const qreal direction = info.attribute(QGeoPositionInfo::Direction); + bool emitDirectionChanged = !equalOrNaN(pDirection, direction); + bool emitDirectionValidChanged = exclusiveNaN(pDirection, direction); + + // ground speed + const qreal pSpeed = m_info.attribute(QGeoPositionInfo::GroundSpeed); + const qreal speed = info.attribute(QGeoPositionInfo::GroundSpeed); + bool emitSpeedChanged = !equalOrNaN(pSpeed, speed); + bool emitSpeedValidChanged = exclusiveNaN(pSpeed, speed); + + // vertical speed + const qreal pVerticalSpeed = m_info.attribute(QGeoPositionInfo::VerticalSpeed); + const qreal verticalSpeed = info.attribute(QGeoPositionInfo::VerticalSpeed); + bool emitVerticalSpeedChanged = !equalOrNaN(pVerticalSpeed, verticalSpeed); + bool emitVerticalSpeedValidChanged = exclusiveNaN(pVerticalSpeed, verticalSpeed); + + // magnetic variation + const qreal pMagneticVariation = m_info.attribute(QGeoPositionInfo::MagneticVariation); + const qreal magneticVariation = info.attribute(QGeoPositionInfo::MagneticVariation); + bool emitMagneticVariationChanged = !equalOrNaN(pMagneticVariation, magneticVariation); + bool emitMagneticVariationValidChanged = exclusiveNaN(pMagneticVariation, magneticVariation); + + // horizontal accuracy + const qreal pHorizontalAccuracy = m_info.attribute(QGeoPositionInfo::HorizontalAccuracy); + const qreal horizontalAccuracy = info.attribute(QGeoPositionInfo::HorizontalAccuracy); + bool emitHorizontalAccuracyChanged = !equalOrNaN(pHorizontalAccuracy, horizontalAccuracy); + bool emitHorizontalAccuracyValidChanged = exclusiveNaN(pHorizontalAccuracy, horizontalAccuracy); + + // vertical accuracy + const qreal pVerticalAccuracy = m_info.attribute(QGeoPositionInfo::VerticalAccuracy); + const qreal verticalAccuracy = info.attribute(QGeoPositionInfo::VerticalAccuracy); + bool emitVerticalAccuracyChanged = !equalOrNaN(pVerticalAccuracy, verticalAccuracy); + bool emitVerticalAccuracyValidChanged = exclusiveNaN(pVerticalAccuracy, verticalAccuracy); + + m_info = info; + + if (emitTimestampChanged) + emit timestampChanged(); + if (emitCoordinateChanged) + emit coordinateChanged(); + if (emitLatitudeValidChanged) + emit latitudeValidChanged(); + if (emitLongitudeValidChanged) + emit longitudeValidChanged(); + if (emitAltitudeValidChanged) + emit altitudeValidChanged(); + if (emitDirectionChanged) + emit directionChanged(); + if (emitDirectionValidChanged) + emit directionValidChanged(); + if (emitSpeedChanged) + emit speedChanged(); + if (emitSpeedValidChanged) + emit speedValidChanged(); + if (emitVerticalSpeedChanged) + emit verticalSpeedChanged(); + if (emitVerticalSpeedValidChanged) + emit verticalSpeedValidChanged(); + if (emitHorizontalAccuracyChanged) + emit horizontalAccuracyChanged(); + if (emitHorizontalAccuracyValidChanged) + emit horizontalAccuracyValidChanged(); + if (emitVerticalAccuracyChanged) + emit verticalAccuracyChanged(); + if (emitVerticalAccuracyValidChanged) + emit verticalAccuracyValidChanged(); + if (emitMagneticVariationChanged) + emit magneticVariationChanged(); + if (emitMagneticVariationValidChanged) + emit magneticVariationValidChanged(); +} + +const QGeoPositionInfo &QDeclarativePosition::position() const +{ + return m_info; +} + +/*! + \qmlproperty coordinate Position::coordinate + + This property holds the latitude, longitude, and altitude value of the Position. + + It is a read-only property. + + \sa longitudeValid, latitudeValid, altitudeValid +*/ +QGeoCoordinate QDeclarativePosition::coordinate() +{ + return m_info.coordinate(); +} + +/*! + \qmlproperty bool Position::latitudeValid + + This property is true if coordinate's latitude has been set + (to indicate whether that data has been received or not, as every update + does not necessarily contain all data). + + \sa coordinate +*/ +bool QDeclarativePosition::isLatitudeValid() const +{ + return !qIsNaN(m_info.coordinate().latitude()); +} + + +/*! + \qmlproperty bool Position::longitudeValid + + This property is true if coordinate's longitude has been set + (to indicate whether that data has been received or not, as every update + does not necessarily contain all data). + + \sa coordinate +*/ +bool QDeclarativePosition::isLongitudeValid() const +{ + return !qIsNaN(m_info.coordinate().longitude()); +} + + +/*! + \qmlproperty bool Position::speedValid + + This property is true if \l speed has been set + (to indicate whether that data has been received or not, as every update + does not necessarily contain all data). + + \sa speed +*/ +bool QDeclarativePosition::isSpeedValid() const +{ + return !qIsNaN(m_info.attribute(QGeoPositionInfo::GroundSpeed)); +} + +/*! + \qmlproperty bool Position::altitudeValid + + This property is true if coordinate's altitude has been set + (to indicate whether that data has been received or not, as every update + does not necessarily contain all data). + + \sa coordinate +*/ +bool QDeclarativePosition::isAltitudeValid() const +{ + return !qIsNaN(m_info.coordinate().altitude()); +} + +/*! + \qmlproperty double Position::speed + + This property holds the value of speed (groundspeed, meters / second). + + It is a read-only property. + + \sa speedValid, coordinate +*/ +double QDeclarativePosition::speed() const +{ + return m_info.attribute(QGeoPositionInfo::GroundSpeed); +} + +/*! + \qmlproperty real Position::horizontalAccuracy + + This property holds the horizontal accuracy of the coordinate (in meters). + + \sa horizontalAccuracyValid, coordinate +*/ +void QDeclarativePosition::setHorizontalAccuracy(qreal horizontalAccuracy) +{ + const qreal pHorizontalAccuracy = m_info.attribute(QGeoPositionInfo::HorizontalAccuracy); + + if (equalOrNaN(pHorizontalAccuracy, horizontalAccuracy)) + return; + + bool validChanged = exclusiveNaN(pHorizontalAccuracy, horizontalAccuracy); + + m_info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, horizontalAccuracy); + emit horizontalAccuracyChanged(); + if (validChanged) + emit horizontalAccuracyValidChanged(); +} + +qreal QDeclarativePosition::horizontalAccuracy() const +{ + return m_info.attribute(QGeoPositionInfo::HorizontalAccuracy); +} + +/*! + \qmlproperty bool Position::horizontalAccuracyValid + + This property is true if \l horizontalAccuracy has been set + (to indicate whether that data has been received or not, as every update + does not necessarily contain all data). + + \sa horizontalAccuracy +*/ +bool QDeclarativePosition::isHorizontalAccuracyValid() const +{ + return !qIsNaN(m_info.attribute(QGeoPositionInfo::HorizontalAccuracy)); +} + +/*! + \qmlproperty real Position::verticalAccuracy + + This property holds the vertical accuracy of the coordinate (in meters). + + \sa verticalAccuracyValid, coordinate +*/ +void QDeclarativePosition::setVerticalAccuracy(qreal verticalAccuracy) +{ + const qreal pVerticalAccuracy = m_info.attribute(QGeoPositionInfo::VerticalAccuracy); + + if (equalOrNaN(pVerticalAccuracy, verticalAccuracy)) + return; + + bool validChanged = exclusiveNaN(pVerticalAccuracy, verticalAccuracy); + + m_info.setAttribute(QGeoPositionInfo::VerticalAccuracy, verticalAccuracy); + emit verticalAccuracyChanged(); + if (validChanged) + emit verticalAccuracyValidChanged(); +} + +qreal QDeclarativePosition::verticalAccuracy() const +{ + return m_info.attribute(QGeoPositionInfo::VerticalAccuracy); +} + +/*! + \qmlproperty bool Position::verticalAccuracyValid + + This property is true if \l verticalAccuracy has been set + (to indicate whether that data has been received or not, as every update + does not necessarily contain all data). + + \sa verticalAccuracy +*/ +bool QDeclarativePosition::isVerticalAccuracyValid() const +{ + return !qIsNaN(m_info.attribute(QGeoPositionInfo::VerticalAccuracy)); +} + +/*! + \qmlproperty date Position::timestamp + + This property holds the timestamp when this position + was received. If the property has not been set, it is invalid. + + It is a read-only property. +*/ +QDateTime QDeclarativePosition::timestamp() const +{ + return m_info.timestamp(); +} + +/*! + \qmlproperty bool Position::directionValid + \since Qt Positioning 5.3 + + This property is true if \l direction has been set (to indicate whether that data has been + received or not, as every update does not necessarily contain all data). + + \sa direction +*/ +bool QDeclarativePosition::isDirectionValid() const +{ + return !qIsNaN(m_info.attribute(QGeoPositionInfo::Direction)); +} + +/*! + \qmlproperty double Position::direction + \since Qt Positioning 5.3 + + This property holds the value of the direction of travel in degrees from true north. + + It is a read-only property. + + \sa directionValid +*/ +double QDeclarativePosition::direction() const +{ + return m_info.attribute(QGeoPositionInfo::Direction); +} + +/*! + \qmlproperty bool Position::verticalSpeedValid + \since Qt Positioning 5.3 + + This property is true if \l verticalSpeed has been set (to indicate whether that data has been + received or not, as every update does not necessarily contain all data). + + \sa verticalSpeed +*/ +bool QDeclarativePosition::isVerticalSpeedValid() const +{ + return !qIsNaN(m_info.attribute(QGeoPositionInfo::VerticalSpeed)); +} + +/*! + \qmlproperty double Position::verticalSpeed + \since Qt Positioning 5.3 + + This property holds the value of the vertical speed in meters per second. + + It is a read-only property. + + \sa verticalSpeedValid +*/ +double QDeclarativePosition::verticalSpeed() const +{ + return m_info.attribute(QGeoPositionInfo::VerticalSpeed); +} + +/*! + \qmlproperty bool Position::magneticVariationValid + \since Qt Positioning 5.4 + + This property is true if \l magneticVariation has been set (to indicate whether that data has been + received or not, as every update does not necessarily contain all data). + + \sa magneticVariation +*/ +bool QDeclarativePosition::isMagneticVariationValid() const +{ + return !qIsNaN(m_info.attribute(QGeoPositionInfo::MagneticVariation)); +} + +/*! + \qmlproperty double Position::magneticVariation + \since Qt Positioning 5.4 + + This property holds the angle between the horizontal component of the + magnetic field and true north, in degrees. Also known as magnetic + declination. A positive value indicates a clockwise direction from + true north and a negative value indicates a counter-clockwise direction. + + It is a read-only property. + + \sa magneticVariationValid +*/ +double QDeclarativePosition::magneticVariation() const +{ + return m_info.attribute(QGeoPositionInfo::MagneticVariation); +} + +QT_END_NAMESPACE diff --git a/src/positioningquick/qdeclarativeposition_p.h b/src/positioningquick/qdeclarativeposition_p.h new file mode 100644 index 0000000..141c37b --- /dev/null +++ b/src/positioningquick/qdeclarativeposition_p.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +***************************************************************************/ + +#ifndef QDECLARATIVEPOSITION_H +#define QDECLARATIVEPOSITION_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class Q_POSITIONINGQUICK_PRIVATE_EXPORT QDeclarativePosition : public QObject +{ + Q_OBJECT + + Q_PROPERTY(bool latitudeValid READ isLatitudeValid NOTIFY latitudeValidChanged) + Q_PROPERTY(bool longitudeValid READ isLongitudeValid NOTIFY longitudeValidChanged) + Q_PROPERTY(bool altitudeValid READ isAltitudeValid NOTIFY altitudeValidChanged) + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate NOTIFY coordinateChanged) + Q_PROPERTY(QDateTime timestamp READ timestamp NOTIFY timestampChanged) + Q_PROPERTY(double speed READ speed NOTIFY speedChanged) + Q_PROPERTY(bool speedValid READ isSpeedValid NOTIFY speedValidChanged) + Q_PROPERTY(qreal horizontalAccuracy READ horizontalAccuracy WRITE setHorizontalAccuracy NOTIFY horizontalAccuracyChanged) + Q_PROPERTY(qreal verticalAccuracy READ verticalAccuracy WRITE setVerticalAccuracy NOTIFY verticalAccuracyChanged) + Q_PROPERTY(bool horizontalAccuracyValid READ isHorizontalAccuracyValid NOTIFY horizontalAccuracyValidChanged) + Q_PROPERTY(bool verticalAccuracyValid READ isVerticalAccuracyValid NOTIFY verticalAccuracyValidChanged) + + Q_PROPERTY(bool directionValid READ isDirectionValid NOTIFY directionValidChanged REVISION 1) + Q_PROPERTY(double direction READ direction NOTIFY directionChanged REVISION 1) + Q_PROPERTY(bool verticalSpeedValid READ isVerticalSpeedValid NOTIFY verticalSpeedValidChanged REVISION 1) + Q_PROPERTY(double verticalSpeed READ verticalSpeed NOTIFY verticalSpeedChanged REVISION 1) + + Q_PROPERTY(double magneticVariation READ magneticVariation NOTIFY magneticVariationChanged REVISION 2) + Q_PROPERTY(bool magneticVariationValid READ isMagneticVariationValid NOTIFY magneticVariationChanged REVISION 2) + +public: + explicit QDeclarativePosition(QObject *parent = 0); + ~QDeclarativePosition(); + + bool isLatitudeValid() const; + bool isLongitudeValid() const; + bool isAltitudeValid() const; + QDateTime timestamp() const; + double speed() const; + bool isSpeedValid() const; + QGeoCoordinate coordinate(); + bool isHorizontalAccuracyValid() const; + qreal horizontalAccuracy() const; + void setHorizontalAccuracy(qreal horizontalAccuracy); + bool isVerticalAccuracyValid() const; + qreal verticalAccuracy() const; + void setVerticalAccuracy(qreal verticalAccuracy); + + bool isDirectionValid() const; + double direction() const; + void setDirection(double direction); + + bool isVerticalSpeedValid() const; + double verticalSpeed() const; + void setVerticalSpeed(double speed); + + bool isMagneticVariationValid() const; + double magneticVariation() const; + + void setPosition(const QGeoPositionInfo &info); + const QGeoPositionInfo &position() const; + +Q_SIGNALS: + void latitudeValidChanged(); + void longitudeValidChanged(); + void altitudeValidChanged(); + void timestampChanged(); + void speedChanged(); + void speedValidChanged(); + void coordinateChanged(); + void horizontalAccuracyChanged(); + void horizontalAccuracyValidChanged(); + void verticalAccuracyChanged(); + void verticalAccuracyValidChanged(); + + Q_REVISION(1) void directionValidChanged(); + Q_REVISION(1) void directionChanged(); + Q_REVISION(1) void verticalSpeedValidChanged(); + Q_REVISION(1) void verticalSpeedChanged(); + + Q_REVISION(2) void magneticVariationChanged(); + Q_REVISION(2) void magneticVariationValidChanged(); + +private: + QGeoPositionInfo m_info; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePosition) + +#endif diff --git a/src/positioningquick/qdeclarativepositionsource.cpp b/src/positioningquick/qdeclarativepositionsource.cpp new file mode 100644 index 0000000..ec462f2 --- /dev/null +++ b/src/positioningquick/qdeclarativepositionsource.cpp @@ -0,0 +1,768 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepositionsource_p.h" +#include "qdeclarativeposition_p.h" + +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +/*! + \qmltype PositionSource + //! \instantiates QDeclarativePositionSource + \inqmlmodule QtPositioning + \since 5.2 + + \brief The PositionSource type provides the device's current position. + + The PositionSource type provides information about the user device's + current position. The position is available as a \l{Position} type, which + contains all the standard parameters typically available from GPS and other + similar systems, including longitude, latitude, speed and accuracy details. + + As different position sources are available on different platforms and + devices, these are categorized by their basic type (Satellite, NonSatellite, + and AllPositioningMethods). The available methods for the current platform + can be enumerated in the \l{supportedPositioningMethods} property. + + To indicate which methods are suitable for your application, set the + \l{preferredPositioningMethods} property. If the preferred methods are not + available, the default source of location data for the platform will be + chosen instead. If no default source is available (because none are installed + for the runtime platform, or because it is disabled), the \l{valid} property + will be set to false. + + The \l updateInterval property can then be used to indicate how often your + application wishes to receive position updates. The \l{start}(), + \l{stop}() and \l{update}() methods can be used to control the operation + of the PositionSource, as well as the \l{active} property, which when set + is equivalent to calling \l{start}() or \l{stop}(). + + When the PositionSource is active, position updates can be retrieved + either by simply using the \l{position} property in a binding (as the + value of another item's property), or by providing an implementation of + the \c {onPositionChanged} signal-handler. + + \section2 Example Usage + + The following example shows a simple PositionSource used to receive + updates every second and print the longitude and latitude out to + the console. + + \code + PositionSource { + id: src + updateInterval: 1000 + active: true + + onPositionChanged: { + var coord = src.position.coordinate; + console.log("Coordinate:", coord.longitude, coord.latitude); + } + } + \endcode + + The \l{geoflickr}{GeoFlickr} example application shows how to use + a PositionSource in your application to retrieve local data for users + from a REST web service. + + \sa {QtPositioning::Position}, {QGeoPositionInfoSource} + +*/ + +/*! + \qmlsignal PositionSource::updateTimeout() + + If \l update() was called, this signal is emitted if the current position could not be + retrieved within a certain amount of time. + + If \l start() was called, this signal is emitted if the position engine determines that + it is not able to provide further regular updates. + + \since Qt Positioning 5.5 + + \sa QGeoPositionInfoSource::updateTimeout() +*/ + + +QDeclarativePositionSource::QDeclarativePositionSource() +: m_positionSource(0), m_preferredPositioningMethods(NoPositioningMethods), m_nmeaFile(0), + m_nmeaSocket(0), m_active(false), m_singleUpdate(false), m_updateInterval(0), + m_sourceError(NoError) +{ +} + +QDeclarativePositionSource::~QDeclarativePositionSource() +{ + delete m_nmeaFile; + delete m_nmeaSocket; + delete m_positionSource; +} + + +/*! + \qmlproperty string PositionSource::name + + This property holds the unique internal name for the plugin currently + providing position information. + + Setting the property causes the PositionSource to use a particular positioning provider. If + the PositionSource is active at the time that the name property is changed, it will become + inactive. If the specified positioning provider cannot be loaded the position source will + become invalid. + + Changing the name property may cause the \l {updateInterval}, \l {supportedPositioningMethods} + and \l {preferredPositioningMethods} properties to change as well. +*/ + + +QString QDeclarativePositionSource::name() const +{ + if (m_positionSource) + return m_positionSource->sourceName(); + else + return QString(); +} + +void QDeclarativePositionSource::setName(const QString &newName) +{ + if (m_positionSource && m_positionSource->sourceName() == newName) + return; + + const QString previousName = name(); + int previousUpdateInterval = updateInterval(); + PositioningMethods previousPositioningMethods = supportedPositioningMethods(); + PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); + + delete m_positionSource; + if (newName.isEmpty()) + m_positionSource = QGeoPositionInfoSource::createDefaultSource(this); + else + m_positionSource = QGeoPositionInfoSource::createSource(newName, this); + + if (m_positionSource) { + connect(m_positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdateReceived(QGeoPositionInfo))); + connect(m_positionSource, SIGNAL(error(QGeoPositionInfoSource::Error)), + this, SLOT(sourceErrorReceived(QGeoPositionInfoSource::Error))); + connect(m_positionSource, SIGNAL(updateTimeout()), + this, SLOT(updateTimeoutReceived())); + + m_positionSource->setUpdateInterval(m_updateInterval); + m_positionSource->setPreferredPositioningMethods( + static_cast(int(m_preferredPositioningMethods))); + + setPosition(m_positionSource->lastKnownPosition()); + } + + if (previousUpdateInterval != updateInterval()) + emit updateIntervalChanged(); + + if (previousPreferredPositioningMethods != preferredPositioningMethods()) + emit preferredPositioningMethodsChanged(); + + if (previousPositioningMethods != supportedPositioningMethods()) + emit supportedPositioningMethodsChanged(); + + emit validityChanged(); + + if (m_active) { + m_active = false; + emit activeChanged(); + } + + if (previousName != name()) + emit nameChanged(); +} + +/*! + \qmlproperty bool PositionSource::valid + + This property is true if the PositionSource object has acquired a valid + backend plugin to provide data. If false, other methods on the PositionSource + will have no effect. + + Applications should check this property to determine whether positioning is + available and enabled on the runtime platform, and react accordingly. +*/ +bool QDeclarativePositionSource::isValid() const +{ + return (m_positionSource != 0); +} + +/*! + \internal +*/ +void QDeclarativePositionSource::setNmeaSource(const QUrl &nmeaSource) +{ + if (nmeaSource.scheme() == QLatin1String("socket")) { + if (m_nmeaSocket + && nmeaSource.host() == m_nmeaSocket->peerName() + && nmeaSource.port() == m_nmeaSocket->peerPort()) { + return; + } + + delete m_nmeaSocket; + m_nmeaSocket = new QTcpSocket(); + + connect(m_nmeaSocket, static_cast (&QAbstractSocket::error), + this, &QDeclarativePositionSource::socketError); + connect(m_nmeaSocket, &QTcpSocket::connected, + this, &QDeclarativePositionSource::socketConnected); + + m_nmeaSocket->connectToHost(nmeaSource.host(), nmeaSource.port(), QTcpSocket::ReadOnly); + } else { + // Strip the filename. This is clumsy but the file may be prefixed in several + // ways: "file:///", "qrc:///", "/", "" in platform dependent manner. + QString localFileName = nmeaSource.toString(); + if (!QFile::exists(localFileName)) { + if (localFileName.startsWith(QStringLiteral("qrc:///"))) { + localFileName.remove(0, 7); + } else if (localFileName.startsWith(QStringLiteral("file:///"))) { + localFileName.remove(0, 7); + } else if (localFileName.startsWith(QStringLiteral("qrc:/"))) { + localFileName.remove(0, 5); + } + if (!QFile::exists(localFileName) && localFileName.startsWith('/')) { + localFileName.remove(0,1); + } + } + if (m_nmeaFileName == localFileName) + return; + m_nmeaFileName = localFileName; + + PositioningMethods previousPositioningMethods = supportedPositioningMethods(); + + // The current position source needs to be deleted + // because QNmeaPositionInfoSource can be bound only to a one file. + delete m_nmeaSocket; + m_nmeaSocket = 0; + delete m_positionSource; + m_positionSource = 0; + setPosition(QGeoPositionInfo()); + // Create the NMEA source based on the given data. QML has automatically set QUrl + // type to point to correct path. If the file is not found, check if the file actually + // was an embedded resource file. + delete m_nmeaFile; + m_nmeaFile = new QFile(localFileName); + if (!m_nmeaFile->exists()) { + localFileName.prepend(':'); + m_nmeaFile->setFileName(localFileName); + } + if (m_nmeaFile->exists()) { +#ifdef QDECLARATIVE_POSITION_DEBUG + qDebug() << "QDeclarativePositionSource NMEA File was found: " << localFileName; +#endif + m_positionSource = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode); + (qobject_cast(m_positionSource))->setUserEquivalentRangeError(2.5); // it is internally multiplied by 2 in qlocationutils_readGga + (qobject_cast(m_positionSource))->setDevice(m_nmeaFile); + connect(m_positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdateReceived(QGeoPositionInfo))); + connect(m_positionSource, SIGNAL(error(QGeoPositionInfoSource::Error)), + this, SLOT(sourceErrorReceived(QGeoPositionInfoSource::Error))); + connect(m_positionSource, SIGNAL(updateTimeout()), + this, SLOT(updateTimeoutReceived())); + + setPosition(m_positionSource->lastKnownPosition()); + if (m_active && !m_singleUpdate) { + // Keep on updating even though source changed + QTimer::singleShot(0, this, SLOT(start())); + } + } else { + qmlWarning(this) << QStringLiteral("Nmea file not found") << localFileName; +#ifdef QDECLARATIVE_POSITION_DEBUG + qDebug() << "QDeclarativePositionSource NMEA File was not found: " << localFileName; +#endif + if (m_active) { + m_active = false; + m_singleUpdate = false; + emit activeChanged(); + } + } + + if (previousPositioningMethods != supportedPositioningMethods()) + emit supportedPositioningMethodsChanged(); + } + + m_nmeaSource = nmeaSource; + emit nmeaSourceChanged(); +} + +/*! + \internal +*/ +void QDeclarativePositionSource::socketConnected() +{ +#ifdef QDECLARATIVE_POSITION_DEBUG + qDebug() << "Socket connected: " << m_nmeaSocket->peerName(); +#endif + PositioningMethods previousPositioningMethods = supportedPositioningMethods(); + + // The current position source needs to be deleted + // because QNmeaPositionInfoSource can be bound only to a one file. + delete m_nmeaFile; + m_nmeaFile = 0; + delete m_positionSource; + + m_positionSource = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::RealTimeMode); + (qobject_cast(m_positionSource))->setDevice(m_nmeaSocket); + + connect(m_positionSource, &QNmeaPositionInfoSource::positionUpdated, + this, &QDeclarativePositionSource::positionUpdateReceived); + connect(m_positionSource, SIGNAL(error(QGeoPositionInfoSource::Error)), + this, SLOT(sourceErrorReceived(QGeoPositionInfoSource::Error))); + connect(m_positionSource, SIGNAL(updateTimeout()), + this, SLOT(updateTimeoutReceived())); + + setPosition(m_positionSource->lastKnownPosition()); + + if (m_active && !m_singleUpdate) { + // Keep on updating even though source changed + QTimer::singleShot(0, this, SLOT(start())); + } + + if (previousPositioningMethods != supportedPositioningMethods()) + emit supportedPositioningMethodsChanged(); +} + +/*! + \internal +*/ +void QDeclarativePositionSource::socketError(QAbstractSocket::SocketError error) +{ + delete m_nmeaSocket; + m_nmeaSocket = 0; + + switch (error) { + case QAbstractSocket::UnknownSocketError: + m_sourceError = QDeclarativePositionSource::UnknownSourceError; + break; + case QAbstractSocket::SocketAccessError: + m_sourceError = QDeclarativePositionSource::AccessError; + break; + case QAbstractSocket::RemoteHostClosedError: + m_sourceError = QDeclarativePositionSource::ClosedError; + break; + default: + qWarning() << "Connection failed! QAbstractSocket::SocketError" << error; + m_sourceError = QDeclarativePositionSource::SocketError; + break; + } + + emit sourceErrorChanged(); +} + + +void QDeclarativePositionSource::updateTimeoutReceived() +{ + if (!m_active) + return; + + if (m_singleUpdate) { + m_singleUpdate = false; + + // only singleUpdate based timeouts change activity + // continuous updates may resume again (see QGeoPositionInfoSource::startUpdates()) + m_active = false; + emit activeChanged(); + } + + emit updateTimeout(); +} + +void QDeclarativePositionSource::setPosition(const QGeoPositionInfo &pi) +{ + m_position.setPosition(pi); + emit positionChanged(); +} + +/*! + \internal +*/ +void QDeclarativePositionSource::setUpdateInterval(int updateInterval) +{ + if (m_positionSource) { + int previousUpdateInterval = m_positionSource->updateInterval(); + + m_updateInterval = updateInterval; + + if (previousUpdateInterval != updateInterval) { + m_positionSource->setUpdateInterval(updateInterval); + if (previousUpdateInterval != m_positionSource->updateInterval()) + emit updateIntervalChanged(); + } + } else { + if (m_updateInterval != updateInterval) { + m_updateInterval = updateInterval; + emit updateIntervalChanged(); + } + } +} + +/*! + \qmlproperty url PositionSource::nmeaSource + + This property holds the source for NMEA (National Marine Electronics Association) + position-specification data (file). One purpose of this property is to be of + development convenience. + + Setting this property will override any other position source. Currently only + files local to the .qml -file are supported. The NMEA source is created in simulation mode, + meaning that the data and time information in the NMEA source data is used to provide + positional updates at the rate at which the data was originally recorded. + + If nmeaSource has been set for a PositionSource object, there is no way to revert + back to non-file sources. +*/ + +QUrl QDeclarativePositionSource::nmeaSource() const +{ + return m_nmeaSource; +} + +/*! + \qmlproperty int PositionSource::updateInterval + + This property holds the desired interval between updates (milliseconds). + + \sa {QGeoPositionInfoSource::updateInterval()} +*/ + +int QDeclarativePositionSource::updateInterval() const +{ + if (!m_positionSource) + return m_updateInterval; + + return m_positionSource->updateInterval(); +} + +/*! + \qmlproperty enumeration PositionSource::supportedPositioningMethods + + This property holds the supported positioning methods of the + current source. + + \list + \li PositionSource.NoPositioningMethods - No positioning methods supported (no source). + \li PositionSource.SatellitePositioningMethods - Satellite-based positioning methods such as GPS are supported. + \li PositionSource.NonSatellitePositioningMethods - Non-satellite-based methods are supported. + \li PositionSource.AllPositioningMethods - Both satellite-based and non-satellite positioning methods are supported. + \endlist + +*/ + +QDeclarativePositionSource::PositioningMethods QDeclarativePositionSource::supportedPositioningMethods() const +{ + if (m_positionSource) { + return static_cast( + int(m_positionSource->supportedPositioningMethods())); + } + return QDeclarativePositionSource::NoPositioningMethods; +} + +/*! + \qmlproperty enumeration PositionSource::preferredPositioningMethods + + This property holds the preferred positioning methods of the + current source. + + \list + \li PositionSource.NoPositioningMethods - No positioning method is preferred. + \li PositionSource.SatellitePositioningMethods - Satellite-based positioning methods such as GPS should be preferred. + \li PositionSource.NonSatellitePositioningMethods - Non-satellite-based methods should be preferred. + \li PositionSource.AllPositioningMethods - Any positioning methods are acceptable. + \endlist + +*/ + +void QDeclarativePositionSource::setPreferredPositioningMethods(PositioningMethods methods) +{ + if (m_positionSource) { + PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); + + m_preferredPositioningMethods = methods; + + if (previousPreferredPositioningMethods != methods) { + m_positionSource->setPreferredPositioningMethods( + static_cast(int(methods))); + if (previousPreferredPositioningMethods != m_positionSource->preferredPositioningMethods()) + emit preferredPositioningMethodsChanged(); + } + } else { + if (m_preferredPositioningMethods != methods) { + m_preferredPositioningMethods = methods; + emit preferredPositioningMethodsChanged(); + } + } +} + +QDeclarativePositionSource::PositioningMethods QDeclarativePositionSource::preferredPositioningMethods() const +{ + if (m_positionSource) { + return static_cast( + int(m_positionSource->preferredPositioningMethods())); + } + return m_preferredPositioningMethods; +} + +/*! + \qmlmethod PositionSource::start() + + Requests updates from the location source. + Uses \l updateInterval if set, default interval otherwise. + If there is no source available, this method has no effect. + + \sa stop, update, active +*/ + +void QDeclarativePositionSource::start() +{ + if (m_positionSource) + m_positionSource->startUpdates(); + + if (!m_active) { + m_active = true; + emit activeChanged(); + } +} + +/*! + \qmlmethod PositionSource::update() + + A convenience method to request single update from the location source. + If there is no source available, this method has no effect. + + If the position source is not active, it will be activated for as + long as it takes to receive an update, or until the request times + out. The request timeout period is source-specific. + + \sa start, stop, active +*/ + +void QDeclarativePositionSource::update() +{ + if (m_positionSource) { + if (!m_active) { + m_active = true; + m_singleUpdate = true; + emit activeChanged(); + } + // Use default timeout value. Set active before calling the + // update request because on some platforms there may + // be results immediately. + m_positionSource->requestUpdate(); + } +} + +/*! + \qmlmethod PositionSource::stop() + + Stops updates from the location source. + If there is no source available or it is not active, + this method has no effect. + + \sa start, update, active +*/ + +void QDeclarativePositionSource::stop() +{ + if (m_positionSource) { + m_positionSource->stopUpdates(); + if (m_active) { + m_active = false; + emit activeChanged(); + } + } +} + +/*! + \qmlproperty bool PositionSource::active + + This property indicates whether the position source is active. + Setting this property to false equals calling \l stop, and + setting this property true equals calling \l start. + + \sa start, stop, update +*/ +void QDeclarativePositionSource::setActive(bool active) +{ + if (active == m_active) + return; + + if (active) + QTimer::singleShot(0, this, SLOT(start())); // delay ensures all properties have been set + else + stop(); +} + +bool QDeclarativePositionSource::isActive() const +{ + return m_active; +} + +/*! + \qmlproperty Position PositionSource::position + + This property holds the last known positional data. + It is a read-only property. + + The Position type has different positional member variables, + whose validity can be checked with appropriate validity functions + (for example sometimes an update does not have speed or altitude data). + + However, whenever a \c {positionChanged} signal has been received, at least + position::coordinate::latitude, position::coordinate::longitude, and position::timestamp can + be assumed to be valid. + + \sa start, stop, update +*/ + +QDeclarativePosition *QDeclarativePositionSource::position() +{ + return &m_position; +} + +void QDeclarativePositionSource::positionUpdateReceived(const QGeoPositionInfo &update) +{ + setPosition(update); + + if (m_singleUpdate && m_active) { + m_active = false; + m_singleUpdate = false; + emit activeChanged(); + } +} + + +/*! + \qmlproperty enumeration PositionSource::sourceError + + This property holds the error which last occurred with the PositionSource. + + \list + \li PositionSource.AccessError - The connection setup to the remote positioning backend failed because the + application lacked the required privileges. + \li PositionSource.ClosedError - The positioning backend closed the connection, which happens for example in case + the user is switching location services to off. As soon as the location service is re-enabled + regular updates will resume. + \li PositionSource.NoError - No error has occurred. + \li PositionSource.UnknownSourceError - An unidentified error occurred. + \li PositionSource.SocketError - An error occurred while connecting to an nmea source using a socket. + \endlist + +*/ + +QDeclarativePositionSource::SourceError QDeclarativePositionSource::sourceError() const +{ + return m_sourceError; +} + +QGeoPositionInfoSource *QDeclarativePositionSource::positionSource() const +{ + return m_positionSource; +} + +void QDeclarativePositionSource::componentComplete() +{ + if (!m_positionSource) { + int previousUpdateInterval = updateInterval(); + PositioningMethods previousPositioningMethods = supportedPositioningMethods(); + PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods(); + + m_positionSource = QGeoPositionInfoSource::createDefaultSource(this); + if (m_positionSource) { + connect(m_positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdateReceived(QGeoPositionInfo))); + connect(m_positionSource, SIGNAL(error(QGeoPositionInfoSource::Error)), + this, SLOT(sourceErrorReceived(QGeoPositionInfoSource::Error))); + connect(m_positionSource, SIGNAL(updateTimeout()), + this, SLOT(updateTimeoutReceived())); + + m_positionSource->setUpdateInterval(m_updateInterval); + m_positionSource->setPreferredPositioningMethods( + static_cast(int(m_preferredPositioningMethods))); + + setPosition(m_positionSource->lastKnownPosition()); + + if (m_active) + QTimer::singleShot(0, this, SLOT(start())); // delay ensures all properties have been set + } else if (m_active) { + m_active = false; + emit activeChanged(); + } + + if (previousUpdateInterval != updateInterval()) + emit updateIntervalChanged(); + + if (previousPreferredPositioningMethods != preferredPositioningMethods()) + emit preferredPositioningMethodsChanged(); + + if (previousPositioningMethods != supportedPositioningMethods()) + emit supportedPositioningMethodsChanged(); + + emit validityChanged(); + emit nameChanged(); + } +} + +/*! + \internal +*/ +void QDeclarativePositionSource::sourceErrorReceived(const QGeoPositionInfoSource::Error error) +{ + if (error == QGeoPositionInfoSource::AccessError) + m_sourceError = QDeclarativePositionSource::AccessError; + else if (error == QGeoPositionInfoSource::ClosedError) + m_sourceError = QDeclarativePositionSource::ClosedError; + else if (error == QGeoPositionInfoSource::NoError) + return; //nothing to do + else + m_sourceError = QDeclarativePositionSource::UnknownSourceError; + + emit sourceErrorChanged(); +} + +QT_END_NAMESPACE diff --git a/src/positioningquick/qdeclarativepositionsource_p.h b/src/positioningquick/qdeclarativepositionsource_p.h new file mode 100644 index 0000000..bb9b618 --- /dev/null +++ b/src/positioningquick/qdeclarativepositionsource_p.h @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +***************************************************************************/ + +#ifndef QDECLARATIVEPOSITIONSOURCE_H +#define QDECLARATIVEPOSITIONSOURCE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qdeclarativeposition_p.h" + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QFile; +class QTcpSocket; + +class Q_POSITIONINGQUICK_PRIVATE_EXPORT QDeclarativePositionSource : public QObject, public QQmlParserStatus +{ + Q_OBJECT + + Q_PROPERTY(QDeclarativePosition *position READ position NOTIFY positionChanged) + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(bool valid READ isValid NOTIFY validityChanged) + Q_PROPERTY(QUrl nmeaSource READ nmeaSource WRITE setNmeaSource NOTIFY nmeaSourceChanged) + Q_PROPERTY(int updateInterval READ updateInterval WRITE setUpdateInterval NOTIFY updateIntervalChanged) + Q_PROPERTY(PositioningMethods supportedPositioningMethods READ supportedPositioningMethods NOTIFY supportedPositioningMethodsChanged) + Q_PROPERTY(PositioningMethods preferredPositioningMethods READ preferredPositioningMethods WRITE setPreferredPositioningMethods NOTIFY preferredPositioningMethodsChanged) + Q_PROPERTY(SourceError sourceError READ sourceError NOTIFY sourceErrorChanged) + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + Q_ENUMS(PositioningMethod) + + Q_INTERFACES(QQmlParserStatus) + +public: + enum PositioningMethod { + NoPositioningMethods = QGeoPositionInfoSource::NoPositioningMethods, + SatellitePositioningMethods = QGeoPositionInfoSource::SatellitePositioningMethods, + NonSatellitePositioningMethods = QGeoPositionInfoSource::NonSatellitePositioningMethods, + AllPositioningMethods = QGeoPositionInfoSource::AllPositioningMethods + }; + + Q_DECLARE_FLAGS(PositioningMethods, PositioningMethod) + Q_FLAGS(PositioningMethods) + + enum SourceError { + AccessError = QGeoPositionInfoSource::AccessError, + ClosedError = QGeoPositionInfoSource::ClosedError, + UnknownSourceError = QGeoPositionInfoSource::UnknownSourceError, + NoError = QGeoPositionInfoSource::NoError, + + //Leave a gap for future error enum values in QGeoPositionInfoSource::Error + SocketError = 100 + }; + Q_ENUMS(SourceError) + + QDeclarativePositionSource(); + ~QDeclarativePositionSource(); + void setNmeaSource(const QUrl &nmeaSource); + void setUpdateInterval(int updateInterval); + void setActive(bool active); + void setPreferredPositioningMethods(PositioningMethods methods); + + QString name() const; + void setName(const QString &name); + + QUrl nmeaSource() const; + int updateInterval() const; + bool isActive() const; + bool isValid() const; + QDeclarativePosition *position(); + PositioningMethods supportedPositioningMethods() const; + PositioningMethods preferredPositioningMethods() const; + SourceError sourceError() const; + QGeoPositionInfoSource *positionSource() const; + + // Virtuals from QQmlParserStatus + void classBegin() { } + void componentComplete(); + +public Q_SLOTS: + void update(); // TODO Qt 6 change to void update(int) + void start(); + void stop(); + +Q_SIGNALS: + void positionChanged(); + void activeChanged(); + void nmeaSourceChanged(); + void updateIntervalChanged(); + void supportedPositioningMethodsChanged(); + void preferredPositioningMethodsChanged(); + void sourceErrorChanged(); + void nameChanged(); + void validityChanged(); + void updateTimeout(); + +private Q_SLOTS: + void positionUpdateReceived(const QGeoPositionInfo &update); + void sourceErrorReceived(const QGeoPositionInfoSource::Error error); + void socketConnected(); + void socketError(QAbstractSocket::SocketError error); + void updateTimeoutReceived(); + +private: + void setPosition(const QGeoPositionInfo &pi); + + QGeoPositionInfoSource *m_positionSource; + QDeclarativePosition m_position; + PositioningMethods m_preferredPositioningMethods; + QFile *m_nmeaFile; + QTcpSocket *m_nmeaSocket; + QString m_nmeaFileName; + QUrl m_nmeaSource; + bool m_active; + bool m_singleUpdate; + int m_updateInterval; + SourceError m_sourceError; +}; + +QT_END_NAMESPACE + +QML_DECLARE_TYPE(QDeclarativePositionSource) + +#endif diff --git a/src/positioningquick/qpositioningquickglobal.h b/src/positioningquick/qpositioningquickglobal.h new file mode 100644 index 0000000..214e462 --- /dev/null +++ b/src/positioningquick/qpositioningquickglobal.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPOSITIONINGQUICKGLOBAL_H +#define QPOSITIONINGQUICKGLOBAL_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +#ifndef QT_STATIC +# if defined(QT_BUILD_POSITIONINGQUICK_LIB) +# define Q_POSITIONINGQUICK_EXPORT Q_DECL_EXPORT +# else +# define Q_POSITIONINGQUICK_EXPORT Q_DECL_IMPORT +# endif +#else +# define Q_POSITIONINGQUICK_EXPORT +#endif + +QT_END_NAMESPACE + +#endif // QPOSITIONINGQUICKGLOBAL_H diff --git a/src/positioningquick/qpositioningquickglobal_p.h b/src/positioningquick/qpositioningquickglobal_p.h new file mode 100644 index 0000000..46e40f2 --- /dev/null +++ b/src/positioningquick/qpositioningquickglobal_p.h @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QPOSITIONINGQUICKGLOBAL_P_H +#define QPOSITIONINGQUICKGLOBAL_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qpositioningquickglobal.h" + +QT_BEGIN_NAMESPACE + +#define Q_POSITIONINGQUICK_PRIVATE_EXPORT Q_POSITIONINGQUICK_EXPORT + +QT_END_NAMESPACE + +#endif // QPOSITIONINGQUICKGLOBAL_P_H diff --git a/src/src.pro b/src/src.pro new file mode 100644 index 0000000..417e227 --- /dev/null +++ b/src/src.pro @@ -0,0 +1,44 @@ +TEMPLATE = subdirs + +QT_FOR_CONFIG += location-private +include($$OUT_PWD/location/qtlocation-config.pri) +include($$OUT_PWD/positioning/qtpositioning-config.pri) + +clip2tri.subdir = 3rdparty/clip2tri +poly2tri.subdir = 3rdparty/poly2tri +clipper.subdir = 3rdparty/clipper + +SUBDIRS += clip2tri clipper poly2tri +clip2tri.depends = clipper poly2tri + +SUBDIRS += positioning +positioning.depends = clip2tri + +qtHaveModule(quick) { + SUBDIRS += positioningquick location + positioningquick.depends += positioning + location.depends += positioningquick clip2tri + + plugins.depends += location + + SUBDIRS += imports + imports.depends += positioningquick positioning location +} +plugins.depends += positioning +SUBDIRS += plugins + +!android:contains(QT_CONFIG, private_tests) { + SUBDIRS += positioning_doc_snippets + positioning_doc_snippets.subdir = positioning/doc/snippets + + #plugin dependency required during static builds + positioning_doc_snippets.depends = positioning plugins + + qtHaveModule(quick) { + SUBDIRS += location_doc_snippets + location_doc_snippets.subdir = location/doc/snippets + + #plugin dependency required during static builds + location_doc_snippets.depends = location plugins + } +} diff --git a/sync.profile b/sync.profile new file mode 100644 index 0000000..dcdc729 --- /dev/null +++ b/sync.profile @@ -0,0 +1,7 @@ +%modules = ( # path to module name map + "QtLocation" => "$basedir/src/location", + "QtPositioning" => "$basedir/src/positioning", + "QtPositioningQuick" => "$basedir/src/positioningquick", +); +%moduleheaders = ( # restrict the module headers to those found in relative path +); diff --git a/tests/applications/positioning_backend/main.cpp b/tests/applications/positioning_backend/main.cpp new file mode 100644 index 0000000..930f1c9 --- /dev/null +++ b/tests/applications/positioning_backend/main.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "widget.h" +#include + +#include +#include +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + + Widget *w1 = new Widget; + Widget *w2 = new Widget; + + QTabWidget tabWidget; + tabWidget.setTabPosition(QTabWidget::South); + + tabWidget.addTab(w1, "Instance 1"); + tabWidget.addTab(w2, "Instance 2"); + + tabWidget.show(); + return a.exec(); +} diff --git a/tests/applications/positioning_backend/positioning_backend.pro b/tests/applications/positioning_backend/positioning_backend.pro new file mode 100644 index 0000000..410dbc8 --- /dev/null +++ b/tests/applications/positioning_backend/positioning_backend.pro @@ -0,0 +1,14 @@ +QT += core gui positioning widgets + +TARGET = posbackendtesting +TEMPLATE = app + + +SOURCES += main.cpp\ + widget.cpp + +HEADERS += widget.h + +FORMS += widget.ui + +winrt: WINRT_MANIFEST.capabilities_device += location diff --git a/tests/applications/positioning_backend/widget.cpp b/tests/applications/positioning_backend/widget.cpp new file mode 100644 index 0000000..93a42a8 --- /dev/null +++ b/tests/applications/positioning_backend/widget.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "widget.h" +#include "ui_widget.h" +#include +#include + +Widget::Widget(QWidget *parent) : + QWidget(parent), + ui(new Ui::Widget) +{ + ui->setupUi(this); + qDebug() << "Available:" << QGeoPositionInfoSource::availableSources(); + m_posSource = QGeoPositionInfoSource::createDefaultSource(this); + if (!m_posSource) + qFatal("No Position Source created!"); + connect(m_posSource, SIGNAL(positionUpdated(QGeoPositionInfo)), + this, SLOT(positionUpdated(QGeoPositionInfo))); + + connect(ui->horizontalSlider, SIGNAL(valueChanged(int)), + this, SLOT(setInterval(int))); + connect(m_posSource, SIGNAL(updateTimeout()), + this, SLOT(positionTimedOut())); + + ui->groupBox->setLayout(ui->gridLayout); + ui->horizontalSlider->setMinimum(m_posSource->minimumUpdateInterval()); + ui->labelTimeOut->setVisible(false); + + connect(m_posSource, SIGNAL(error(QGeoPositionInfoSource::Error)), + this, SLOT(errorChanged(QGeoPositionInfoSource::Error))); +} + +void Widget::positionUpdated(QGeoPositionInfo gpsPos) +{ + QGeoCoordinate coord = gpsPos.coordinate(); + ui->labelLatitude->setText(QString::number(coord.latitude())); + ui->labelLongitude->setText(QString::number(coord.longitude())); + ui->labelAltitude->setText(QString::number(coord.altitude())); + ui->labelTimeStamp->setText(gpsPos.timestamp().toString()); + if (gpsPos.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + ui->labelHAccuracy->setText(QString::number(gpsPos.attribute(QGeoPositionInfo::HorizontalAccuracy))); + else + ui->labelHAccuracy->setText(QStringLiteral("N/A")); + + if (gpsPos.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) + ui->labelVAccuracy->setText(QString::number(gpsPos.attribute(QGeoPositionInfo::VerticalAccuracy))); + else + ui->labelVAccuracy->setText(QStringLiteral("N/A")); + + if (gpsPos.hasAttribute(QGeoPositionInfo::Direction)) + ui->labelDirection->setText(QString::number(gpsPos.attribute(QGeoPositionInfo::Direction))); + else + ui->labelDirection->setText(QStringLiteral("N/A")); + + if (gpsPos.hasAttribute(QGeoPositionInfo::GroundSpeed)) + ui->labelSpeed->setText(QString::number(gpsPos.attribute(QGeoPositionInfo::GroundSpeed))); + else + ui->labelSpeed->setText(QStringLiteral("N/A")); +} + +void Widget::positionTimedOut() +{ + ui->labelTimeOut->setVisible(true); +} + +void Widget::errorChanged(QGeoPositionInfoSource::Error err) +{ + ui->labelErrorState->setText(err == 3 ? QStringLiteral("OK") : QString::number(err)); +} + +Widget::~Widget() +{ + delete ui; +} + +void Widget::setInterval(int msec) +{ + m_posSource->setUpdateInterval(msec); +} + +void Widget::on_buttonRetrieve_clicked() +{ + // Requesting current position for _one_ time + m_posSource->requestUpdate(10000); +} + +void Widget::on_buttonStart_clicked() +{ + // Either start or stop the current position info source + bool running = ui->checkBox->isChecked(); + if (running) { + m_posSource->stopUpdates(); + ui->checkBox->setChecked(false); + } else { + m_posSource->startUpdates(); + ui->checkBox->setChecked(true); + } +} + +void Widget::on_radioButton_clicked() +{ + m_posSource->setPreferredPositioningMethods(QGeoPositionInfoSource::NoPositioningMethods); +} + +void Widget::on_radioButton_2_clicked() +{ + m_posSource->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods); +} + +void Widget::on_radioButton_3_clicked() +{ + m_posSource->setPreferredPositioningMethods(QGeoPositionInfoSource::NonSatellitePositioningMethods); +} + +void Widget::on_radioButton_4_clicked() +{ + m_posSource->setPreferredPositioningMethods(QGeoPositionInfoSource::AllPositioningMethods); +} + +void Widget::on_buttonUpdateSupported_clicked() +{ + QGeoPositionInfoSource::PositioningMethods m = m_posSource->supportedPositioningMethods(); + QString text; + switch (m) { + case QGeoPositionInfoSource::NoPositioningMethods: + text = QStringLiteral("None"); + break; + case QGeoPositionInfoSource::SatellitePositioningMethods: + text = QStringLiteral("Satellite"); + break; + case QGeoPositionInfoSource::NonSatellitePositioningMethods: + text = QStringLiteral("Non Satellite"); + break; + case QGeoPositionInfoSource::AllPositioningMethods: + text = QStringLiteral("All"); + break; + } + + ui->labelSupported->setText(text); +} diff --git a/tests/applications/positioning_backend/widget.h b/tests/applications/positioning_backend/widget.h new file mode 100644 index 0000000..fc04c42 --- /dev/null +++ b/tests/applications/positioning_backend/widget.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtPositioning module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef WIDGET_H +#define WIDGET_H + +#include +#include + +namespace Ui { + class Widget; +} + +class Widget : public QWidget +{ + Q_OBJECT + +public: + explicit Widget(QWidget *parent = 0); + ~Widget(); + +public slots: + void positionUpdated(QGeoPositionInfo gpsPos); + void setInterval(int msec); + void positionTimedOut(); + void errorChanged(QGeoPositionInfoSource::Error err); +private slots: + void on_buttonRetrieve_clicked(); + void on_buttonStart_clicked(); + void on_radioButton_2_clicked(); + void on_radioButton_clicked(); + void on_radioButton_3_clicked(); + void on_radioButton_4_clicked(); + + void on_buttonUpdateSupported_clicked(); + +private: + Ui::Widget *ui; + QGeoPositionInfoSource *m_posSource; +}; + +#endif // WIDGET_H diff --git a/tests/applications/positioning_backend/widget.ui b/tests/applications/positioning_backend/widget.ui new file mode 100644 index 0000000..a96a83f --- /dev/null +++ b/tests/applications/positioning_backend/widget.ui @@ -0,0 +1,323 @@ + + + Widget + + + + 0 + 0 + 276 + 467 + + + + Widget + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Latitude: + + + + + + + N/A + + + + + + + Longitude: + + + + + + + N/A + + + + + + + Altitude: + + + + + + + N/A + + + + + + + TimeStamp: + + + + + + + N/A + + + + + + + Horizontal Accuracy: + + + + + + + N/A + + + + + + + Vertical Accuracy: + + + + + + + N/A + + + + + + + TimeOut: + + + + + + + true + + + !!!!!TimeOut!!!!! + + + + + + + Supported Methods: + + + + + + + Error State: + + + + + + + N/A + + + + + + + + + N/A + + + + + + + Update + + + + + + + + + Direction: + + + + + + + N/A + + + + + + + Speed: + + + + + + + N/A + + + + + + + + + Method + + + + + 43 + 21 + 251 + 71 + + + + + + + None + + + + + + + Satelite + + + true + + + + + + + Non-Satelite + + + + + + + All + + + + + + + + + + + + + Interval: + + + + + + + 50 + + + 10000 + + + Qt::Horizontal + + + + + + + 0 + + + + + + + + + false + + + Running + + + + + + + + + Start/Stop + + + + + + + Retrieve + + + + + + + + + + + + horizontalSlider + valueChanged(int) + labelInterval + setNum(int) + + + 217 + 137 + + + 386 + 138 + + + + + diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro new file mode 100644 index 0000000..1229874 --- /dev/null +++ b/tests/auto/auto.pro @@ -0,0 +1,85 @@ +TEMPLATE = subdirs + +qtHaveModule(location) { + + #Place unit tests + SUBDIRS += qplace \ + qplaceattribute \ + qplacecategory \ + qplacecontactdetail \ + qplacecontentrequest \ + qplacedetailsreply \ + qplaceeditorial \ + qplacematchreply \ + qplacematchrequest \ + qplaceimage \ + qplaceratings \ + qplaceresult \ + qproposedsearchresult \ + qplacereply \ + qplacereview \ + qplacesearchrequest \ + qplacesupplier \ + qplacesearchresult \ + qplacesearchreply \ + qplacesearchsuggestionreply \ + qplaceuser \ + qplacemanager \ + qplacemanager_nokia \ + qplacemanager_unsupported \ + placesplugin_unsupported + + #misc tests + SUBDIRS += qmlinterface \ + cmake \ + doublevectors + + #Map and Navigation tests + SUBDIRS += geotestplugin \ + qgeocodingmanagerplugins \ + qgeocameracapabilities\ + qgeocameradata \ + qgeocodereply \ + qgeocodingmanager \ + qgeomaneuver \ + qgeotiledmapscene \ + qgeoroute \ + qgeoroutereply \ + qgeorouterequest \ + qgeoroutesegment \ + qgeoroutingmanager \ + qgeoroutingmanagerplugins \ + qgeoserviceprovider \ + qgeotiledmap \ + qgeotilespec \ + qgeoroutexmlparser \ + maptype \ + nokia_services \ + qgeocameratiles + + qtHaveModule(quick) { + SUBDIRS += declarative_core \ + declarative_geoshape + + !mac: SUBDIRS += declarative_ui + } +} + + +SUBDIRS += \ + positionplugin \ + positionplugintest \ + qgeoaddress \ + qgeoareamonitor \ + qgeoshape \ + qgeorectangle \ + qgeocircle \ + qgeopath \ + qgeopolygon \ + qgeocoordinate \ + qgeolocation \ + qgeopositioninfo \ + qgeopositioninfosource \ + qgeosatelliteinfo \ + qgeosatelliteinfosource \ + qnmeapositioninfosource diff --git a/tests/auto/bic/data/QtPositioning.5.10.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.10.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..cd57ca0 --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.10.0.linux-gcc-amd64.txt @@ -0,0 +1,4773 @@ +Class std::__failure_type + size=1 align=1 + base size=0 base align=1 +std::__failure_type (0x0x7f13e8ed68a0) 0 empty + +Class std::__do_is_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_destructible_impl (0x0x7f13e8fad060) 0 empty + +Class std::__do_is_nt_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nt_destructible_impl (0x0x7f13e8fad2a0) 0 empty + +Class std::__do_is_default_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_default_constructible_impl (0x0x7f13e8fad4e0) 0 empty + +Class std::__do_is_static_castable_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_static_castable_impl (0x0x7f13e8fad720) 0 empty + +Class std::__do_is_direct_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_direct_constructible_impl (0x0x7f13e8fad8a0) 0 empty + +Class std::__do_is_nary_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nary_constructible_impl (0x0x7f13e8fadc60) 0 empty + +Class std::__do_common_type_impl + size=1 align=1 + base size=0 base align=1 +std::__do_common_type_impl (0x0x7f13e6c69420) 0 empty + +Class std::__do_member_type_wrapper + size=1 align=1 + base size=0 base align=1 +std::__do_member_type_wrapper (0x0x7f13e6c694e0) 0 empty + +Class std::__result_of_memfun_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_ref_impl (0x0x7f13e6c69840) 0 empty + +Class std::__result_of_memfun_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_deref_impl (0x0x7f13e6c69900) 0 empty + +Class std::__result_of_memobj_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_ref_impl (0x0x7f13e6c699c0) 0 empty + +Class std::__result_of_memobj_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_deref_impl (0x0x7f13e6c69a80) 0 empty + +Class std::__result_of_other_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_other_impl (0x0x7f13e6c69d20) 0 empty + +Class std::piecewise_construct_t + size=1 align=1 + base size=0 base align=1 +std::piecewise_construct_t (0x0x7f13e6c69f00) 0 empty + +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7f13e6cdb3c0) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7f13e6cdb420) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7f13e6d4f0c0) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7f13e6d4f120) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7f13e6cac208) 0 empty + std::input_iterator_tag (0x0x7f13e6d4f180) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7f13e6cac270) 0 empty + std::forward_iterator_tag (0x0x7f13e6cac2d8) 0 empty + std::input_iterator_tag (0x0x7f13e6d4f1e0) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7f13e6cac340) 0 empty + std::bidirectional_iterator_tag (0x0x7f13e6cac3a8) 0 empty + std::forward_iterator_tag (0x0x7f13e6cac410) 0 empty + std::input_iterator_tag (0x0x7f13e6d4f240) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_iter (0x0x7f13e6d4fea0) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_val (0x0x7f13e6d4ff00) 0 empty + +Class __gnu_cxx::__ops::_Val_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Val_less_iter (0x0x7f13e6d4ff60) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7f13e6a0b000) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7f13e6a0b060) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7f13e6a0bb40) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7f13e6a0bd80) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7f13e6a0be40) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7f13e6a0bea0) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7f13e6a0bf60) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7f13e6abf000) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7f13e6abf480) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7f13e6abf4e0) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7f13e6abf540) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7f13e6cac958) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7f13e6abf5a0) 0 nearly-empty + primary-for std::bad_exception (0x0x7f13e6cac958) + +Class std::__exception_ptr::exception_ptr + size=8 align=8 + base size=8 base align=8 +std::__exception_ptr::exception_ptr (0x0x7f13e6abf600) 0 + +Vtable for std::nested_exception +std::nested_exception::_ZTVSt16nested_exception: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16nested_exception) +16 (int (*)(...))std::nested_exception::~nested_exception +24 (int (*)(...))std::nested_exception::~nested_exception + +Class std::nested_exception + size=16 align=8 + base size=16 base align=8 +std::nested_exception (0x0x7f13e6abf660) 0 + vptr=((& std::nested_exception::_ZTVSt16nested_exception) + 16u) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7f13e6cacb60) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7f13e6abfa80) 0 nearly-empty + primary-for std::bad_alloc (0x0x7f13e6cacb60) + +Vtable for std::bad_array_new_length +std::bad_array_new_length::_ZTVSt20bad_array_new_length: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt20bad_array_new_length) +16 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +24 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +32 (int (*)(...))std::bad_array_new_length::what + +Class std::bad_array_new_length + size=8 align=8 + base size=8 base align=8 +std::bad_array_new_length (0x0x7f13e6cacbc8) 0 nearly-empty + vptr=((& std::bad_array_new_length::_ZTVSt20bad_array_new_length) + 16u) + std::bad_alloc (0x0x7f13e6cacc30) 0 nearly-empty + primary-for std::bad_array_new_length (0x0x7f13e6cacbc8) + std::exception (0x0x7f13e6abfae0) 0 nearly-empty + primary-for std::bad_alloc (0x0x7f13e6cacc30) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7f13e6abfb40) 0 empty + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7f13e6839780) 0 + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7f13e663c480) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7f13e663c4e0) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7f13e66e13c0) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7f13e66e1420) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7f13e66e14e0) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7f13e66e1540) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7f13e66e15a0) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7f13e66e1600) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7f13e66e1720) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7f13e66e1780) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7f13e66e1ba0) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7f13e66e1c00) 0 + +Class std::_Hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Hash_impl (0x0x7f13e6219420) 0 empty + +Class std::_Fnv_hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Fnv_hash_impl (0x0x7f13e6219480) 0 empty + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7f13e62d3420) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7f13e608a240) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7f13e623d9c0) 0 + std::iterator (0x0x7f13e608a300) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7f13e623da28) 0 + std::_Bit_iterator_base (0x0x7f13e623da90) 0 + std::iterator (0x0x7f13e608a360) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7f13e623daf8) 0 + std::_Bit_iterator_base (0x0x7f13e623db60) 0 + std::iterator (0x0x7f13e608a3c0) 0 empty + +Class std::random_device + size=5000 align=8 + base size=5000 base align=8 +std::random_device (0x0x7f13e5e9a1e0) 0 + +Class std::bernoulli_distribution::param_type + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution::param_type (0x0x7f13e5e9af60) 0 + +Class std::bernoulli_distribution + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution (0x0x7f13e5e9af00) 0 + +Class std::seed_seq + size=24 align=8 + base size=24 base align=8 +std::seed_seq (0x0x7f13e5c01f00) 0 + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7f13e4bcea20) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7f13e4bcea80) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7f13e490c540) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7f13e490c5a0) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7f13e490c600) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7f13e490c660) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7f13e490c900) 0 + +Class std::__atomic_flag_base + size=1 align=1 + base size=1 base align=1 +std::__atomic_flag_base (0x0x7f13e490ce40) 0 + +Class std::atomic_flag + size=1 align=1 + base size=1 base align=1 +std::atomic_flag (0x0x7f13e4907888) 0 + std::__atomic_flag_base (0x0x7f13e490cea0) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7f13e4584000) 0 + QAtomicInteger (0x0x7f13e4584068) 0 + QBasicAtomicInteger (0x0x7f13e44b5600) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7f13e435eba0) 0 empty + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7f13e417cc00) 0 + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7f13e417cd20) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7f13e4362a90) 0 + QGenericArgument (0x0x7f13e417cd80) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7f13e417cf00) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7f13e3e1f000) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7f13e3ea1060) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7f13e3ea10c0) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7f13e3ea1360) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7f13e3ea13c0) 0 + +Class QtPrivate::QContainerImplHelper + size=1 align=1 + base size=0 base align=1 +QtPrivate::QContainerImplHelper (0x0x7f13e3ea1720) 0 empty + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7f13e3ea1780) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7f13e3ea17e0) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7f13e3ea1840) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7f13e3ea18a0) 0 + +Class std::__cow_string + size=8 align=8 + base size=8 base align=8 +std::__cow_string (0x0x7f13e3ea1c60) 0 + +Vtable for std::logic_error +std::logic_error::_ZTVSt11logic_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11logic_error) +16 (int (*)(...))std::logic_error::~logic_error +24 (int (*)(...))std::logic_error::~logic_error +32 (int (*)(...))std::logic_error::what + +Class std::logic_error + size=16 align=8 + base size=16 base align=8 +std::logic_error (0x0x7f13e3c47000) 0 + vptr=((& std::logic_error::_ZTVSt11logic_error) + 16u) + std::exception (0x0x7f13e3ea1d20) 0 nearly-empty + primary-for std::logic_error (0x0x7f13e3c47000) + +Vtable for std::domain_error +std::domain_error::_ZTVSt12domain_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12domain_error) +16 (int (*)(...))std::domain_error::~domain_error +24 (int (*)(...))std::domain_error::~domain_error +32 (int (*)(...))std::logic_error::what + +Class std::domain_error + size=16 align=8 + base size=16 base align=8 +std::domain_error (0x0x7f13e3c47068) 0 + vptr=((& std::domain_error::_ZTVSt12domain_error) + 16u) + std::logic_error (0x0x7f13e3c470d0) 0 + primary-for std::domain_error (0x0x7f13e3c47068) + std::exception (0x0x7f13e3ea1d80) 0 nearly-empty + primary-for std::logic_error (0x0x7f13e3c470d0) + +Vtable for std::invalid_argument +std::invalid_argument::_ZTVSt16invalid_argument: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16invalid_argument) +16 (int (*)(...))std::invalid_argument::~invalid_argument +24 (int (*)(...))std::invalid_argument::~invalid_argument +32 (int (*)(...))std::logic_error::what + +Class std::invalid_argument + size=16 align=8 + base size=16 base align=8 +std::invalid_argument (0x0x7f13e3c47138) 0 + vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16u) + std::logic_error (0x0x7f13e3c471a0) 0 + primary-for std::invalid_argument (0x0x7f13e3c47138) + std::exception (0x0x7f13e3ea1de0) 0 nearly-empty + primary-for std::logic_error (0x0x7f13e3c471a0) + +Vtable for std::length_error +std::length_error::_ZTVSt12length_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12length_error) +16 (int (*)(...))std::length_error::~length_error +24 (int (*)(...))std::length_error::~length_error +32 (int (*)(...))std::logic_error::what + +Class std::length_error + size=16 align=8 + base size=16 base align=8 +std::length_error (0x0x7f13e3c47208) 0 + vptr=((& std::length_error::_ZTVSt12length_error) + 16u) + std::logic_error (0x0x7f13e3c47270) 0 + primary-for std::length_error (0x0x7f13e3c47208) + std::exception (0x0x7f13e3ea1e40) 0 nearly-empty + primary-for std::logic_error (0x0x7f13e3c47270) + +Vtable for std::out_of_range +std::out_of_range::_ZTVSt12out_of_range: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12out_of_range) +16 (int (*)(...))std::out_of_range::~out_of_range +24 (int (*)(...))std::out_of_range::~out_of_range +32 (int (*)(...))std::logic_error::what + +Class std::out_of_range + size=16 align=8 + base size=16 base align=8 +std::out_of_range (0x0x7f13e3c472d8) 0 + vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16u) + std::logic_error (0x0x7f13e3c47340) 0 + primary-for std::out_of_range (0x0x7f13e3c472d8) + std::exception (0x0x7f13e3ea1ea0) 0 nearly-empty + primary-for std::logic_error (0x0x7f13e3c47340) + +Vtable for std::runtime_error +std::runtime_error::_ZTVSt13runtime_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13runtime_error) +16 (int (*)(...))std::runtime_error::~runtime_error +24 (int (*)(...))std::runtime_error::~runtime_error +32 (int (*)(...))std::runtime_error::what + +Class std::runtime_error + size=16 align=8 + base size=16 base align=8 +std::runtime_error (0x0x7f13e3c473a8) 0 + vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16u) + std::exception (0x0x7f13e3ea1f00) 0 nearly-empty + primary-for std::runtime_error (0x0x7f13e3c473a8) + +Vtable for std::range_error +std::range_error::_ZTVSt11range_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11range_error) +16 (int (*)(...))std::range_error::~range_error +24 (int (*)(...))std::range_error::~range_error +32 (int (*)(...))std::runtime_error::what + +Class std::range_error + size=16 align=8 + base size=16 base align=8 +std::range_error (0x0x7f13e3c47410) 0 + vptr=((& std::range_error::_ZTVSt11range_error) + 16u) + std::runtime_error (0x0x7f13e3c47478) 0 + primary-for std::range_error (0x0x7f13e3c47410) + std::exception (0x0x7f13e3ea1f60) 0 nearly-empty + primary-for std::runtime_error (0x0x7f13e3c47478) + +Vtable for std::overflow_error +std::overflow_error::_ZTVSt14overflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt14overflow_error) +16 (int (*)(...))std::overflow_error::~overflow_error +24 (int (*)(...))std::overflow_error::~overflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::overflow_error + size=16 align=8 + base size=16 base align=8 +std::overflow_error (0x0x7f13e3c474e0) 0 + vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16u) + std::runtime_error (0x0x7f13e3c47548) 0 + primary-for std::overflow_error (0x0x7f13e3c474e0) + std::exception (0x0x7f13e3c65000) 0 nearly-empty + primary-for std::runtime_error (0x0x7f13e3c47548) + +Vtable for std::underflow_error +std::underflow_error::_ZTVSt15underflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt15underflow_error) +16 (int (*)(...))std::underflow_error::~underflow_error +24 (int (*)(...))std::underflow_error::~underflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::underflow_error + size=16 align=8 + base size=16 base align=8 +std::underflow_error (0x0x7f13e3c475b0) 0 + vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16u) + std::runtime_error (0x0x7f13e3c47618) 0 + primary-for std::underflow_error (0x0x7f13e3c475b0) + std::exception (0x0x7f13e3c65060) 0 nearly-empty + primary-for std::runtime_error (0x0x7f13e3c47618) + +Vtable for std::_V2::error_category +std::_V2::error_category::_ZTVNSt3_V214error_categoryE: 10u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt3_V214error_categoryE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))std::_V2::error_category::_M_message +48 (int (*)(...))__cxa_pure_virtual +56 (int (*)(...))std::_V2::error_category::default_error_condition +64 (int (*)(...))std::_V2::error_category::equivalent +72 (int (*)(...))std::_V2::error_category::equivalent + +Class std::_V2::error_category + size=8 align=8 + base size=8 base align=8 +std::_V2::error_category (0x0x7f13e3c651e0) 0 nearly-empty + vptr=((& std::_V2::error_category::_ZTVNSt3_V214error_categoryE) + 16u) + +Class std::error_code + size=16 align=8 + base size=16 base align=8 +std::error_code (0x0x7f13e3c65420) 0 + +Class std::error_condition + size=16 align=8 + base size=16 base align=8 +std::error_condition (0x0x7f13e3c655a0) 0 + +Vtable for std::system_error +std::system_error::_ZTVSt12system_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12system_error) +16 (int (*)(...))std::system_error::~system_error +24 (int (*)(...))std::system_error::~system_error +32 (int (*)(...))std::runtime_error::what + +Class std::system_error + size=32 align=8 + base size=32 base align=8 +std::system_error (0x0x7f13e3c47af8) 0 + vptr=((& std::system_error::_ZTVSt12system_error) + 16u) + std::runtime_error (0x0x7f13e3c47b60) 0 + primary-for std::system_error (0x0x7f13e3c47af8) + std::exception (0x0x7f13e3c657e0) 0 nearly-empty + primary-for std::runtime_error (0x0x7f13e3c47b60) + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=32 align=8 + base size=32 base align=8 +std::ios_base::failure (0x0x7f13e3ccc750) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16u) + std::system_error (0x0x7f13e3ccc7b8) 0 + primary-for std::ios_base::failure (0x0x7f13e3ccc750) + std::runtime_error (0x0x7f13e3ccc820) 0 + primary-for std::system_error (0x0x7f13e3ccc7b8) + std::exception (0x0x7f13e3c65ae0) 0 nearly-empty + primary-for std::runtime_error (0x0x7f13e3ccc820) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7f13e3c65b40) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7f13e3c65ba0) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7f13e3c65c00) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7f13e3c65a80) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7f13e3da43c0) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7f13e3da4a80) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7f13e399b0d0 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7f13e399b1a0 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7f13e399b548 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7f13e399b618 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7f13e3950300) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7f13e3950360) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7f13e36d76c0) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7f13e36d7a20) 0 + +Class QStringView + size=16 align=8 + base size=16 base align=8 +QStringView (0x0x7f13e36d7ea0) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7f13e340ad20) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7f13e34983c0) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7f13e3498360) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7f13e323c480) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7f13e2fec0c0) 0 + +Class QtPrivate::QHashCombine + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombine (0x0x7f13e2feccc0) 0 empty + +Class QtPrivate::QHashCombineCommutative + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombineCommutative (0x0x7f13e2fecd20) 0 empty + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7f13e2fecd80) 0 + +Class QListData::NotArrayCompatibleLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotArrayCompatibleLayout (0x0x7f13e2dde180) 0 empty + +Class QListData::NotIndirectLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotIndirectLayout (0x0x7f13e2dde1e0) 0 empty + +Class QListData::ArrayCompatibleLayout + size=1 align=1 + base size=1 base align=1 +QListData::ArrayCompatibleLayout (0x0x7f13e2df4068) 0 empty + QListData::NotIndirectLayout (0x0x7f13e2dde240) 0 empty + +Class QListData::InlineWithPaddingLayout + size=1 align=1 + base size=1 base align=1 +QListData::InlineWithPaddingLayout (0x0x7f13e2e75620) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7f13e2dde2a0) 0 empty + QListData::NotIndirectLayout (0x0x7f13e2dde300) 0 empty + +Class QListData::IndirectLayout + size=1 align=1 + base size=1 base align=1 +QListData::IndirectLayout (0x0x7f13e2df40d0) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7f13e2dde360) 0 empty + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7f13e2dde3c0) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7f13e2dde120) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7f13e2dde840) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7f13e2c21a80) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7f13e2c21a20) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7f13e2c22af8) 0 + QList (0x0x7f13e2c22b60) 0 + QListSpecialMethods (0x0x7f13e2c21c60) 0 empty + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7f13e2cbe0c0) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7f13e2cbeba0) 0 + +Class std::allocator_arg_t + size=1 align=1 + base size=0 base align=1 +std::allocator_arg_t (0x0x7f13e2a9b240) 0 empty + +Class std::__uses_alloc_base + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc_base (0x0x7f13e2a9b3c0) 0 empty + +Class std::__uses_alloc0::_Sink + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc0::_Sink (0x0x7f13e2a9b480) 0 empty + +Class std::__uses_alloc0 + size=1 align=1 + base size=1 base align=1 +std::__uses_alloc0 (0x0x7f13e2d61340) 0 + std::__uses_alloc_base (0x0x7f13e2a9b420) 0 empty + +Class std::_Swallow_assign + size=1 align=1 + base size=0 base align=1 +std::_Swallow_assign (0x0x7f13e2bae4e0) 0 empty + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7f13e2bae720) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7f13e2bae7e0) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7f13e2bae900) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7f13e2baea80) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7f13e2baeea0) 0 + +Class QtMetaTypePrivate::VectorBoolElements + size=1 align=1 + base size=0 base align=1 +QtMetaTypePrivate::VectorBoolElements (0x0x7f13e2906000) 0 empty + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7f13e2906960) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7f13e2906d80) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7f13e25e30c0) 0 + +Class std::chrono::_V2::system_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::system_clock (0x0x7f13e23f6900) 0 empty + +Class std::chrono::_V2::steady_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::steady_clock (0x0x7f13e2557780) 0 empty + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7f13e25577e0) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7f13e25579c0) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7f13e2557960) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7f13e221ec60) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7f13e221ecc0) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7f13e221ed80) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7f13e224a680) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7f13e221ed20) 0 + primary-for QAbstractAnimation (0x0x7f13e224a680) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7f13e221ee40) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7f13e224a6e8) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7f13e221ede0) 0 + primary-for QAnimationDriver (0x0x7f13e224a6e8) + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7f13e221ef00) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7f13e224a750) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7f13e221eea0) 0 + primary-for QEventLoop (0x0x7f13e224a750) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7f13e22a8120) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7f13e22a81e0) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7f13e22a8240) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7f13e224a888) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7f13e22a8180) 0 + primary-for QAbstractEventDispatcher (0x0x7f13e224a888) + +Vtable for std::type_info +std::type_info::_ZTVSt9type_info: 8u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9type_info) +16 (int (*)(...))std::type_info::~type_info +24 (int (*)(...))std::type_info::~type_info +32 (int (*)(...))std::type_info::__is_pointer_p +40 (int (*)(...))std::type_info::__is_function_p +48 (int (*)(...))std::type_info::__do_catch +56 (int (*)(...))std::type_info::__do_upcast + +Class std::type_info + size=16 align=8 + base size=16 base align=8 +std::type_info (0x0x7f13e22a84e0) 0 + vptr=((& std::type_info::_ZTVSt9type_info) + 16u) + +Vtable for std::bad_cast +std::bad_cast::_ZTVSt8bad_cast: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8bad_cast) +16 (int (*)(...))std::bad_cast::~bad_cast +24 (int (*)(...))std::bad_cast::~bad_cast +32 (int (*)(...))std::bad_cast::what + +Class std::bad_cast + size=8 align=8 + base size=8 base align=8 +std::bad_cast (0x0x7f13e224aa90) 0 nearly-empty + vptr=((& std::bad_cast::_ZTVSt8bad_cast) + 16u) + std::exception (0x0x7f13e22a8540) 0 nearly-empty + primary-for std::bad_cast (0x0x7f13e224aa90) + +Vtable for std::bad_typeid +std::bad_typeid::_ZTVSt10bad_typeid: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt10bad_typeid) +16 (int (*)(...))std::bad_typeid::~bad_typeid +24 (int (*)(...))std::bad_typeid::~bad_typeid +32 (int (*)(...))std::bad_typeid::what + +Class std::bad_typeid + size=8 align=8 + base size=8 base align=8 +std::bad_typeid (0x0x7f13e224aaf8) 0 nearly-empty + vptr=((& std::bad_typeid::_ZTVSt10bad_typeid) + 16u) + std::exception (0x0x7f13e22a85a0) 0 nearly-empty + primary-for std::bad_typeid (0x0x7f13e224aaf8) + +Vtable for std::bad_function_call +std::bad_function_call::_ZTVSt17bad_function_call: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt17bad_function_call) +16 (int (*)(...))std::bad_function_call::~bad_function_call +24 (int (*)(...))std::bad_function_call::~bad_function_call +32 (int (*)(...))std::bad_function_call::what + +Class std::bad_function_call + size=8 align=8 + base size=8 base align=8 +std::bad_function_call (0x0x7f13e1fa4d68) 0 nearly-empty + vptr=((& std::bad_function_call::_ZTVSt17bad_function_call) + 16u) + std::exception (0x0x7f13e206c660) 0 nearly-empty + primary-for std::bad_function_call (0x0x7f13e1fa4d68) + +Class std::_Nocopy_types + size=16 align=8 + base size=16 base align=8 +std::_Nocopy_types (0x0x7f13e206c720) 0 + +Class std::_Any_data + size=16 align=8 + base size=16 base align=8 +std::_Any_data (0x0x7f13e206c780) 0 + +Class std::_Function_base + size=24 align=8 + base size=24 base align=8 +std::_Function_base (0x0x7f13e206c8a0) 0 + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7f13e206cd80) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7f13e2121300) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7f13e21216c0) 0 + +Class QHashData + size=48 align=8 + base size=44 base align=8 +QHashData (0x0x7f13e2121660) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7f13e2121720) 0 empty + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7f13e1f5a000) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7f13e1f5a0c0) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7f13e1f5a060) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7f13e1f5a120) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7f13e2121f60) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7f13e1bfdc00) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7f13e1ca32a0) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7f13e1ca3240) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7f13e1ca3360) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7f13e1ca3300) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7f13e19bf660) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7f13e19bfd20) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7f13e1b97480) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7f13e1b88af8) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7f13e1b97420) 0 + primary-for QAbstractItemModel (0x0x7f13e1b88af8) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7f13e1b977e0) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractTableModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7f13e1b88d00) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7f13e1b88d68) 0 + primary-for QAbstractTableModel (0x0x7f13e1b88d00) + QObject (0x0x7f13e1b97780) 0 + primary-for QAbstractItemModel (0x0x7f13e1b88d68) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7f13e1b978a0) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractListModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7f13e1b88dd0) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7f13e1b88e38) 0 + primary-for QAbstractListModel (0x0x7f13e1b88dd0) + QObject (0x0x7f13e1b97840) 0 + primary-for QAbstractItemModel (0x0x7f13e1b88e38) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7f13e1b97b40) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7f13e1b97c00) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QAbstractProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7f13e1b88f70) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7f13e189e000) 0 + primary-for QAbstractProxyModel (0x0x7f13e1b88f70) + QObject (0x0x7f13e1b97ba0) 0 + primary-for QAbstractItemModel (0x0x7f13e189e000) + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7f13e1b97cc0) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7f13e189e068) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7f13e1b97c60) 0 + primary-for QAbstractState (0x0x7f13e189e068) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7f13e1b97d80) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7f13e189e0d0) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7f13e1b97d20) 0 + primary-for QAbstractTransition (0x0x7f13e189e0d0) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7f13e1b97e40) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7f13e189e138) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7f13e189e1a0) 0 + primary-for QAnimationGroup (0x0x7f13e189e138) + QObject (0x0x7f13e1b97de0) 0 + primary-for QAbstractAnimation (0x0x7f13e189e1a0) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7f13e190bba0) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7f13e190be40) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7f13e190bf00) 0 + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7f13e15a2240) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7f13e189e820) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7f13e15a21e0) 0 + primary-for QIODevice (0x0x7f13e189e820) + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7f13e15a2480) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7f13e189e958) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7f13e189e9c0) 0 + primary-for QBuffer (0x0x7f13e189e958) + QObject (0x0x7f13e15a2420) 0 + primary-for QIODevice (0x0x7f13e189e9c0) + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7f13e15a2540) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7f13e15a24e0) 0 + +Class QStaticByteArrayMatcherBase::Skiptable + size=256 align=1 + base size=256 base align=1 +QStaticByteArrayMatcherBase::Skiptable (0x0x7f13e15a2660) 0 + +Class QStaticByteArrayMatcherBase + size=256 align=16 + base size=256 base align=16 +QStaticByteArrayMatcherBase (0x0x7f13e15a2600) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7f13e15a2840) 0 + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7f13e15a2a20) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7f13e173d060) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7f13e173d120) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7f13e13bb120) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7f13e13bb5a0) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7f13e13a3ea0) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7f13e13bb600) 0 + primary-for QTimerEvent (0x0x7f13e13a3ea0) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7f13e13a3f08) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7f13e13bb660) 0 + primary-for QChildEvent (0x0x7f13e13a3f08) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7f13e142d478) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7f13e13bbb40) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7f13e142d478) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7f13e142d4e0) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7f13e13bbba0) 0 + primary-for QDeferredDeleteEvent (0x0x7f13e142d4e0) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7f13e13bbc60) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7f13e142d548) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7f13e13bbc00) 0 + primary-for QCoreApplication (0x0x7f13e142d548) + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7f13e13bbcc0) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7f13e13bbd20) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7f13e1493360) 0 + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7f13e14933c0) 0 + +Class QtPrivate::StreamStateSaver + size=16 align=8 + base size=12 base align=8 +QtPrivate::StreamStateSaver (0x0x7f13e1493480) 0 + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7f13e1493960) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7f13e1493c00) 0 + +Class QDateTime::ShortData + size=8 align=8 + base size=8 base align=8 +QDateTime::ShortData (0x0x7f13e11763c0) 0 + +Class QDateTime::Data + size=8 align=8 + base size=8 base align=8 +QDateTime::Data (0x0x7f13e1176420) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7f13e1176360) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7f13e1225540) 0 + +Class QDeadlineTimer + size=16 align=8 + base size=16 base align=8 +QDeadlineTimer (0x0x7f13e1225a20) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7f13e13499c0) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7f13e1349c60) 0 + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7f13e1349ea0) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7f13e107f060) 0 + +Class QDebug::Stream + size=80 align=8 + base size=76 base align=8 +QDebug::Stream (0x0x7f13e107f5a0) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7f13e107f540) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7f13e0de5a80) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7f13e0de5b40) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7f13e0eb6cc0) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7f13e0ed1410) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7f13e0ed1478) 0 + primary-for QFileDevice (0x0x7f13e0ed1410) + QObject (0x0x7f13e0eb6c60) 0 + primary-for QIODevice (0x0x7f13e0ed1478) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7f13e0eb6f00) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7f13e0ed15b0) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7f13e0ed1618) 0 + primary-for QFile (0x0x7f13e0ed15b0) + QIODevice (0x0x7f13e0ed1680) 0 + primary-for QFileDevice (0x0x7f13e0ed1618) + QObject (0x0x7f13e0eb6ea0) 0 + primary-for QIODevice (0x0x7f13e0ed1680) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7f13e0f3f120) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7f13e0f3f540) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7f13e0f3ff00) 0 + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7f13e0c33180) 0 + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7f13e0cea5a0) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7f13e0ce28f0) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7f13e0ce2958) 0 + primary-for QEventTransition (0x0x7f13e0ce28f0) + QObject (0x0x7f13e0cea540) 0 + primary-for QAbstractTransition (0x0x7f13e0ce2958) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7f13e0ce29c0) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7f13e0cea600) 0 nearly-empty + primary-for QException (0x0x7f13e0ce29c0) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7f13e0ce2a28) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7f13e0ce2a90) 0 nearly-empty + primary-for QUnhandledException (0x0x7f13e0ce2a28) + std::exception (0x0x7f13e0cea660) 0 nearly-empty + primary-for QException (0x0x7f13e0ce2a90) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7f13e0cea6c0) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7f13e0cea780) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7f13e0cea7e0) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7f13e0cea900) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7f13e0ce2af8) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7f13e0cea8a0) 0 + primary-for QFileSelector (0x0x7f13e0ce2af8) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7f13e0cea9c0) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7f13e0ce2b60) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7f13e0cea960) 0 + primary-for QFileSystemWatcher (0x0x7f13e0ce2b60) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7f13e0ceaa80) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7f13e0ce2bc8) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7f13e0ce2c30) 0 + primary-for QFinalState (0x0x7f13e0ce2bc8) + QObject (0x0x7f13e0ceaa20) 0 + primary-for QAbstractState (0x0x7f13e0ce2c30) + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7f13e0ceaae0) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7f13e0ceab40) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7f13e0ce2d68) 0 + QBasicMutex (0x0x7f13e0cead20) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7f13e0cead80) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7f13e0ceade0) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7f13e0ceae40) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7f13e0ceaf60) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7f13e0a6d7e0) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7f13e0b1f000) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 0u +48 0u +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7f13e0a72ea0) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7f13e0a6df60) 0 + primary-for QFutureWatcherBase (0x0x7f13e0a72ea0) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7f13e0b1f600) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7f13e0b317b8) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7f13e0b31820) 0 + primary-for QHistoryState (0x0x7f13e0b317b8) + QObject (0x0x7f13e0b1f5a0) 0 + primary-for QAbstractState (0x0x7f13e0b31820) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7f13e0b1f6c0) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7f13e0b31888) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7f13e0b318f0) 0 + primary-for QIdentityProxyModel (0x0x7f13e0b31888) + QAbstractItemModel (0x0x7f13e0b31958) 0 + primary-for QAbstractProxyModel (0x0x7f13e0b318f0) + QObject (0x0x7f13e0b1f660) 0 + primary-for QAbstractItemModel (0x0x7f13e0b31958) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7f13e0b1f720) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7f13e0b1fde0) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7f13e07ce1a0) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7f13e0b1fd80) 0 + primary-for QItemSelectionModel (0x0x7f13e07ce1a0) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7f13e07ce3a8) 0 + QList (0x0x7f13e07ce410) 0 + QListSpecialMethods (0x0x7f13e0817120) 0 empty + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7f13e0817600) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7f13e05b3d20) 0 + +Class QJsonValuePtr + size=24 align=8 + base size=24 base align=8 +QJsonValuePtr (0x0x7f13e061c2a0) 0 + +Class QJsonValueRefPtr + size=16 align=8 + base size=16 base align=8 +QJsonValueRefPtr (0x0x7f13e061c300) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7f13e061c4e0) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7f13e061c540) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7f13e061c480) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7f13e06de780) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7f13e06de7e0) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7f13e06dee40) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7f13e06deea0) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7f13e06dede0) 0 + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7f13e041f180) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7f13e03e5750) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7f13e041f120) 0 + primary-for QLibrary (0x0x7f13e03e5750) + +Class QVersionNumber::SegmentStorage + size=8 align=8 + base size=8 base align=8 +QVersionNumber::SegmentStorage (0x0x7f13e041f840) 0 + +Class QVersionNumber + size=8 align=8 + base size=8 base align=8 +QVersionNumber (0x0x7f13e041f360) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7f13e041fd20) 0 empty + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7f13e041fd80) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7f13e04f6060) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7f13e04f6300) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7f13e04f6c60) 0 + +Class QLinkedListData + size=32 align=8 + base size=25 base align=8 +QLinkedListData (0x0x7f13e01b2600) 0 + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7f13e01b2960) 0 + +Class QLoggingCategory::AtomicBools + size=4 align=1 + base size=4 base align=1 +QLoggingCategory::AtomicBools (0x0x7f13e01b2ae0) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7f13e01b2a80) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7f13e01b2c60) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7f13e01b2f00) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7f13dff3c5a0) 0 + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7f13dff3c600) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7f13dff3cc00) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7f13dff3cf00) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7f13dff3cf60) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7f13dfff32a0) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7f13dff5e9c0) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7f13dfff3240) 0 + primary-for QMimeData (0x0x7f13dff5e9c0) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7f13dfff3300) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7f13dfff3600) 0 + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7f13dfff36c0) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7f13dff5ebc8) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7f13dfff3660) 0 + primary-for QObjectCleanupHandler (0x0x7f13dff5ebc8) + +Class QOperatingSystemVersion + size=16 align=4 + base size=16 base align=4 +QOperatingSystemVersion (0x0x7f13dfff3720) 0 + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7f13dfff3ea0) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7f13e005b2d8) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7f13e005b340) 0 + primary-for QParallelAnimationGroup (0x0x7f13e005b2d8) + QAbstractAnimation (0x0x7f13e005b3a8) 0 + primary-for QAnimationGroup (0x0x7f13e005b340) + QObject (0x0x7f13dfff3e40) 0 + primary-for QAbstractAnimation (0x0x7f13e005b3a8) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7f13dfff3f60) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7f13e005b410) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7f13e005b478) 0 + primary-for QPauseAnimation (0x0x7f13e005b410) + QObject (0x0x7f13dfff3f00) 0 + primary-for QAbstractAnimation (0x0x7f13e005b478) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7f13e008f180) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7f13e008f480) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7f13e005b680) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7f13e008f420) 0 + primary-for QPluginLoader (0x0x7f13e005b680) + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7f13e008f4e0) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7f13e008fba0) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7f13e005bd00) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7f13e005bd68) 0 + primary-for QProcess (0x0x7f13e005bd00) + QObject (0x0x7f13e008fb40) 0 + primary-for QIODevice (0x0x7f13e005bd68) + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7f13e008fc60) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7f13e005bdd0) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7f13e005be38) 0 + primary-for QVariantAnimation (0x0x7f13e005bdd0) + QObject (0x0x7f13e008fc00) 0 + primary-for QAbstractAnimation (0x0x7f13e005be38) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7f13e008fd20) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7f13e005bf08) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7f13e005bf70) 0 + primary-for QPropertyAnimation (0x0x7f13e005bf08) + QAbstractAnimation (0x0x7f13dfd33000) 0 + primary-for QVariantAnimation (0x0x7f13e005bf70) + QObject (0x0x7f13e008fcc0) 0 + primary-for QAbstractAnimation (0x0x7f13dfd33000) + +Class QRandomGenerator::Storage + size=2504 align=8 + base size=2504 base align=8 +QRandomGenerator::Storage (0x0x7f13e008fe40) 0 + +Class QRandomGenerator + size=2512 align=8 + base size=2512 base align=8 +QRandomGenerator (0x0x7f13e008fde0) 0 + +Class QRandomGenerator64 + size=2512 align=8 + base size=2512 base align=8 +QRandomGenerator64 (0x0x7f13dfdd12d8) 0 + QRandomGenerator (0x0x7f13dfda8de0) 0 + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7f13dfda8ea0) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7f13dfe34180) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7f13dfe34240) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7f13dfe34300) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7f13dfe345a0) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7f13dfe34840) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7f13dfe34ae0) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7f13dfe34d80) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7f13dfc563c0) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7f13dfc566c0) 0 + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7f13dfc569c0) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7f13dfc56b40) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7f13dfc754e0) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7f13dfc75548) 0 + primary-for QSaveFile (0x0x7f13dfc754e0) + QIODevice (0x0x7f13dfc755b0) 0 + primary-for QFileDevice (0x0x7f13dfc75548) + QObject (0x0x7f13dfc56ae0) 0 + primary-for QIODevice (0x0x7f13dfc755b0) + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7f13dfc56c00) 0 + +Class QSemaphoreReleaser + size=16 align=8 + base size=12 base align=8 +QSemaphoreReleaser (0x0x7f13dfc56c60) 0 + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7f13df9f32a0) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7f13df9d8dd0) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7f13df9d8e38) 0 + primary-for QSequentialAnimationGroup (0x0x7f13df9d8dd0) + QAbstractAnimation (0x0x7f13df9d8ea0) 0 + primary-for QAnimationGroup (0x0x7f13df9d8e38) + QObject (0x0x7f13df9f3240) 0 + primary-for QAbstractAnimation (0x0x7f13df9d8ea0) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7f13df9f3360) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7f13df9d8f08) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7f13df9f3300) 0 + primary-for QSettings (0x0x7f13df9d8f08) + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7f13df9f3420) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7f13df9d8f70) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7f13df9f33c0) 0 + primary-for QSharedMemory (0x0x7f13df9d8f70) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7f13df9f34e0) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7f13dfa42000) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7f13df9f3480) 0 + primary-for QSignalMapper (0x0x7f13dfa42000) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7f13df9f35a0) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7f13dfa42068) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7f13dfa420d0) 0 + primary-for QSignalTransition (0x0x7f13dfa42068) + QObject (0x0x7f13df9f3540) 0 + primary-for QAbstractTransition (0x0x7f13dfa420d0) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7f13df9f3660) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7f13dfa42138) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7f13df9f3600) 0 + primary-for QSocketNotifier (0x0x7f13dfa42138) + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7f13df9f3720) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7f13dfa421a0) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7f13dfa42208) 0 + primary-for QSortFilterProxyModel (0x0x7f13dfa421a0) + QAbstractItemModel (0x0x7f13dfa42270) 0 + primary-for QAbstractProxyModel (0x0x7f13dfa42208) + QObject (0x0x7f13df9f36c0) 0 + primary-for QAbstractItemModel (0x0x7f13dfa42270) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7f13df9f37e0) 0 empty + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7f13df9f3a20) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7f13dfa42410) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7f13dfa42478) 0 + primary-for QState (0x0x7f13dfa42410) + QObject (0x0x7f13df9f39c0) 0 + primary-for QAbstractState (0x0x7f13dfa42478) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7f13df9f3b40) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7f13dfa42618) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7f13df9f3ba0) 0 + primary-for QStateMachine::SignalEvent (0x0x7f13dfa42618) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7f13dfa42680) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7f13df9f3c00) 0 + primary-for QStateMachine::WrappedEvent (0x0x7f13dfa42680) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7f13dfa424e0) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7f13dfa42548) 0 + primary-for QStateMachine (0x0x7f13dfa424e0) + QAbstractState (0x0x7f13dfa425b0) 0 + primary-for QState (0x0x7f13dfa42548) + QObject (0x0x7f13df9f3ae0) 0 + primary-for QAbstractState (0x0x7f13dfa425b0) + +Class QStorageInfo + size=8 align=8 + base size=8 base align=8 +QStorageInfo (0x0x7f13df9f3c60) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7f13df73dba0) 0 empty + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7f13df79a6c0) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7f13df75db60) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7f13df75dbc8) 0 + primary-for QStringListModel (0x0x7f13df75db60) + QAbstractItemModel (0x0x7f13df75dc30) 0 + primary-for QAbstractListModel (0x0x7f13df75dbc8) + QObject (0x0x7f13df79a660) 0 + primary-for QAbstractItemModel (0x0x7f13df75dc30) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7f13df79a720) 0 + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7f13df79a7e0) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7f13df79a900) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7f13df75dc98) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7f13df75dd00) 0 + primary-for QTemporaryFile (0x0x7f13df75dc98) + QFileDevice (0x0x7f13df75dd68) 0 + primary-for QFile (0x0x7f13df75dd00) + QIODevice (0x0x7f13df75ddd0) 0 + primary-for QFileDevice (0x0x7f13df75dd68) + QObject (0x0x7f13df79a8a0) 0 + primary-for QIODevice (0x0x7f13df75ddd0) + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7f13df79a960) 0 + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7f13df79aba0) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 0u +64 0u + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7f13df79ab40) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7f13df79ad80) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7f13df79ade0) 0 + +Class std::__mutex_base + size=40 align=8 + base size=40 base align=8 +std::__mutex_base (0x0x7f13df79ae40) 0 + +Class std::__recursive_mutex_base + size=40 align=8 + base size=40 base align=8 +std::__recursive_mutex_base (0x0x7f13df79aea0) 0 + +Class std::mutex + size=40 align=8 + base size=40 base align=8 +std::mutex (0x0x7f13df8bc000) 0 + std::__mutex_base (0x0x7f13df79af00) 0 + +Class std::recursive_mutex + size=40 align=8 + base size=40 base align=8 +std::recursive_mutex (0x0x7f13df8bc068) 0 + std::__recursive_mutex_base (0x0x7f13df79af60) 0 + +Class std::timed_mutex + size=40 align=8 + base size=40 base align=8 +std::timed_mutex (0x0x7f13df8d2460) 0 + std::__mutex_base (0x0x7f13df8cf0c0) 0 + std::__timed_mutex_impl (0x0x7f13df8cf120) 0 empty + +Class std::recursive_timed_mutex + size=40 align=8 + base size=40 base align=8 +std::recursive_timed_mutex (0x0x7f13df8e80e0) 0 + std::__recursive_mutex_base (0x0x7f13df8cf1e0) 0 + std::__timed_mutex_impl (0x0x7f13df8cf240) 0 empty + +Class std::defer_lock_t + size=1 align=1 + base size=0 base align=1 +std::defer_lock_t (0x0x7f13df8cf2a0) 0 empty + +Class std::try_to_lock_t + size=1 align=1 + base size=0 base align=1 +std::try_to_lock_t (0x0x7f13df8cf300) 0 empty + +Class std::adopt_lock_t + size=1 align=1 + base size=0 base align=1 +std::adopt_lock_t (0x0x7f13df8cf360) 0 empty + +Class std::once_flag + size=4 align=4 + base size=4 base align=4 +std::once_flag (0x0x7f13df8cf5a0) 0 + +Vtable for __gnu_cxx::__concurrence_lock_error +__gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_lock_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error +24 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error +32 (int (*)(...))__gnu_cxx::__concurrence_lock_error::what + +Class __gnu_cxx::__concurrence_lock_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_lock_error (0x0x7f13df8bc1a0) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE) + 16u) + std::exception (0x0x7f13df8cf660) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_lock_error (0x0x7f13df8bc1a0) + +Vtable for __gnu_cxx::__concurrence_unlock_error +__gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx26__concurrence_unlock_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error +24 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error +32 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::what + +Class __gnu_cxx::__concurrence_unlock_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_unlock_error (0x0x7f13df8bc208) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE) + 16u) + std::exception (0x0x7f13df8cf720) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_unlock_error (0x0x7f13df8bc208) + +Vtable for __gnu_cxx::__concurrence_broadcast_error +__gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx29__concurrence_broadcast_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error +24 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error +32 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::what + +Class __gnu_cxx::__concurrence_broadcast_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_broadcast_error (0x0x7f13df8bc270) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE) + 16u) + std::exception (0x0x7f13df8cf7e0) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_broadcast_error (0x0x7f13df8bc270) + +Vtable for __gnu_cxx::__concurrence_wait_error +__gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_wait_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error +24 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error +32 (int (*)(...))__gnu_cxx::__concurrence_wait_error::what + +Class __gnu_cxx::__concurrence_wait_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_wait_error (0x0x7f13df8bc340) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE) + 16u) + std::exception (0x0x7f13df8cf8a0) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_wait_error (0x0x7f13df8bc340) + +Class __gnu_cxx::__mutex + size=40 align=8 + base size=40 base align=8 +__gnu_cxx::__mutex (0x0x7f13df8cf960) 0 + +Class __gnu_cxx::__recursive_mutex + size=40 align=8 + base size=40 base align=8 +__gnu_cxx::__recursive_mutex (0x0x7f13df8cf9c0) 0 + +Class __gnu_cxx::__scoped_lock + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__scoped_lock (0x0x7f13df8cfa20) 0 + +Class __gnu_cxx::__cond + size=48 align=8 + base size=48 base align=8 +__gnu_cxx::__cond (0x0x7f13df8cfa80) 0 + +Vtable for std::bad_weak_ptr +std::bad_weak_ptr::_ZTVSt12bad_weak_ptr: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12bad_weak_ptr) +16 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr +24 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr +32 (int (*)(...))std::bad_weak_ptr::what + +Class std::bad_weak_ptr + size=8 align=8 + base size=8 base align=8 +std::bad_weak_ptr (0x0x7f13df8bc618) 0 nearly-empty + vptr=((& std::bad_weak_ptr::_ZTVSt12bad_weak_ptr) + 16u) + std::exception (0x0x7f13df8cfde0) 0 nearly-empty + primary-for std::bad_weak_ptr (0x0x7f13df8bc618) + +Class std::_Sp_make_shared_tag + size=1 align=1 + base size=0 base align=1 +std::_Sp_make_shared_tag (0x0x7f13df619660) 0 empty + +Class std::_Sp_locker + size=2 align=1 + base size=2 base align=1 +std::_Sp_locker (0x0x7f13df619e40) 0 + +Class std::thread::id + size=8 align=8 + base size=8 base align=8 +std::thread::id (0x0x7f13df2a9060) 0 + +Vtable for std::thread::_Impl_base +std::thread::_Impl_base::_ZTVNSt6thread10_Impl_baseE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6thread10_Impl_baseE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class std::thread::_Impl_base + size=24 align=8 + base size=24 base align=8 +std::thread::_Impl_base (0x0x7f13df2a90c0) 0 + vptr=((& std::thread::_Impl_base::_ZTVNSt6thread10_Impl_baseE) + 16u) + +Class std::thread + size=8 align=8 + base size=8 base align=8 +std::thread (0x0x7f13df2a9000) 0 + +Class std::condition_variable + size=48 align=8 + base size=48 base align=8 +std::condition_variable (0x0x7f13df3e0cc0) 0 + +Class std::__at_thread_exit_elt + size=16 align=8 + base size=16 base align=8 +std::__at_thread_exit_elt (0x0x7f13df3e0d80) 0 + +Class std::_V2::condition_variable_any + size=64 align=8 + base size=64 base align=8 +std::_V2::condition_variable_any (0x0x7f13df3e0de0) 0 + +Class std::__atomic_futex_unsigned_base + size=1 align=1 + base size=0 base align=1 +std::__atomic_futex_unsigned_base (0x0x7f13df0cd480) 0 empty + +Vtable for std::future_error +std::future_error::_ZTVSt12future_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12future_error) +16 (int (*)(...))std::future_error::~future_error +24 (int (*)(...))std::future_error::~future_error +32 (int (*)(...))std::future_error::what + +Class std::future_error + size=32 align=8 + base size=32 base align=8 +std::future_error (0x0x7f13df0ca958) 0 + vptr=((& std::future_error::_ZTVSt12future_error) + 16u) + std::logic_error (0x0x7f13df0ca9c0) 0 + primary-for std::future_error (0x0x7f13df0ca958) + std::exception (0x0x7f13df0cd5a0) 0 nearly-empty + primary-for std::logic_error (0x0x7f13df0ca9c0) + +Class std::__future_base::_Result_base::_Deleter + size=1 align=1 + base size=0 base align=1 +std::__future_base::_Result_base::_Deleter (0x0x7f13df0cd6c0) 0 empty + +Vtable for std::__future_base::_Result_base +std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt13__future_base12_Result_baseE) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class std::__future_base::_Result_base + size=16 align=8 + base size=16 base align=8 +std::__future_base::_Result_base (0x0x7f13df0cd660) 0 + vptr=((& std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE) + 16u) + +Class std::__future_base::_State_baseV2::__exception_ptr_tag + size=1 align=1 + base size=0 base align=1 +std::__future_base::_State_baseV2::__exception_ptr_tag (0x0x7f13df228c00) 0 empty + +Class std::__future_base::_State_baseV2::_Make_ready + size=32 align=8 + base size=32 base align=8 +std::__future_base::_State_baseV2::_Make_ready (0x0x7f13df162f70) 0 + std::__at_thread_exit_elt (0x0x7f13df228cc0) 0 + +Vtable for std::__future_base::_State_baseV2 +std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt13__future_base13_State_baseV2E) +16 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2 +24 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2 +32 (int (*)(...))std::__future_base::_State_baseV2::_M_complete_async +40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future + +Class std::__future_base::_State_baseV2 + size=32 align=8 + base size=28 base align=8 +std::__future_base::_State_baseV2 (0x0x7f13df0cd840) 0 + vptr=((& std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E) + 16u) + +Class std::__future_base + size=1 align=1 + base size=0 base align=1 +std::__future_base (0x0x7f13df0cd600) 0 empty + +Vtable for std::__future_base::_Async_state_commonV2 +std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt13__future_base21_Async_state_commonV2E) +16 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2 +24 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2 +32 (int (*)(...))std::__future_base::_Async_state_commonV2::_M_complete_async +40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future + +Class std::__future_base::_Async_state_commonV2 + size=48 align=8 + base size=44 base align=8 +std::__future_base::_Async_state_commonV2 (0x0x7f13deb7ab60) 0 + vptr=((& std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E) + 16u) + std::__future_base::_State_baseV2 (0x0x7f13deb75ba0) 0 + primary-for std::__future_base::_Async_state_commonV2 (0x0x7f13deb7ab60) + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7f13debda300) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7f13debd9618) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7f13debda2a0) 0 + primary-for QThread (0x0x7f13debd9618) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7f13debda420) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7f13debd9680) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7f13debda3c0) 0 + primary-for QThreadPool (0x0x7f13debd9680) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7f13debda480) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7f13debda5a0) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7f13debd96e8) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7f13debda540) 0 + primary-for QTimeLine (0x0x7f13debd96e8) + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7f13debda660) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7f13debd9750) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7f13debda600) 0 + primary-for QTimer (0x0x7f13debd9750) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7f13debdad20) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7f13debdacc0) 0 + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7f13de8c1300) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7f13de8c81a0) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7f13de8c12a0) 0 + primary-for QTranslator (0x0x7f13de8c81a0) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7f13de8c1420) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7f13de9daae0) 0 + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7f13de670180) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7f13de670780) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7f13de6707e0) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7f13de670ae0) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7f13de699c98) 0 + QVector (0x0x7f13de670ea0) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7f13de670f00) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7f13de7671e0) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7f13de767480) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7f13de767720) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7f13de767780) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7f13de4121e0) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7f13de412300) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7f13de412a80) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7f13de412ea0) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7f13de497360) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7f13de497420) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7f13de4974e0) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7f13de44d6e8) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7f13de497480) 0 + primary-for QGeoPositionInfoSource (0x0x7f13de44d6e8) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7f13de497720) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7f13de44d820) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7f13de4976c0) 0 + primary-for QGeoAreaMonitorSource (0x0x7f13de44d820) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7f13de44d888) 0 + QGeoShape (0x0x7f13de497780) 0 + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7f13de44db60) 0 + QGeoShape (0x0x7f13de497c60) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7f13de574060) 0 + +Class QGeoPath + size=8 align=8 + base size=8 base align=8 +QGeoPath (0x0x7f13de58e3a8) 0 + QGeoShape (0x0x7f13de5747e0) 0 + +Class QGeoPolygon + size=8 align=8 + base size=8 base align=8 +QGeoPolygon (0x0x7f13de58e5b0) 0 + QGeoShape (0x0x7f13de574ba0) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7f13de574f60) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7f13de5e4060) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7f13de58e7b8) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7f13de5e4000) 0 + primary-for QGeoSatelliteInfoSource (0x0x7f13de58e7b8) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7f13de5e4120) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7f13de5e4240) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7f13de58e820) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7f13de58e888) 0 + primary-for QNmeaPositionInfoSource (0x0x7f13de58e820) + QObject (0x0x7f13de5e41e0) 0 + primary-for QGeoPositionInfoSource (0x0x7f13de58e888) + diff --git a/tests/auto/bic/data/QtPositioning.5.11.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.11.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..0d9ef89 --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.11.0.linux-gcc-amd64.txt @@ -0,0 +1,4773 @@ +Class std::__failure_type + size=1 align=1 + base size=0 base align=1 +std::__failure_type (0x0x7f1d80fa6de0) 0 empty + +Class std::__do_is_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_destructible_impl (0x0x7f1d810875a0) 0 empty + +Class std::__do_is_nt_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nt_destructible_impl (0x0x7f1d810877e0) 0 empty + +Class std::__do_is_default_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_default_constructible_impl (0x0x7f1d81087a20) 0 empty + +Class std::__do_is_static_castable_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_static_castable_impl (0x0x7f1d81087c60) 0 empty + +Class std::__do_is_direct_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_direct_constructible_impl (0x0x7f1d81087de0) 0 empty + +Class std::__do_is_nary_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nary_constructible_impl (0x0x7f1d7ec391e0) 0 empty + +Class std::__do_common_type_impl + size=1 align=1 + base size=0 base align=1 +std::__do_common_type_impl (0x0x7f1d7ecc0960) 0 empty + +Class std::__do_member_type_wrapper + size=1 align=1 + base size=0 base align=1 +std::__do_member_type_wrapper (0x0x7f1d7ecc0a20) 0 empty + +Class std::__result_of_memfun_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_ref_impl (0x0x7f1d7ecc0d80) 0 empty + +Class std::__result_of_memfun_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_deref_impl (0x0x7f1d7ecc0e40) 0 empty + +Class std::__result_of_memobj_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_ref_impl (0x0x7f1d7ecc0f00) 0 empty + +Class std::__result_of_memobj_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_deref_impl (0x0x7f1d7ecf4000) 0 empty + +Class std::__result_of_other_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_other_impl (0x0x7f1d7ecf42a0) 0 empty + +Class std::piecewise_construct_t + size=1 align=1 + base size=0 base align=1 +std::piecewise_construct_t (0x0x7f1d7ecf4480) 0 empty + +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7f1d7ecf4900) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7f1d7ecf4960) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7f1d7eda0600) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7f1d7eda0660) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7f1d7ed052d8) 0 empty + std::input_iterator_tag (0x0x7f1d7eda06c0) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7f1d7ed05340) 0 empty + std::forward_iterator_tag (0x0x7f1d7ed053a8) 0 empty + std::input_iterator_tag (0x0x7f1d7eda0720) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7f1d7ed05410) 0 empty + std::bidirectional_iterator_tag (0x0x7f1d7ed05478) 0 empty + std::forward_iterator_tag (0x0x7f1d7ed054e0) 0 empty + std::input_iterator_tag (0x0x7f1d7eda0780) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_iter (0x0x7f1d7ede6420) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_val (0x0x7f1d7ede6480) 0 empty + +Class __gnu_cxx::__ops::_Val_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Val_less_iter (0x0x7f1d7ede64e0) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7f1d7ede6540) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7f1d7ede65a0) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7f1d7eaf60c0) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7f1d7eaf6300) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7f1d7eaf63c0) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7f1d7eaf6420) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7f1d7eaf64e0) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7f1d7eaf6540) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7f1d7eaf69c0) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7f1d7eaf6a20) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7f1d7eaf6a80) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7f1d7ed05a28) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7f1d7eaf6ae0) 0 nearly-empty + primary-for std::bad_exception (0x0x7f1d7ed05a28) + +Class std::__exception_ptr::exception_ptr + size=8 align=8 + base size=8 base align=8 +std::__exception_ptr::exception_ptr (0x0x7f1d7eaf6b40) 0 + +Vtable for std::nested_exception +std::nested_exception::_ZTVSt16nested_exception: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16nested_exception) +16 (int (*)(...))std::nested_exception::~nested_exception +24 (int (*)(...))std::nested_exception::~nested_exception + +Class std::nested_exception + size=16 align=8 + base size=16 base align=8 +std::nested_exception (0x0x7f1d7eaf6ba0) 0 + vptr=((& std::nested_exception::_ZTVSt16nested_exception) + 16u) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7f1d7ed05c30) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7f1d7ec2c000) 0 nearly-empty + primary-for std::bad_alloc (0x0x7f1d7ed05c30) + +Vtable for std::bad_array_new_length +std::bad_array_new_length::_ZTVSt20bad_array_new_length: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt20bad_array_new_length) +16 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +24 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +32 (int (*)(...))std::bad_array_new_length::what + +Class std::bad_array_new_length + size=8 align=8 + base size=8 base align=8 +std::bad_array_new_length (0x0x7f1d7ed05c98) 0 nearly-empty + vptr=((& std::bad_array_new_length::_ZTVSt20bad_array_new_length) + 16u) + std::bad_alloc (0x0x7f1d7ed05d00) 0 nearly-empty + primary-for std::bad_array_new_length (0x0x7f1d7ed05c98) + std::exception (0x0x7f1d7ec2c060) 0 nearly-empty + primary-for std::bad_alloc (0x0x7f1d7ed05d00) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7f1d7ec2c0c0) 0 empty + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7f1d7ec2ccc0) 0 + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7f1d7ea299c0) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7f1d7ea29a20) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7f1d7e733900) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7f1d7e733960) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7f1d7e733a20) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7f1d7e733a80) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7f1d7e733ae0) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7f1d7e733b40) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7f1d7e733c60) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7f1d7e733cc0) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7f1d7e49a120) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7f1d7e49a180) 0 + +Class std::_Hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Hash_impl (0x0x7f1d7e291960) 0 empty + +Class std::_Fnv_hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Fnv_hash_impl (0x0x7f1d7e2919c0) 0 empty + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7f1d7e2c5960) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7f1d7e0b9780) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7f1d7e2bfa90) 0 + std::iterator (0x0x7f1d7e0b9840) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7f1d7e2bfaf8) 0 + std::_Bit_iterator_base (0x0x7f1d7e2bfb60) 0 + std::iterator (0x0x7f1d7e0b98a0) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7f1d7e2bfbc8) 0 + std::_Bit_iterator_base (0x0x7f1d7e2bfc30) 0 + std::iterator (0x0x7f1d7e0b9900) 0 empty + +Class std::random_device + size=5000 align=8 + base size=5000 base align=8 +std::random_device (0x0x7f1d7defc720) 0 + +Class std::bernoulli_distribution::param_type + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution::param_type (0x0x7f1d7dfe74e0) 0 + +Class std::bernoulli_distribution + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution (0x0x7f1d7dfe7480) 0 + +Class std::seed_seq + size=24 align=8 + base size=24 base align=8 +std::seed_seq (0x0x7f1d7dd91480) 0 + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7f1d7cac7f60) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7f1d7c981000) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7f1d7ca0ea80) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7f1d7ca0eae0) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7f1d7ca0eb40) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7f1d7ca0eba0) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7f1d7ca0ee40) 0 + +Class std::__atomic_flag_base + size=1 align=1 + base size=1 base align=1 +std::__atomic_flag_base (0x0x7f1d7c67c3c0) 0 + +Class std::atomic_flag + size=1 align=1 + base size=1 base align=1 +std::atomic_flag (0x0x7f1d7ca17958) 0 + std::__atomic_flag_base (0x0x7f1d7c67c420) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7f1d7c2930d0) 0 + QAtomicInteger (0x0x7f1d7c293138) 0 + QBasicAtomicInteger (0x0x7f1d7c4a7b40) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7f1d7be79120) 0 empty + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7f1d7beec180) 0 + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7f1d7beec2a0) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7f1d7c070b60) 0 + QGenericArgument (0x0x7f1d7beec300) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7f1d7beec480) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7f1d7beec540) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7f1d7bfca5a0) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7f1d7bfca600) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7f1d7bfca8a0) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7f1d7bfca900) 0 + +Class QtPrivate::QContainerImplHelper + size=1 align=1 + base size=0 base align=1 +QtPrivate::QContainerImplHelper (0x0x7f1d7bfcac60) 0 empty + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7f1d7bfcacc0) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7f1d7bfcad20) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7f1d7bfcad80) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7f1d7bfcade0) 0 + +Class std::__cow_string + size=8 align=8 + base size=8 base align=8 +std::__cow_string (0x0x7f1d7bd661e0) 0 + +Vtable for std::logic_error +std::logic_error::_ZTVSt11logic_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11logic_error) +16 (int (*)(...))std::logic_error::~logic_error +24 (int (*)(...))std::logic_error::~logic_error +32 (int (*)(...))std::logic_error::what + +Class std::logic_error + size=16 align=8 + base size=16 base align=8 +std::logic_error (0x0x7f1d7bd700d0) 0 + vptr=((& std::logic_error::_ZTVSt11logic_error) + 16u) + std::exception (0x0x7f1d7bd662a0) 0 nearly-empty + primary-for std::logic_error (0x0x7f1d7bd700d0) + +Vtable for std::domain_error +std::domain_error::_ZTVSt12domain_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12domain_error) +16 (int (*)(...))std::domain_error::~domain_error +24 (int (*)(...))std::domain_error::~domain_error +32 (int (*)(...))std::logic_error::what + +Class std::domain_error + size=16 align=8 + base size=16 base align=8 +std::domain_error (0x0x7f1d7bd70138) 0 + vptr=((& std::domain_error::_ZTVSt12domain_error) + 16u) + std::logic_error (0x0x7f1d7bd701a0) 0 + primary-for std::domain_error (0x0x7f1d7bd70138) + std::exception (0x0x7f1d7bd66300) 0 nearly-empty + primary-for std::logic_error (0x0x7f1d7bd701a0) + +Vtable for std::invalid_argument +std::invalid_argument::_ZTVSt16invalid_argument: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16invalid_argument) +16 (int (*)(...))std::invalid_argument::~invalid_argument +24 (int (*)(...))std::invalid_argument::~invalid_argument +32 (int (*)(...))std::logic_error::what + +Class std::invalid_argument + size=16 align=8 + base size=16 base align=8 +std::invalid_argument (0x0x7f1d7bd70208) 0 + vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16u) + std::logic_error (0x0x7f1d7bd70270) 0 + primary-for std::invalid_argument (0x0x7f1d7bd70208) + std::exception (0x0x7f1d7bd66360) 0 nearly-empty + primary-for std::logic_error (0x0x7f1d7bd70270) + +Vtable for std::length_error +std::length_error::_ZTVSt12length_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12length_error) +16 (int (*)(...))std::length_error::~length_error +24 (int (*)(...))std::length_error::~length_error +32 (int (*)(...))std::logic_error::what + +Class std::length_error + size=16 align=8 + base size=16 base align=8 +std::length_error (0x0x7f1d7bd702d8) 0 + vptr=((& std::length_error::_ZTVSt12length_error) + 16u) + std::logic_error (0x0x7f1d7bd70340) 0 + primary-for std::length_error (0x0x7f1d7bd702d8) + std::exception (0x0x7f1d7bd663c0) 0 nearly-empty + primary-for std::logic_error (0x0x7f1d7bd70340) + +Vtable for std::out_of_range +std::out_of_range::_ZTVSt12out_of_range: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12out_of_range) +16 (int (*)(...))std::out_of_range::~out_of_range +24 (int (*)(...))std::out_of_range::~out_of_range +32 (int (*)(...))std::logic_error::what + +Class std::out_of_range + size=16 align=8 + base size=16 base align=8 +std::out_of_range (0x0x7f1d7bd703a8) 0 + vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16u) + std::logic_error (0x0x7f1d7bd70410) 0 + primary-for std::out_of_range (0x0x7f1d7bd703a8) + std::exception (0x0x7f1d7bd66420) 0 nearly-empty + primary-for std::logic_error (0x0x7f1d7bd70410) + +Vtable for std::runtime_error +std::runtime_error::_ZTVSt13runtime_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13runtime_error) +16 (int (*)(...))std::runtime_error::~runtime_error +24 (int (*)(...))std::runtime_error::~runtime_error +32 (int (*)(...))std::runtime_error::what + +Class std::runtime_error + size=16 align=8 + base size=16 base align=8 +std::runtime_error (0x0x7f1d7bd70478) 0 + vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16u) + std::exception (0x0x7f1d7bd66480) 0 nearly-empty + primary-for std::runtime_error (0x0x7f1d7bd70478) + +Vtable for std::range_error +std::range_error::_ZTVSt11range_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11range_error) +16 (int (*)(...))std::range_error::~range_error +24 (int (*)(...))std::range_error::~range_error +32 (int (*)(...))std::runtime_error::what + +Class std::range_error + size=16 align=8 + base size=16 base align=8 +std::range_error (0x0x7f1d7bd704e0) 0 + vptr=((& std::range_error::_ZTVSt11range_error) + 16u) + std::runtime_error (0x0x7f1d7bd70548) 0 + primary-for std::range_error (0x0x7f1d7bd704e0) + std::exception (0x0x7f1d7bd664e0) 0 nearly-empty + primary-for std::runtime_error (0x0x7f1d7bd70548) + +Vtable for std::overflow_error +std::overflow_error::_ZTVSt14overflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt14overflow_error) +16 (int (*)(...))std::overflow_error::~overflow_error +24 (int (*)(...))std::overflow_error::~overflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::overflow_error + size=16 align=8 + base size=16 base align=8 +std::overflow_error (0x0x7f1d7bd705b0) 0 + vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16u) + std::runtime_error (0x0x7f1d7bd70618) 0 + primary-for std::overflow_error (0x0x7f1d7bd705b0) + std::exception (0x0x7f1d7bd66540) 0 nearly-empty + primary-for std::runtime_error (0x0x7f1d7bd70618) + +Vtable for std::underflow_error +std::underflow_error::_ZTVSt15underflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt15underflow_error) +16 (int (*)(...))std::underflow_error::~underflow_error +24 (int (*)(...))std::underflow_error::~underflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::underflow_error + size=16 align=8 + base size=16 base align=8 +std::underflow_error (0x0x7f1d7bd70680) 0 + vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16u) + std::runtime_error (0x0x7f1d7bd706e8) 0 + primary-for std::underflow_error (0x0x7f1d7bd70680) + std::exception (0x0x7f1d7bd665a0) 0 nearly-empty + primary-for std::runtime_error (0x0x7f1d7bd706e8) + +Vtable for std::_V2::error_category +std::_V2::error_category::_ZTVNSt3_V214error_categoryE: 10u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt3_V214error_categoryE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))std::_V2::error_category::_M_message +48 (int (*)(...))__cxa_pure_virtual +56 (int (*)(...))std::_V2::error_category::default_error_condition +64 (int (*)(...))std::_V2::error_category::equivalent +72 (int (*)(...))std::_V2::error_category::equivalent + +Class std::_V2::error_category + size=8 align=8 + base size=8 base align=8 +std::_V2::error_category (0x0x7f1d7bd66720) 0 nearly-empty + vptr=((& std::_V2::error_category::_ZTVNSt3_V214error_categoryE) + 16u) + +Class std::error_code + size=16 align=8 + base size=16 base align=8 +std::error_code (0x0x7f1d7bd66960) 0 + +Class std::error_condition + size=16 align=8 + base size=16 base align=8 +std::error_condition (0x0x7f1d7bd66ae0) 0 + +Vtable for std::system_error +std::system_error::_ZTVSt12system_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12system_error) +16 (int (*)(...))std::system_error::~system_error +24 (int (*)(...))std::system_error::~system_error +32 (int (*)(...))std::runtime_error::what + +Class std::system_error + size=32 align=8 + base size=32 base align=8 +std::system_error (0x0x7f1d7bd70bc8) 0 + vptr=((& std::system_error::_ZTVSt12system_error) + 16u) + std::runtime_error (0x0x7f1d7bd70c30) 0 + primary-for std::system_error (0x0x7f1d7bd70bc8) + std::exception (0x0x7f1d7bd66d20) 0 nearly-empty + primary-for std::runtime_error (0x0x7f1d7bd70c30) + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=32 align=8 + base size=32 base align=8 +std::ios_base::failure (0x0x7f1d7be05820) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16u) + std::system_error (0x0x7f1d7be05888) 0 + primary-for std::ios_base::failure (0x0x7f1d7be05820) + std::runtime_error (0x0x7f1d7be058f0) 0 + primary-for std::system_error (0x0x7f1d7be05888) + std::exception (0x0x7f1d7ba37060) 0 nearly-empty + primary-for std::runtime_error (0x0x7f1d7be058f0) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7f1d7ba370c0) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7f1d7ba37120) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7f1d7ba37180) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7f1d7ba37000) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7f1d7ba37900) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7f1d7bbbb000) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7f1d7b6d91a0 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7f1d7b6d9270 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7f1d7b6d9618 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7f1d7b6d96e8 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7f1d7b9b4840) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7f1d7b9b48a0) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7f1d7b816c00) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7f1d7b816f60) 0 + +Class QStringView + size=16 align=8 + base size=16 base align=8 +QStringView (0x0x7f1d7b50e420) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7f1d7b5962a0) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7f1d7b596900) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7f1d7b5968a0) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7f1d7b37b9c0) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7f1d7b117600) 0 + +Class QtPrivate::QHashCombine + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombine (0x0x7f1d7af242a0) 0 empty + +Class QtPrivate::QHashCombineCommutative + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombineCommutative (0x0x7f1d7af24300) 0 empty + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7f1d7af24360) 0 + +Class QListData::NotArrayCompatibleLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotArrayCompatibleLayout (0x0x7f1d7af24720) 0 empty + +Class QListData::NotIndirectLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotIndirectLayout (0x0x7f1d7af24780) 0 empty + +Class QListData::ArrayCompatibleLayout + size=1 align=1 + base size=1 base align=1 +QListData::ArrayCompatibleLayout (0x0x7f1d7af371a0) 0 empty + QListData::NotIndirectLayout (0x0x7f1d7af247e0) 0 empty + +Class QListData::InlineWithPaddingLayout + size=1 align=1 + base size=1 base align=1 +QListData::InlineWithPaddingLayout (0x0x7f1d7afeda10) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7f1d7af24840) 0 empty + QListData::NotIndirectLayout (0x0x7f1d7af248a0) 0 empty + +Class QListData::IndirectLayout + size=1 align=1 + base size=1 base align=1 +QListData::IndirectLayout (0x0x7f1d7af37208) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7f1d7af24900) 0 empty + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7f1d7af24960) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7f1d7af246c0) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7f1d7af24de0) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7f1d7adb5060) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7f1d7adb5000) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7f1d7ada1c30) 0 + QList (0x0x7f1d7ada1c98) 0 + QListSpecialMethods (0x0x7f1d7adb5240) 0 empty + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7f1d7adb5660) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7f1d7aad4240) 0 + +Class std::allocator_arg_t + size=1 align=1 + base size=0 base align=1 +std::allocator_arg_t (0x0x7f1d7aad48a0) 0 empty + +Class std::__uses_alloc_base + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc_base (0x0x7f1d7aad4a20) 0 empty + +Class std::__uses_alloc0::_Sink + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc0::_Sink (0x0x7f1d7aad4ae0) 0 empty + +Class std::__uses_alloc0 + size=1 align=1 + base size=1 base align=1 +std::__uses_alloc0 (0x0x7f1d7ae205b0) 0 + std::__uses_alloc_base (0x0x7f1d7aad4a80) 0 empty + +Class std::_Swallow_assign + size=1 align=1 + base size=0 base align=1 +std::_Swallow_assign (0x0x7f1d7a8f9b40) 0 empty + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7f1d7a8f9d80) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7f1d7a8f9e40) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7f1d7a8f9f60) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7f1d7aa22120) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7f1d7aa22540) 0 + +Class QtMetaTypePrivate::VectorBoolElements + size=1 align=1 + base size=0 base align=1 +QtMetaTypePrivate::VectorBoolElements (0x0x7f1d7aa22660) 0 empty + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7f1d7a73f000) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7f1d7a73f420) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7f1d7a73f720) 0 + +Class std::chrono::_V2::system_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::system_clock (0x0x7f1d7a58cf60) 0 empty + +Class std::chrono::_V2::steady_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::steady_clock (0x0x7f1d7a30ade0) 0 empty + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7f1d7a30ae40) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7f1d7a37d060) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7f1d7a37d000) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7f1d7a415300) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7f1d7a415360) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7f1d7a415420) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7f1d7a4117b8) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7f1d7a4153c0) 0 + primary-for QAbstractAnimation (0x0x7f1d7a4117b8) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7f1d7a4154e0) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7f1d7a411820) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7f1d7a415480) 0 + primary-for QAnimationDriver (0x0x7f1d7a411820) + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7f1d7a4155a0) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7f1d7a411888) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7f1d7a415540) 0 + primary-for QEventLoop (0x0x7f1d7a411888) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7f1d7a415780) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7f1d7a415840) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7f1d7a4158a0) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7f1d7a4119c0) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7f1d7a4157e0) 0 + primary-for QAbstractEventDispatcher (0x0x7f1d7a4119c0) + +Vtable for std::type_info +std::type_info::_ZTVSt9type_info: 8u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9type_info) +16 (int (*)(...))std::type_info::~type_info +24 (int (*)(...))std::type_info::~type_info +32 (int (*)(...))std::type_info::__is_pointer_p +40 (int (*)(...))std::type_info::__is_function_p +48 (int (*)(...))std::type_info::__do_catch +56 (int (*)(...))std::type_info::__do_upcast + +Class std::type_info + size=16 align=8 + base size=16 base align=8 +std::type_info (0x0x7f1d7a415b40) 0 + vptr=((& std::type_info::_ZTVSt9type_info) + 16u) + +Vtable for std::bad_cast +std::bad_cast::_ZTVSt8bad_cast: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8bad_cast) +16 (int (*)(...))std::bad_cast::~bad_cast +24 (int (*)(...))std::bad_cast::~bad_cast +32 (int (*)(...))std::bad_cast::what + +Class std::bad_cast + size=8 align=8 + base size=8 base align=8 +std::bad_cast (0x0x7f1d7a411bc8) 0 nearly-empty + vptr=((& std::bad_cast::_ZTVSt8bad_cast) + 16u) + std::exception (0x0x7f1d7a415ba0) 0 nearly-empty + primary-for std::bad_cast (0x0x7f1d7a411bc8) + +Vtable for std::bad_typeid +std::bad_typeid::_ZTVSt10bad_typeid: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt10bad_typeid) +16 (int (*)(...))std::bad_typeid::~bad_typeid +24 (int (*)(...))std::bad_typeid::~bad_typeid +32 (int (*)(...))std::bad_typeid::what + +Class std::bad_typeid + size=8 align=8 + base size=8 base align=8 +std::bad_typeid (0x0x7f1d7a411c30) 0 nearly-empty + vptr=((& std::bad_typeid::_ZTVSt10bad_typeid) + 16u) + std::exception (0x0x7f1d7a415c00) 0 nearly-empty + primary-for std::bad_typeid (0x0x7f1d7a411c30) + +Vtable for std::bad_function_call +std::bad_function_call::_ZTVSt17bad_function_call: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt17bad_function_call) +16 (int (*)(...))std::bad_function_call::~bad_function_call +24 (int (*)(...))std::bad_function_call::~bad_function_call +32 (int (*)(...))std::bad_function_call::what + +Class std::bad_function_call + size=8 align=8 + base size=8 base align=8 +std::bad_function_call (0x0x7f1d7a164ea0) 0 nearly-empty + vptr=((& std::bad_function_call::_ZTVSt17bad_function_call) + 16u) + std::exception (0x0x7f1d7a1d6cc0) 0 nearly-empty + primary-for std::bad_function_call (0x0x7f1d7a164ea0) + +Class std::_Nocopy_types + size=16 align=8 + base size=16 base align=8 +std::_Nocopy_types (0x0x7f1d7a1d6d80) 0 + +Class std::_Any_data + size=16 align=8 + base size=16 base align=8 +std::_Any_data (0x0x7f1d7a1d6de0) 0 + +Class std::_Function_base + size=24 align=8 + base size=24 base align=8 +std::_Function_base (0x0x7f1d7a1d6f00) 0 + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7f1d79e7f420) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7f1d79e7f960) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7f1d79e7fd20) 0 + +Class QHashData + size=48 align=8 + base size=44 base align=8 +QHashData (0x0x7f1d79e7fcc0) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7f1d79e7fd80) 0 empty + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7f1d7a012660) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7f1d7a012720) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7f1d7a0126c0) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7f1d7a012780) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7f1d7a012600) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7f1d79a692a0) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7f1d79a69900) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7f1d79a698a0) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7f1d79a699c0) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7f1d79a69960) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7f1d79b92cc0) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7f1d798d83c0) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7f1d79974ae0) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7f1d79975c30) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7f1d79974a80) 0 + primary-for QAbstractItemModel (0x0x7f1d79975c30) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7f1d799d53c0) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractTableModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7f1d799d4340) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7f1d799d43a8) 0 + primary-for QAbstractTableModel (0x0x7f1d799d4340) + QObject (0x0x7f1d799d5360) 0 + primary-for QAbstractItemModel (0x0x7f1d799d43a8) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7f1d799d5480) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractListModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7f1d799d4410) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7f1d799d4478) 0 + primary-for QAbstractListModel (0x0x7f1d799d4410) + QObject (0x0x7f1d799d5420) 0 + primary-for QAbstractItemModel (0x0x7f1d799d4478) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7f1d799d5720) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7f1d799d57e0) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QAbstractProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7f1d799d45b0) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7f1d799d4618) 0 + primary-for QAbstractProxyModel (0x0x7f1d799d45b0) + QObject (0x0x7f1d799d5780) 0 + primary-for QAbstractItemModel (0x0x7f1d799d4618) + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7f1d799d58a0) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7f1d799d4680) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7f1d799d5840) 0 + primary-for QAbstractState (0x0x7f1d799d4680) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7f1d799d5960) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7f1d799d46e8) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7f1d799d5900) 0 + primary-for QAbstractTransition (0x0x7f1d799d46e8) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7f1d799d5a20) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7f1d799d4750) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7f1d799d47b8) 0 + primary-for QAnimationGroup (0x0x7f1d799d4750) + QObject (0x0x7f1d799d59c0) 0 + primary-for QAbstractAnimation (0x0x7f1d799d47b8) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7f1d79743780) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7f1d79743a20) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7f1d79743ae0) 0 + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7f1d79743de0) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7f1d799d4e38) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7f1d79743d80) 0 + primary-for QIODevice (0x0x7f1d799d4e38) + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7f1d79822060) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7f1d799d4f70) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7f1d79821000) 0 + primary-for QBuffer (0x0x7f1d799d4f70) + QObject (0x0x7f1d79822000) 0 + primary-for QIODevice (0x0x7f1d79821000) + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7f1d79822120) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7f1d798220c0) 0 + +Class QStaticByteArrayMatcherBase::Skiptable + size=256 align=1 + base size=256 base align=1 +QStaticByteArrayMatcherBase::Skiptable (0x0x7f1d79822240) 0 + +Class QStaticByteArrayMatcherBase + size=256 align=16 + base size=256 base align=16 +QStaticByteArrayMatcherBase (0x0x7f1d798221e0) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7f1d79822420) 0 + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7f1d79822600) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7f1d79822c00) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7f1d79822cc0) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7f1d7961dcc0) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7f1d7925b180) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7f1d792304e0) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7f1d7925b1e0) 0 + primary-for QTimerEvent (0x0x7f1d792304e0) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7f1d79230548) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7f1d7925b240) 0 + primary-for QChildEvent (0x0x7f1d79230548) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7f1d79230a90) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7f1d7925b720) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7f1d79230a90) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7f1d79230af8) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7f1d7925b780) 0 + primary-for QDeferredDeleteEvent (0x0x7f1d79230af8) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7f1d7925b840) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7f1d79230b60) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7f1d7925b7e0) 0 + primary-for QCoreApplication (0x0x7f1d79230b60) + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7f1d7925b8a0) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7f1d7925b900) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7f1d7925bf00) 0 + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7f1d7925bf60) 0 + +Class QtPrivate::StreamStateSaver + size=16 align=8 + base size=12 base align=8 +QtPrivate::StreamStateSaver (0x0x7f1d7936a060) 0 + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7f1d7936a540) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7f1d7936a7e0) 0 + +Class QDateTime::ShortData + size=8 align=8 + base size=8 base align=8 +QDateTime::ShortData (0x0x7f1d7936af60) 0 + +Class QDateTime::Data + size=8 align=8 + base size=8 base align=8 +QDateTime::Data (0x0x7f1d79410000) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7f1d7936af00) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7f1d790bf120) 0 + +Class QDeadlineTimer + size=16 align=8 + base size=16 base align=8 +QDeadlineTimer (0x0x7f1d790bf600) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7f1d791f85a0) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7f1d791f8840) 0 + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7f1d791f8a80) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7f1d791f8c00) 0 + +Class QDebug::Stream + size=80 align=8 + base size=76 base align=8 +QDebug::Stream (0x0x7f1d78fb6180) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7f1d78fb6120) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7f1d78c80660) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7f1d78c80720) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7f1d78d588a0) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7f1d78d5cc98) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7f1d78d5cd00) 0 + primary-for QFileDevice (0x0x7f1d78d5cc98) + QObject (0x0x7f1d78d58840) 0 + primary-for QIODevice (0x0x7f1d78d5cd00) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7f1d78d58ae0) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7f1d78d5ce38) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7f1d78d5cea0) 0 + primary-for QFile (0x0x7f1d78d5ce38) + QIODevice (0x0x7f1d78d5cf08) 0 + primary-for QFileDevice (0x0x7f1d78d5cea0) + QObject (0x0x7f1d78d58a80) 0 + primary-for QIODevice (0x0x7f1d78d5cf08) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7f1d78d58cc0) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7f1d78e19120) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7f1d78e19720) 0 + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7f1d78e19960) 0 + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7f1d78b6ed80) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7f1d78b70b60) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7f1d78b70bc8) 0 + primary-for QEventTransition (0x0x7f1d78b70b60) + QObject (0x0x7f1d78b6ed20) 0 + primary-for QAbstractTransition (0x0x7f1d78b70bc8) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7f1d78b70c30) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7f1d78b6ede0) 0 nearly-empty + primary-for QException (0x0x7f1d78b70c30) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7f1d78b70c98) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7f1d78b70d00) 0 nearly-empty + primary-for QUnhandledException (0x0x7f1d78b70c98) + std::exception (0x0x7f1d78b6ee40) 0 nearly-empty + primary-for QException (0x0x7f1d78b70d00) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7f1d78b6eea0) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7f1d78b6ef60) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7f1d78bea000) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7f1d78bea120) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7f1d78b70d68) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7f1d78bea0c0) 0 + primary-for QFileSelector (0x0x7f1d78b70d68) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7f1d78bea1e0) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7f1d78b70dd0) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7f1d78bea180) 0 + primary-for QFileSystemWatcher (0x0x7f1d78b70dd0) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7f1d78bea2a0) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7f1d78b70e38) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7f1d78b70ea0) 0 + primary-for QFinalState (0x0x7f1d78b70e38) + QObject (0x0x7f1d78bea240) 0 + primary-for QAbstractState (0x0x7f1d78b70ea0) + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7f1d78bea300) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7f1d78bea360) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7f1d7889b000) 0 + QBasicMutex (0x0x7f1d78bea540) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7f1d78bea5a0) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7f1d78bea600) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7f1d78bea660) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7f1d78bea780) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7f1d78913000) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7f1d789137e0) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 0u +48 0u +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7f1d78991138) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7f1d78913780) 0 + primary-for QFutureWatcherBase (0x0x7f1d78991138) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7f1d78913de0) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7f1d78991a28) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7f1d78991a90) 0 + primary-for QHistoryState (0x0x7f1d78991a28) + QObject (0x0x7f1d78913d80) 0 + primary-for QAbstractState (0x0x7f1d78991a90) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7f1d78913ea0) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7f1d78991af8) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7f1d78991b60) 0 + primary-for QIdentityProxyModel (0x0x7f1d78991af8) + QAbstractItemModel (0x0x7f1d78991bc8) 0 + primary-for QAbstractProxyModel (0x0x7f1d78991b60) + QObject (0x0x7f1d78913e40) 0 + primary-for QAbstractItemModel (0x0x7f1d78991bc8) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7f1d78913f00) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7f1d78658600) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7f1d7865b410) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7f1d786585a0) 0 + primary-for QItemSelectionModel (0x0x7f1d7865b410) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7f1d7865b618) 0 + QList (0x0x7f1d7865b680) 0 + QListSpecialMethods (0x0x7f1d78658900) 0 empty + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7f1d78658de0) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7f1d78465540) 0 + +Class QJsonValuePtr + size=24 align=8 + base size=24 base align=8 +QJsonValuePtr (0x0x7f1d78465a80) 0 + +Class QJsonValueRefPtr + size=16 align=8 + base size=16 base align=8 +QJsonValueRefPtr (0x0x7f1d78465ae0) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7f1d78465cc0) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7f1d78465d20) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7f1d78465c60) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7f1d78566f60) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7f1d785d9000) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7f1d785d9660) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7f1d785d96c0) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7f1d785d9600) 0 + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7f1d78284960) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7f1d782859c0) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7f1d78284900) 0 + primary-for QLibrary (0x0x7f1d782859c0) + +Class QVersionNumber::SegmentStorage + size=8 align=8 + base size=8 base align=8 +QVersionNumber::SegmentStorage (0x0x7f1d78315060) 0 + +Class QVersionNumber + size=8 align=8 + base size=8 base align=8 +QVersionNumber (0x0x7f1d78284b40) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7f1d78315540) 0 empty + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7f1d783155a0) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7f1d78315840) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7f1d78315ae0) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7f1d7800f480) 0 + +Class QLinkedListData + size=32 align=8 + base size=25 base align=8 +QLinkedListData (0x0x7f1d7800fde0) 0 + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7f1d780b6180) 0 + +Class QLoggingCategory::AtomicBools + size=4 align=1 + base size=4 base align=1 +QLoggingCategory::AtomicBools (0x0x7f1d780b6300) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7f1d780b62a0) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7f1d780b6480) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7f1d780b6720) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7f1d780b6d80) 0 + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7f1d780b6de0) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7f1d77e7a420) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7f1d77e7a720) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7f1d77e7a780) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7f1d77e7aa80) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7f1d7820dc30) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7f1d77e7aa20) 0 + primary-for QMimeData (0x0x7f1d7820dc30) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7f1d77e7aae0) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7f1d77e7ade0) 0 + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7f1d77e7aea0) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7f1d7820de38) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7f1d77e7ae40) 0 + primary-for QObjectCleanupHandler (0x0x7f1d7820de38) + +Class QOperatingSystemVersion + size=16 align=4 + base size=16 base align=4 +QOperatingSystemVersion (0x0x7f1d77e7af00) 0 + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7f1d77f086c0) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7f1d77f0b548) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7f1d77f0b5b0) 0 + primary-for QParallelAnimationGroup (0x0x7f1d77f0b548) + QAbstractAnimation (0x0x7f1d77f0b618) 0 + primary-for QAnimationGroup (0x0x7f1d77f0b5b0) + QObject (0x0x7f1d77f08660) 0 + primary-for QAbstractAnimation (0x0x7f1d77f0b618) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7f1d77f08780) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7f1d77f0b680) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7f1d77f0b6e8) 0 + primary-for QPauseAnimation (0x0x7f1d77f0b680) + QObject (0x0x7f1d77f08720) 0 + primary-for QAbstractAnimation (0x0x7f1d77f0b6e8) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7f1d77f08960) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7f1d77f08c60) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7f1d77f0b8f0) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7f1d77f08c00) 0 + primary-for QPluginLoader (0x0x7f1d77f0b8f0) + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7f1d77f08cc0) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7f1d77f8a3c0) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7f1d77f0bf70) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7f1d77f96000) 0 + primary-for QProcess (0x0x7f1d77f0bf70) + QObject (0x0x7f1d77f8a360) 0 + primary-for QIODevice (0x0x7f1d77f96000) + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7f1d77f8a480) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7f1d77f96068) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7f1d77f960d0) 0 + primary-for QVariantAnimation (0x0x7f1d77f96068) + QObject (0x0x7f1d77f8a420) 0 + primary-for QAbstractAnimation (0x0x7f1d77f960d0) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7f1d77f8a540) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7f1d77f961a0) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7f1d77f96208) 0 + primary-for QPropertyAnimation (0x0x7f1d77f961a0) + QAbstractAnimation (0x0x7f1d77f96270) 0 + primary-for QVariantAnimation (0x0x7f1d77f96208) + QObject (0x0x7f1d77f8a4e0) 0 + primary-for QAbstractAnimation (0x0x7f1d77f96270) + +Class QRandomGenerator::Storage + size=2504 align=8 + base size=2504 base align=8 +QRandomGenerator::Storage (0x0x7f1d77f8a660) 0 + +Class QRandomGenerator + size=2512 align=8 + base size=2512 base align=8 +QRandomGenerator (0x0x7f1d77f8a600) 0 + +Class QRandomGenerator64 + size=2512 align=8 + base size=2512 base align=8 +QRandomGenerator64 (0x0x7f1d77c78548) 0 + QRandomGenerator (0x0x7f1d77c76600) 0 + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7f1d77c766c0) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7f1d77c76960) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7f1d77c76a20) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7f1d77c76ae0) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7f1d77c76d80) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7f1d77d90060) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7f1d77d90300) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7f1d77d905a0) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7f1d77d90ba0) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7f1d77d90ea0) 0 + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7f1d77b961e0) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7f1d77b96360) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7f1d77a81750) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7f1d77a817b8) 0 + primary-for QSaveFile (0x0x7f1d77a81750) + QIODevice (0x0x7f1d77a81820) 0 + primary-for QFileDevice (0x0x7f1d77a817b8) + QObject (0x0x7f1d77b96300) 0 + primary-for QIODevice (0x0x7f1d77a81820) + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7f1d77b96420) 0 + +Class QSemaphoreReleaser + size=16 align=8 + base size=12 base align=8 +QSemaphoreReleaser (0x0x7f1d77b965a0) 0 + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7f1d778e3ba0) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7f1d778f50d0) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7f1d778f5138) 0 + primary-for QSequentialAnimationGroup (0x0x7f1d778f50d0) + QAbstractAnimation (0x0x7f1d778f51a0) 0 + primary-for QAnimationGroup (0x0x7f1d778f5138) + QObject (0x0x7f1d778e3b40) 0 + primary-for QAbstractAnimation (0x0x7f1d778f51a0) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7f1d778e3c60) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7f1d778f5208) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7f1d778e3c00) 0 + primary-for QSettings (0x0x7f1d778f5208) + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7f1d778e3d20) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7f1d778f5270) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7f1d778e3cc0) 0 + primary-for QSharedMemory (0x0x7f1d778f5270) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7f1d778e3de0) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7f1d778f52d8) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7f1d778e3d80) 0 + primary-for QSignalMapper (0x0x7f1d778f52d8) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7f1d778e3ea0) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7f1d778f5340) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7f1d778f53a8) 0 + primary-for QSignalTransition (0x0x7f1d778f5340) + QObject (0x0x7f1d778e3e40) 0 + primary-for QAbstractTransition (0x0x7f1d778f53a8) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7f1d778e3f60) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7f1d778f5410) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7f1d778e3f00) 0 + primary-for QSocketNotifier (0x0x7f1d778f5410) + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7f1d77976060) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7f1d778f5478) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7f1d778f54e0) 0 + primary-for QSortFilterProxyModel (0x0x7f1d778f5478) + QAbstractItemModel (0x0x7f1d778f5548) 0 + primary-for QAbstractProxyModel (0x0x7f1d778f54e0) + QObject (0x0x7f1d77976000) 0 + primary-for QAbstractItemModel (0x0x7f1d778f5548) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7f1d77976120) 0 empty + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7f1d77976360) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7f1d778f56e8) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7f1d778f5750) 0 + primary-for QState (0x0x7f1d778f56e8) + QObject (0x0x7f1d77976300) 0 + primary-for QAbstractState (0x0x7f1d778f5750) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7f1d77976480) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7f1d778f58f0) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7f1d779764e0) 0 + primary-for QStateMachine::SignalEvent (0x0x7f1d778f58f0) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7f1d778f5958) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7f1d77976540) 0 + primary-for QStateMachine::WrappedEvent (0x0x7f1d778f5958) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7f1d778f57b8) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7f1d778f5820) 0 + primary-for QStateMachine (0x0x7f1d778f57b8) + QAbstractState (0x0x7f1d778f5888) 0 + primary-for QState (0x0x7f1d778f5820) + QObject (0x0x7f1d77976420) 0 + primary-for QAbstractState (0x0x7f1d778f5888) + +Class QStorageInfo + size=8 align=8 + base size=8 base align=8 +QStorageInfo (0x0x7f1d779765a0) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7f1d7765c4e0) 0 empty + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7f1d776e98a0) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7f1d776ed958) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7f1d776ed9c0) 0 + primary-for QStringListModel (0x0x7f1d776ed958) + QAbstractItemModel (0x0x7f1d776eda28) 0 + primary-for QAbstractListModel (0x0x7f1d776ed9c0) + QObject (0x0x7f1d776e9840) 0 + primary-for QAbstractItemModel (0x0x7f1d776eda28) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7f1d776e9900) 0 + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7f1d776e99c0) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7f1d776e9ae0) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7f1d776eda90) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7f1d776edaf8) 0 + primary-for QTemporaryFile (0x0x7f1d776eda90) + QFileDevice (0x0x7f1d776edb60) 0 + primary-for QFile (0x0x7f1d776edaf8) + QIODevice (0x0x7f1d776edbc8) 0 + primary-for QFileDevice (0x0x7f1d776edb60) + QObject (0x0x7f1d776e9a80) 0 + primary-for QIODevice (0x0x7f1d776edbc8) + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7f1d776e9b40) 0 + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7f1d776e9d80) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 0u +64 0u + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7f1d776e9d20) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7f1d776e9f60) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7f1d777d7000) 0 + +Class std::__mutex_base + size=40 align=8 + base size=40 base align=8 +std::__mutex_base (0x0x7f1d777d7060) 0 + +Class std::__recursive_mutex_base + size=40 align=8 + base size=40 base align=8 +std::__recursive_mutex_base (0x0x7f1d777d70c0) 0 + +Class std::mutex + size=40 align=8 + base size=40 base align=8 +std::mutex (0x0x7f1d776eddd0) 0 + std::__mutex_base (0x0x7f1d777d7120) 0 + +Class std::recursive_mutex + size=40 align=8 + base size=40 base align=8 +std::recursive_mutex (0x0x7f1d776ede38) 0 + std::__recursive_mutex_base (0x0x7f1d777d7180) 0 + +Class std::timed_mutex + size=40 align=8 + base size=40 base align=8 +std::timed_mutex (0x0x7f1d777f1b60) 0 + std::__mutex_base (0x0x7f1d777d72a0) 0 + std::__timed_mutex_impl (0x0x7f1d777d7300) 0 empty + +Class std::recursive_timed_mutex + size=40 align=8 + base size=40 base align=8 +std::recursive_timed_mutex (0x0x7f1d7730f540) 0 + std::__recursive_mutex_base (0x0x7f1d777d73c0) 0 + std::__timed_mutex_impl (0x0x7f1d777d7420) 0 empty + +Class std::defer_lock_t + size=1 align=1 + base size=0 base align=1 +std::defer_lock_t (0x0x7f1d777d7480) 0 empty + +Class std::try_to_lock_t + size=1 align=1 + base size=0 base align=1 +std::try_to_lock_t (0x0x7f1d777d74e0) 0 empty + +Class std::adopt_lock_t + size=1 align=1 + base size=0 base align=1 +std::adopt_lock_t (0x0x7f1d777d7540) 0 empty + +Class std::once_flag + size=4 align=4 + base size=4 base align=4 +std::once_flag (0x0x7f1d777d7780) 0 + +Vtable for __gnu_cxx::__concurrence_lock_error +__gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_lock_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error +24 (int (*)(...))__gnu_cxx::__concurrence_lock_error::~__concurrence_lock_error +32 (int (*)(...))__gnu_cxx::__concurrence_lock_error::what + +Class __gnu_cxx::__concurrence_lock_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_lock_error (0x0x7f1d776edf70) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_lock_error::_ZTVN9__gnu_cxx24__concurrence_lock_errorE) + 16u) + std::exception (0x0x7f1d777d7840) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_lock_error (0x0x7f1d776edf70) + +Vtable for __gnu_cxx::__concurrence_unlock_error +__gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx26__concurrence_unlock_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error +24 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::~__concurrence_unlock_error +32 (int (*)(...))__gnu_cxx::__concurrence_unlock_error::what + +Class __gnu_cxx::__concurrence_unlock_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_unlock_error (0x0x7f1d77369000) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_unlock_error::_ZTVN9__gnu_cxx26__concurrence_unlock_errorE) + 16u) + std::exception (0x0x7f1d777d7900) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_unlock_error (0x0x7f1d77369000) + +Vtable for __gnu_cxx::__concurrence_broadcast_error +__gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx29__concurrence_broadcast_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error +24 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::~__concurrence_broadcast_error +32 (int (*)(...))__gnu_cxx::__concurrence_broadcast_error::what + +Class __gnu_cxx::__concurrence_broadcast_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_broadcast_error (0x0x7f1d77369068) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_broadcast_error::_ZTVN9__gnu_cxx29__concurrence_broadcast_errorE) + 16u) + std::exception (0x0x7f1d777d79c0) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_broadcast_error (0x0x7f1d77369068) + +Vtable for __gnu_cxx::__concurrence_wait_error +__gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9__gnu_cxx24__concurrence_wait_errorE) +16 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error +24 (int (*)(...))__gnu_cxx::__concurrence_wait_error::~__concurrence_wait_error +32 (int (*)(...))__gnu_cxx::__concurrence_wait_error::what + +Class __gnu_cxx::__concurrence_wait_error + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__concurrence_wait_error (0x0x7f1d77369138) 0 nearly-empty + vptr=((& __gnu_cxx::__concurrence_wait_error::_ZTVN9__gnu_cxx24__concurrence_wait_errorE) + 16u) + std::exception (0x0x7f1d777d7a80) 0 nearly-empty + primary-for __gnu_cxx::__concurrence_wait_error (0x0x7f1d77369138) + +Class __gnu_cxx::__mutex + size=40 align=8 + base size=40 base align=8 +__gnu_cxx::__mutex (0x0x7f1d777d7b40) 0 + +Class __gnu_cxx::__recursive_mutex + size=40 align=8 + base size=40 base align=8 +__gnu_cxx::__recursive_mutex (0x0x7f1d777d7ba0) 0 + +Class __gnu_cxx::__scoped_lock + size=8 align=8 + base size=8 base align=8 +__gnu_cxx::__scoped_lock (0x0x7f1d777d7c00) 0 + +Class __gnu_cxx::__cond + size=48 align=8 + base size=48 base align=8 +__gnu_cxx::__cond (0x0x7f1d777d7c60) 0 + +Vtable for std::bad_weak_ptr +std::bad_weak_ptr::_ZTVSt12bad_weak_ptr: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12bad_weak_ptr) +16 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr +24 (int (*)(...))std::bad_weak_ptr::~bad_weak_ptr +32 (int (*)(...))std::bad_weak_ptr::what + +Class std::bad_weak_ptr + size=8 align=8 + base size=8 base align=8 +std::bad_weak_ptr (0x0x7f1d77369410) 0 nearly-empty + vptr=((& std::bad_weak_ptr::_ZTVSt12bad_weak_ptr) + 16u) + std::exception (0x0x7f1d7742f000) 0 nearly-empty + primary-for std::bad_weak_ptr (0x0x7f1d77369410) + +Class std::_Sp_make_shared_tag + size=1 align=1 + base size=0 base align=1 +std::_Sp_make_shared_tag (0x0x7f1d7742f840) 0 empty + +Class std::_Sp_locker + size=2 align=1 + base size=2 base align=1 +std::_Sp_locker (0x0x7f1d771a8060) 0 + +Class std::thread::id + size=8 align=8 + base size=8 base align=8 +std::thread::id (0x0x7f1d771a8240) 0 + +Vtable for std::thread::_Impl_base +std::thread::_Impl_base::_ZTVNSt6thread10_Impl_baseE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6thread10_Impl_baseE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class std::thread::_Impl_base + size=24 align=8 + base size=24 base align=8 +std::thread::_Impl_base (0x0x7f1d771a82a0) 0 + vptr=((& std::thread::_Impl_base::_ZTVNSt6thread10_Impl_baseE) + 16u) + +Class std::thread + size=8 align=8 + base size=8 base align=8 +std::thread (0x0x7f1d771a81e0) 0 + +Class std::condition_variable + size=48 align=8 + base size=48 base align=8 +std::condition_variable (0x0x7f1d76f18ea0) 0 + +Class std::__at_thread_exit_elt + size=16 align=8 + base size=16 base align=8 +std::__at_thread_exit_elt (0x0x7f1d76f18f60) 0 + +Class std::_V2::condition_variable_any + size=64 align=8 + base size=64 base align=8 +std::_V2::condition_variable_any (0x0x7f1d76f7c000) 0 + +Class std::__atomic_futex_unsigned_base + size=1 align=1 + base size=0 base align=1 +std::__atomic_futex_unsigned_base (0x0x7f1d77003660) 0 empty + +Vtable for std::future_error +std::future_error::_ZTVSt12future_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12future_error) +16 (int (*)(...))std::future_error::~future_error +24 (int (*)(...))std::future_error::~future_error +32 (int (*)(...))std::future_error::what + +Class std::future_error + size=32 align=8 + base size=32 base align=8 +std::future_error (0x0x7f1d77005750) 0 + vptr=((& std::future_error::_ZTVSt12future_error) + 16u) + std::logic_error (0x0x7f1d770057b8) 0 + primary-for std::future_error (0x0x7f1d77005750) + std::exception (0x0x7f1d77003780) 0 nearly-empty + primary-for std::logic_error (0x0x7f1d770057b8) + +Class std::__future_base::_Result_base::_Deleter + size=1 align=1 + base size=0 base align=1 +std::__future_base::_Result_base::_Deleter (0x0x7f1d770038a0) 0 empty + +Vtable for std::__future_base::_Result_base +std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt13__future_base12_Result_baseE) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class std::__future_base::_Result_base + size=16 align=8 + base size=16 base align=8 +std::__future_base::_Result_base (0x0x7f1d77003840) 0 + vptr=((& std::__future_base::_Result_base::_ZTVNSt13__future_base12_Result_baseE) + 16u) + +Class std::__future_base::_State_baseV2::__exception_ptr_tag + size=1 align=1 + base size=0 base align=1 +std::__future_base::_State_baseV2::__exception_ptr_tag (0x0x7f1d76cf7de0) 0 empty + +Class std::__future_base::_State_baseV2::_Make_ready + size=32 align=8 + base size=32 base align=8 +std::__future_base::_State_baseV2::_Make_ready (0x0x7f1d76cf6d68) 0 + std::__at_thread_exit_elt (0x0x7f1d76cf7ea0) 0 + +Vtable for std::__future_base::_State_baseV2 +std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt13__future_base13_State_baseV2E) +16 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2 +24 (int (*)(...))std::__future_base::_State_baseV2::~_State_baseV2 +32 (int (*)(...))std::__future_base::_State_baseV2::_M_complete_async +40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future + +Class std::__future_base::_State_baseV2 + size=32 align=8 + base size=28 base align=8 +std::__future_base::_State_baseV2 (0x0x7f1d77003a20) 0 + vptr=((& std::__future_base::_State_baseV2::_ZTVNSt13__future_base13_State_baseV2E) + 16u) + +Class std::__future_base + size=1 align=1 + base size=0 base align=1 +std::__future_base (0x0x7f1d770037e0) 0 empty + +Vtable for std::__future_base::_Async_state_commonV2 +std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt13__future_base21_Async_state_commonV2E) +16 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2 +24 (int (*)(...))std::__future_base::_Async_state_commonV2::~_Async_state_commonV2 +32 (int (*)(...))std::__future_base::_Async_state_commonV2::_M_complete_async +40 (int (*)(...))std::__future_base::_State_baseV2::_M_is_deferred_future + +Class std::__future_base::_Async_state_commonV2 + size=48 align=8 + base size=44 base align=8 +std::__future_base::_Async_state_commonV2 (0x0x7f1d76abd958) 0 + vptr=((& std::__future_base::_Async_state_commonV2::_ZTVNSt13__future_base21_Async_state_commonV2E) + 16u) + std::__future_base::_State_baseV2 (0x0x7f1d76aa9d80) 0 + primary-for std::__future_base::_Async_state_commonV2 (0x0x7f1d76abd958) + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7f1d767184e0) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7f1d7671e410) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7f1d76718480) 0 + primary-for QThread (0x0x7f1d7671e410) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7f1d76718600) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7f1d7671e478) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7f1d767185a0) 0 + primary-for QThreadPool (0x0x7f1d7671e478) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7f1d76718660) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7f1d76718780) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7f1d7671e4e0) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7f1d76718720) 0 + primary-for QTimeLine (0x0x7f1d7671e4e0) + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7f1d76718840) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7f1d7671e548) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7f1d767187e0) 0 + primary-for QTimer (0x0x7f1d7671e548) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7f1d76718f00) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7f1d76718ea0) 0 + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7f1d767ff4e0) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7f1d7671e5b0) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7f1d767ff480) 0 + primary-for QTranslator (0x0x7f1d7671e5b0) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7f1d767ff600) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7f1d7651bcc0) 0 + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7f1d765b7360) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7f1d765b7960) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7f1d765b79c0) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7f1d765b7c60) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7f1d765e4a28) 0 + QVector (0x0x7f1d7666e060) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7f1d7666e0c0) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7f1d7666e360) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7f1d7666e600) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7f1d7666e8a0) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7f1d7666e900) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7f1d7635a360) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7f1d7635a480) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7f1d7635ac00) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7f1d763ce060) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7f1d763ce4e0) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7f1d763ce5a0) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7f1d763ce780) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7f1d7639a478) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7f1d763ce720) 0 + primary-for QGeoPositionInfoSource (0x0x7f1d7639a478) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7f1d763ce9c0) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7f1d7639a5b0) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7f1d763ce960) 0 + primary-for QGeoAreaMonitorSource (0x0x7f1d7639a5b0) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7f1d7639a618) 0 + QGeoShape (0x0x7f1d763cea20) 0 + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7f1d7639a8f0) 0 + QGeoShape (0x0x7f1d763cef00) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7f1d764c1300) 0 + +Class QGeoPath + size=8 align=8 + base size=8 base align=8 +QGeoPath (0x0x7f1d764eb138) 0 + QGeoShape (0x0x7f1d764c1a80) 0 + +Class QGeoPolygon + size=8 align=8 + base size=8 base align=8 +QGeoPolygon (0x0x7f1d764eb340) 0 + QGeoShape (0x0x7f1d764c1e40) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7f1d76121240) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7f1d76121300) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7f1d764eb548) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7f1d761212a0) 0 + primary-for QGeoSatelliteInfoSource (0x0x7f1d764eb548) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7f1d761213c0) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7f1d761214e0) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7f1d764eb5b0) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7f1d764eb618) 0 + primary-for QNmeaPositionInfoSource (0x0x7f1d764eb5b0) + QObject (0x0x7f1d76121480) 0 + primary-for QGeoPositionInfoSource (0x0x7f1d764eb618) + diff --git a/tests/auto/bic/data/QtPositioning.5.3.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.3.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..d29c24a --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.3.0.linux-gcc-amd64.txt @@ -0,0 +1,3822 @@ +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7f95f2df8f00) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7f95f2df8f60) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7f95f1d8fb40) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7f95f1d8fba0) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7f95f1d26820) 0 empty + std::input_iterator_tag (0x0x7f95f1d8fc00) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7f95f1d26888) 0 empty + std::forward_iterator_tag (0x0x7f95f1d268f0) 0 empty + std::input_iterator_tag (0x0x7f95f1d8fc60) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7f95f1d26958) 0 empty + std::bidirectional_iterator_tag (0x0x7f95f1d269c0) 0 empty + std::forward_iterator_tag (0x0x7f95f1d26a28) 0 empty + std::input_iterator_tag (0x0x7f95f1d8fcc0) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7f95f1de9840) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7f95f1de9a80) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7f95f1de9b40) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7f95f1de9ba0) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7f95f1de9c60) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7f95f1de9cc0) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7f95f1eab180) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7f95f1eab1e0) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7f95f1eab240) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7f95f1d26d68) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7f95f1eab2a0) 0 nearly-empty + primary-for std::bad_exception (0x0x7f95f1d26d68) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7f95f1d26dd0) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7f95f1eab300) 0 nearly-empty + primary-for std::bad_alloc (0x0x7f95f1d26dd0) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7f95f1eab360) 0 empty + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7f95f0cb36c0) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7f95f0cb3720) 0 + +Class QtPrivate::big_ + size=2 align=1 + base size=2 base align=1 +QtPrivate::big_ (0x0x7f95f0cb3900) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7f95f0a18060) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7f95f0a180c0) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7f95f0a18120) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7f95f0a18180) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7f95f0a182a0) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7f95f06ee3a8) 0 + QAtomicInteger (0x0x7f95f06ee410) 0 + QBasicAtomicInteger (0x0x7f95f0a18d80) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7f95f04f5ea0) 0 empty + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7f95f069fde0) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7f95f05ca548) 0 + QGenericArgument (0x0x7f95f069fe40) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7f95f0304000) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7f95f0304120) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7f95f0304360) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7f95f03043c0) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7f95f03044e0) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7f95f0304540) 0 + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7f95f03048a0) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7f95f0304900) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7f95f0304a80) 0 + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7f95f0304ea0) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 (int (*)(...))__cxxabiv1::__forced_unwind::~__forced_unwind +24 (int (*)(...))__cxxabiv1::__forced_unwind::~__forced_unwind +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7f95f0304f00) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7f95f02039c0) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7f95f0203a20) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7f95f0203ae0) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7f95f0203b40) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7f95f0203ba0) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7f95f0203c00) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7f95f0203d20) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7f95f0203d80) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7f95eff0a4e0) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7f95eff0a660) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7f95eff0a720) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7f95eff0a6c0) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7f95eff0a8a0) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7f95eff0ab40) 0 + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7f95eff0ad20) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7f95eff0ad80) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7f95eff0ade0) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7f95eff0ae40) 0 + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureE) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=16 align=8 + base size=16 base align=8 +std::ios_base::failure (0x0x7f95eff0e000) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureE) + 16u) + std::exception (0x0x7f95efbc92a0) 0 nearly-empty + primary-for std::ios_base::failure (0x0x7f95eff0e000) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7f95efbc9300) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7f95efbc9360) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7f95efbc93c0) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7f95efbc9240) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7f95efbc9540) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7f95efbc9c00) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7f95ef810a90 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +32 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 (int (*)(...))std::basic_istream::_ZTv0_n24_NSiD1Ev +72 (int (*)(...))std::basic_istream::_ZTv0_n24_NSiD0Ev + +Construction vtable for std::basic_ostream (0x0x7f95ef810b60 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +32 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSoD1Ev +72 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSoD0Ev + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7f95ef810e38 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +32 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 (int (*)(...))std::basic_istream::_ZTv0_n24_NSt13basic_istreamIwSt11char_traitsIwEED1Ev +72 (int (*)(...))std::basic_istream::_ZTv0_n24_NSt13basic_istreamIwSt11char_traitsIwEED0Ev + +Construction vtable for std::basic_ostream (0x0x7f95ef810f08 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +32 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSt13basic_ostreamIwSt11char_traitsIwEED1Ev +72 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSt13basic_ostreamIwSt11char_traitsIwEED0Ev + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7f95ef8b0000) 0 + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7f95ef8b0300) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7f95ef8b02a0) 0 + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7f95ef8b06c0) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7f95ef6a5420) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7f95ef67d208) 0 + std::iterator (0x0x7f95ef6a54e0) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7f95ef67d270) 0 + std::_Bit_iterator_base (0x0x7f95ef67d2d8) 0 + std::iterator (0x0x7f95ef6a5540) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7f95ef67d340) 0 + std::_Bit_iterator_base (0x0x7f95ef67d3a8) 0 + std::iterator (0x0x7f95ef6a55a0) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7f95ef6a5960) 0 + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7f95ef6a5d80) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7f95ef6a5e40) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7f95ef6a5f00) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7f95ef180360) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7f95ef1806c0) 0 + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7f95ef180ae0) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7f95ef180cc0) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7f95ef180d80) 0 + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7f95eeff3060) 0 + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7f95eeff31e0) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7f95eeff3360) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7f95eeff3300) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7f95eeff3660) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7f95eeff36c0) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7f95eeff3780) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 (int (*)(...))QAbstractAnimation::~QAbstractAnimation +48 (int (*)(...))QAbstractAnimation::~QAbstractAnimation +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7f95ef204dd0) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7f95eeff3720) 0 + primary-for QAbstractAnimation (0x0x7f95ef204dd0) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7f95eeff3840) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7f95ef204e38) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7f95eeff37e0) 0 + primary-for QAnimationDriver (0x0x7f95ef204e38) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7f95eeff3900) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 (int (*)(...))QAnimationGroup::~QAnimationGroup +48 (int (*)(...))QAnimationGroup::~QAnimationGroup +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7f95ef204ea0) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7f95ef204f08) 0 + primary-for QAnimationGroup (0x0x7f95ef204ea0) + QObject (0x0x7f95eeff38a0) 0 + primary-for QAbstractAnimation (0x0x7f95ef204f08) + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7f95eeff39c0) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7f95ef204f70) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7f95eecee000) 0 + primary-for QParallelAnimationGroup (0x0x7f95ef204f70) + QAbstractAnimation (0x0x7f95eecee068) 0 + primary-for QAnimationGroup (0x0x7f95eecee000) + QObject (0x0x7f95eeff3960) 0 + primary-for QAbstractAnimation (0x0x7f95eecee068) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7f95eeff3a80) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7f95eecee0d0) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7f95eecee138) 0 + primary-for QPauseAnimation (0x0x7f95eecee0d0) + QObject (0x0x7f95eeff3a20) 0 + primary-for QAbstractAnimation (0x0x7f95eecee138) + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7f95eeff3c60) 0 + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7f95eeff3e40) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7f95eeff3f00) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7f95eee232a0) 0 + +Class QHashData + size=48 align=8 + base size=48 base align=8 +QHashData (0x0x7f95eee23240) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7f95eee23300) 0 empty + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7f95eee237e0) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 (int (*)(...))QIODevice::~QIODevice +48 (int (*)(...))QIODevice::~QIODevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7f95eeceea28) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7f95eee23780) 0 + primary-for QIODevice (0x0x7f95eeceea28) + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7f95eee23900) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7f95eee239c0) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7f95eee23b40) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7f95eee23ae0) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7f95eeceebc8) 0 + QList (0x0x7f95eee23cc0) 0 + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7f95ee8fe000) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7f95ee8fe0c0) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7f95ee8fe060) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7f95ee8fe120) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7f95eee23f60) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7f95ee8fe3c0) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7f95ee8fe480) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7f95ee8fe420) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7f95ee8fe540) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7f95ee8fe4e0) 0 + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7f95eea14120) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7f95ee9cc5b0) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7f95ee9cc618) 0 + primary-for QVariantAnimation (0x0x7f95ee9cc5b0) + QObject (0x0x7f95eea140c0) 0 + primary-for QAbstractAnimation (0x0x7f95ee9cc618) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7f95eea141e0) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7f95ee9cc6e8) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7f95ee9cc750) 0 + primary-for QPropertyAnimation (0x0x7f95ee9cc6e8) + QAbstractAnimation (0x0x7f95ee9cc7b8) 0 + primary-for QVariantAnimation (0x0x7f95ee9cc750) + QObject (0x0x7f95eea14180) 0 + primary-for QAbstractAnimation (0x0x7f95ee9cc7b8) + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7f95eea142a0) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7f95ee9cc820) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7f95ee9cc888) 0 + primary-for QSequentialAnimationGroup (0x0x7f95ee9cc820) + QAbstractAnimation (0x0x7f95ee9cc8f0) 0 + primary-for QAnimationGroup (0x0x7f95ee9cc888) + QObject (0x0x7f95eea14240) 0 + primary-for QAbstractAnimation (0x0x7f95ee9cc8f0) + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7f95eea14360) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 (int (*)(...))QTextCodec::~QTextCodec +64 (int (*)(...))QTextCodec::~QTextCodec + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7f95eea14300) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7f95eea14480) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7f95eea144e0) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7f95eea14540) 0 + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7f95eea147e0) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7f95eea14960) 0 + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7f95eea14de0) 0 empty + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7f95ee7cd5a0) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7f95ee7cd6c0) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7f95ee7cd7e0) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7f95ee7cd960) 0 empty + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7f95ee7cda20) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7f95ee9ccf08) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7f95ee9ccf70) 0 + primary-for QBuffer (0x0x7f95ee9ccf08) + QObject (0x0x7f95ee7cd9c0) 0 + primary-for QIODevice (0x0x7f95ee9ccf70) + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7f95ee7cda80) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7f95ee7cdd80) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7f95ee7cdde0) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7f95ee7cdea0) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7f95ee59f180) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7f95ee59f3c0) 0 + +Class QDebug::Stream + size=72 align=8 + base size=72 base align=8 +QDebug::Stream (0x0x7f95ee59fa20) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7f95ee59f9c0) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7f95ee59fb40) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7f95ee59fc00) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7f95ee59fcc0) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7f95ee5a2208) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7f95ee5a2270) 0 + primary-for QFileDevice (0x0x7f95ee5a2208) + QObject (0x0x7f95ee59fc60) 0 + primary-for QIODevice (0x0x7f95ee5a2270) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7f95ee59fe40) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7f95ee5a23a8) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7f95ee5a2410) 0 + primary-for QFile (0x0x7f95ee5a23a8) + QIODevice (0x0x7f95ee5a2478) 0 + primary-for QFileDevice (0x0x7f95ee5a2410) + QObject (0x0x7f95ee59fde0) 0 + primary-for QIODevice (0x0x7f95ee5a2478) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7f95ee59ff60) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7f95ee371240) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7f95ee371540) 0 + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7f95ee371720) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7f95ee5a2958) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7f95ee3716c0) 0 + primary-for QFileSelector (0x0x7f95ee5a2958) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7f95ee3717e0) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7f95ee5a29c0) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7f95ee371780) 0 + primary-for QFileSystemWatcher (0x0x7f95ee5a29c0) + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7f95ee371840) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7f95ee371960) 0 + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7f95ee3719c0) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7f95ee371ba0) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7f95ee5a2a90) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7f95ee5a2af8) 0 + primary-for QProcess (0x0x7f95ee5a2a90) + QObject (0x0x7f95ee371b40) 0 + primary-for QIODevice (0x0x7f95ee5a2af8) + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7f95ee371c00) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7f95ee371d80) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7f95ee5a2b60) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7f95ee5a2bc8) 0 + primary-for QSaveFile (0x0x7f95ee5a2b60) + QIODevice (0x0x7f95ee5a2c30) 0 + primary-for QFileDevice (0x0x7f95ee5a2bc8) + QObject (0x0x7f95ee371d20) 0 + primary-for QIODevice (0x0x7f95ee5a2c30) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7f95ee371e40) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7f95ee5a2c98) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7f95ee371de0) 0 + primary-for QSettings (0x0x7f95ee5a2c98) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7f95ee371ea0) 0 empty + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7f95ee0f2000) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7f95ee0f2120) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7f95ee5a2dd0) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7f95ee5a2e38) 0 + primary-for QTemporaryFile (0x0x7f95ee5a2dd0) + QFileDevice (0x0x7f95ee5a2ea0) 0 + primary-for QFile (0x0x7f95ee5a2e38) + QIODevice (0x0x7f95ee5a2f08) 0 + primary-for QFileDevice (0x0x7f95ee5a2ea0) + QObject (0x0x7f95ee0f20c0) 0 + primary-for QIODevice (0x0x7f95ee5a2f08) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7f95ee0f2240) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7f95ee0f2660) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7f95ee0f27e0) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7f95ee0f2900) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7f95ee0f2a80) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 (int (*)(...))QAbstractItemModel::~QAbstractItemModel +48 (int (*)(...))QAbstractItemModel::~QAbstractItemModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7f95ee1602d8) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7f95ee0f2a20) 0 + primary-for QAbstractItemModel (0x0x7f95ee1602d8) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7f95ee0f2d80) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 (int (*)(...))QAbstractTableModel::~QAbstractTableModel +48 (int (*)(...))QAbstractTableModel::~QAbstractTableModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7f95ee160410) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7f95ee160478) 0 + primary-for QAbstractTableModel (0x0x7f95ee160410) + QObject (0x0x7f95ee0f2d20) 0 + primary-for QAbstractItemModel (0x0x7f95ee160478) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7f95ee0f2e40) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 (int (*)(...))QAbstractListModel::~QAbstractListModel +48 (int (*)(...))QAbstractListModel::~QAbstractListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7f95ee1604e0) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7f95ee160548) 0 + primary-for QAbstractListModel (0x0x7f95ee1604e0) + QObject (0x0x7f95ee0f2de0) 0 + primary-for QAbstractItemModel (0x0x7f95ee160548) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7f95ee0f2f00) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 (int (*)(...))QAbstractProxyModel::~QAbstractProxyModel +48 (int (*)(...))QAbstractProxyModel::~QAbstractProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7f95ee1605b0) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7f95ee160618) 0 + primary-for QAbstractProxyModel (0x0x7f95ee1605b0) + QObject (0x0x7f95ee0f2ea0) 0 + primary-for QAbstractItemModel (0x0x7f95ee160618) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7f95edee8000) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7f95ee160680) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7f95ee1606e8) 0 + primary-for QIdentityProxyModel (0x0x7f95ee160680) + QAbstractItemModel (0x0x7f95ee160750) 0 + primary-for QAbstractProxyModel (0x0x7f95ee1606e8) + QObject (0x0x7f95ee0f2f60) 0 + primary-for QAbstractItemModel (0x0x7f95ee160750) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7f95edee8060) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7f95edee81e0) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7f95ee160820) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7f95edee8180) 0 + primary-for QItemSelectionModel (0x0x7f95ee160820) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7f95ee160958) 0 + QList (0x0x7f95edee83c0) 0 + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7f95edee8480) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7f95ee1609c0) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7f95ee160a28) 0 + primary-for QSortFilterProxyModel (0x0x7f95ee1609c0) + QAbstractItemModel (0x0x7f95ee160a90) 0 + primary-for QAbstractProxyModel (0x0x7f95ee160a28) + QObject (0x0x7f95edee8420) 0 + primary-for QAbstractItemModel (0x0x7f95ee160a90) + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7f95edee8540) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7f95ee160af8) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7f95ee160b60) 0 + primary-for QStringListModel (0x0x7f95ee160af8) + QAbstractItemModel (0x0x7f95ee160bc8) 0 + primary-for QAbstractListModel (0x0x7f95ee160b60) + QObject (0x0x7f95edee84e0) 0 + primary-for QAbstractItemModel (0x0x7f95ee160bc8) + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7f95edee85a0) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7f95edee8660) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7f95edee8780) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7f95edee87e0) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7f95edee8720) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7f95edee8840) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7f95edee88a0) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7f95edee8960) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7f95edee89c0) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7f95edee8900) 0 + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7f95edee8ae0) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7f95ee160c30) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7f95edee8a80) 0 + primary-for QEventLoop (0x0x7f95ee160c30) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7f95edee8c00) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7f95edee8cc0) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7f95edee8d20) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 (int (*)(...))QAbstractEventDispatcher::~QAbstractEventDispatcher +48 (int (*)(...))QAbstractEventDispatcher::~QAbstractEventDispatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7f95ee160d68) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7f95edee8c60) 0 + primary-for QAbstractEventDispatcher (0x0x7f95ee160d68) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 (int (*)(...))QAbstractNativeEventFilter::~QAbstractNativeEventFilter +24 (int (*)(...))QAbstractNativeEventFilter::~QAbstractNativeEventFilter +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7f95edee8d80) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7f95edee8de0) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7f95edee8f00) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7f95ee160e38) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7f95edee8f60) 0 + primary-for QTimerEvent (0x0x7f95ee160e38) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7f95ee160ea0) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7f95edd19000) 0 + primary-for QChildEvent (0x0x7f95ee160ea0) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7f95ee160f08) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7f95edd19060) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7f95ee160f08) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7f95ee160f70) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7f95edd190c0) 0 + primary-for QDeferredDeleteEvent (0x0x7f95ee160f70) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7f95edd19180) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7f95edd27000) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7f95edd19120) 0 + primary-for QCoreApplication (0x0x7f95edd27000) + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7f95edd191e0) 0 + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7f95edd19240) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7f95edd19360) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7f95edd19480) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7f95edd194e0) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7f95edd19660) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7f95edd27270) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7f95edd19600) 0 + primary-for QMimeData (0x0x7f95edd27270) + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7f95edd19720) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7f95edd272d8) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7f95edd196c0) 0 + primary-for QObjectCleanupHandler (0x0x7f95edd272d8) + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7f95edd19960) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7f95edd27340) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7f95edd19900) 0 + primary-for QSharedMemory (0x0x7f95edd27340) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7f95edd19a20) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7f95edd273a8) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7f95edd199c0) 0 + primary-for QSignalMapper (0x0x7f95edd273a8) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7f95edd19ae0) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7f95edd27410) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7f95edd19a80) 0 + primary-for QSocketNotifier (0x0x7f95edd27410) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7f95edd19b40) 0 + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7f95edd19c60) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7f95edd27478) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7f95edd19c00) 0 + primary-for QTimer (0x0x7f95edd27478) + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7f95edd19d80) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7f95edd274e0) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7f95edd19d20) 0 + primary-for QTranslator (0x0x7f95edd274e0) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7f95edd19de0) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7f95edd19f60) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 (int (*)(...))QFactoryInterface::~QFactoryInterface +24 (int (*)(...))QFactoryInterface::~QFactoryInterface +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7f95edead000) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7f95edead120) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7f95edd275b0) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7f95edead0c0) 0 + primary-for QLibrary (0x0x7f95edd275b0) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7f95edead240) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7f95edead3c0) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7f95edd27750) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7f95edead360) 0 + primary-for QPluginLoader (0x0x7f95edd27750) + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7f95edead420) 0 + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7f95edead5a0) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 (int (*)(...))QAbstractState::~QAbstractState +48 (int (*)(...))QAbstractState::~QAbstractState +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7f95edd27820) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7f95edead540) 0 + primary-for QAbstractState (0x0x7f95edd27820) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7f95edead660) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 (int (*)(...))QAbstractTransition::~QAbstractTransition +48 (int (*)(...))QAbstractTransition::~QAbstractTransition +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7f95edd27888) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7f95edead600) 0 + primary-for QAbstractTransition (0x0x7f95edd27888) + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7f95edead720) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7f95edd278f0) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7f95edd27958) 0 + primary-for QEventTransition (0x0x7f95edd278f0) + QObject (0x0x7f95edead6c0) 0 + primary-for QAbstractTransition (0x0x7f95edd27958) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7f95edead7e0) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7f95edd279c0) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7f95edd27a28) 0 + primary-for QFinalState (0x0x7f95edd279c0) + QObject (0x0x7f95edead780) 0 + primary-for QAbstractState (0x0x7f95edd27a28) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7f95edead8a0) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7f95edd27a90) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7f95edd27af8) 0 + primary-for QHistoryState (0x0x7f95edd27a90) + QObject (0x0x7f95edead840) 0 + primary-for QAbstractState (0x0x7f95edd27af8) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7f95edead960) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7f95edd27b60) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7f95edd27bc8) 0 + primary-for QSignalTransition (0x0x7f95edd27b60) + QObject (0x0x7f95edead900) 0 + primary-for QAbstractTransition (0x0x7f95edd27bc8) + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7f95edeada20) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7f95edd27c30) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7f95edd27c98) 0 + primary-for QState (0x0x7f95edd27c30) + QObject (0x0x7f95edead9c0) 0 + primary-for QAbstractState (0x0x7f95edd27c98) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7f95edeadb40) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7f95edd27e38) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7f95edeadba0) 0 + primary-for QStateMachine::SignalEvent (0x0x7f95edd27e38) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7f95edd27ea0) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7f95edeadc00) 0 + primary-for QStateMachine::WrappedEvent (0x0x7f95edd27ea0) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7f95edd27d00) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7f95edd27d68) 0 + primary-for QStateMachine (0x0x7f95edd27d00) + QAbstractState (0x0x7f95edd27dd0) 0 + primary-for QState (0x0x7f95edd27d68) + QObject (0x0x7f95edeadae0) 0 + primary-for QAbstractState (0x0x7f95edd27dd0) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7f95edd27f08) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7f95edeadc60) 0 nearly-empty + primary-for QException (0x0x7f95edd27f08) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7f95edd27f70) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7f95edbb5000) 0 nearly-empty + primary-for QUnhandledException (0x0x7f95edd27f70) + std::exception (0x0x7f95edeadcc0) 0 nearly-empty + primary-for QException (0x0x7f95edbb5000) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7f95edeadd20) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7f95edeadde0) 0 + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QRunnable::~QRunnable +32 (int (*)(...))QRunnable::~QRunnable + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7f95edeade40) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7f95edeadea0) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7f95edbb51a0) 0 + QBasicMutex (0x0x7f95edbed000) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7f95edbed060) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7f95edbed0c0) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7f95edbed120) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7f95edbed2a0) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7f95edbed360) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7f95edbed6c0) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 (int (*)(...))QFutureWatcherBase::~QFutureWatcherBase +48 (int (*)(...))QFutureWatcherBase::~QFutureWatcherBase +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7f95edbb5a90) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7f95edbed660) 0 + primary-for QFutureWatcherBase (0x0x7f95edbb5a90) + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7f95edbed7e0) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7f95edbed840) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7f95edbed8a0) 0 + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7f95edbed900) 0 + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7f95edbed9c0) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7f95edbb5e38) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7f95edbed960) 0 + primary-for QThread (0x0x7f95edbb5e38) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7f95edbeda80) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7f95edbb5ea0) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7f95edbeda20) 0 + primary-for QThreadPool (0x0x7f95edbb5ea0) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7f95edbedae0) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7f95edbedba0) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7f95ed9a7180) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7f95ed9a71e0) 0 + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7f95ed9a7360) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7f95ed9a7300) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7f95ed9a74e0) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7f95ed9a75a0) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7f95ed9a7780) 0 + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7f95ed9a7900) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7f95ed9a7960) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7f95ed9a79c0) 0 + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7f95ed9a7a20) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7f95ed9a7b40) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7f95ed9a7c60) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7f95ed9a7d80) 0 + +Class QLinkedListData + size=32 align=8 + base size=32 base align=8 +QLinkedListData (0x0x7f95ed9a7ea0) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7f95ed75a240) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7f95ed75a360) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7f95ed75a480) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7f95ed75a540) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7f95ed75a660) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7f95ed75a780) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7f95ed75a8a0) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7f95ed75a9c0) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7f95ed75acc0) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7f95ed75ae40) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7f95ed63f0c0) 0 empty + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7f95ed63fae0) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7f95ed63fc60) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7f95ed6657b8) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7f95ed63fc00) 0 + primary-for QTimeLine (0x0x7f95ed6657b8) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7f95ed63fd20) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7f95ed63fcc0) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7f95ed63ff60) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7f95ed337000) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7f95ed665a28) 0 + QVector (0x0x7f95ed3371e0) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7f95ed337240) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7f95ed337360) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7f95ed337480) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7f95ed3375a0) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7f95ed337600) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7f95ed337720) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7f95ed337840) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7f95ed337ae0) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7f95ed337d80) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7f95ed447060) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7f95ed447120) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7f95ed4471e0) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 (int (*)(...))QGeoPositionInfoSource::~QGeoPositionInfoSource +48 (int (*)(...))QGeoPositionInfoSource::~QGeoPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7f95ed665d00) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7f95ed447180) 0 + primary-for QGeoPositionInfoSource (0x0x7f95ed665d00) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7f95ed447360) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 (int (*)(...))QGeoAreaMonitorSource::~QGeoAreaMonitorSource +48 (int (*)(...))QGeoAreaMonitorSource::~QGeoAreaMonitorSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7f95ed665e38) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7f95ed447300) 0 + primary-for QGeoAreaMonitorSource (0x0x7f95ed665e38) + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7f95ed665ea0) 0 + QGeoShape (0x0x7f95ed4473c0) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7f95ed447600) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7f95ed4478a0) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7f95ed447960) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 (int (*)(...))QGeoSatelliteInfoSource::~QGeoSatelliteInfoSource +48 (int (*)(...))QGeoSatelliteInfoSource::~QGeoSatelliteInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7f95ed060000) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7f95ed447900) 0 + primary-for QGeoSatelliteInfoSource (0x0x7f95ed060000) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 (int (*)(...))QGeoPositionInfoSourceFactory::~QGeoPositionInfoSourceFactory +24 (int (*)(...))QGeoPositionInfoSourceFactory::~QGeoPositionInfoSourceFactory +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7f95ed447a20) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7f95ed060068) 0 + QGeoShape (0x0x7f95ed447ae0) 0 + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7f95ed447e40) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7f95ed060138) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7f95ed0601a0) 0 + primary-for QNmeaPositionInfoSource (0x0x7f95ed060138) + QObject (0x0x7f95ed447de0) 0 + primary-for QGeoPositionInfoSource (0x0x7f95ed0601a0) + diff --git a/tests/auto/bic/data/QtPositioning.5.4.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.4.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..3746cf7 --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.4.0.linux-gcc-amd64.txt @@ -0,0 +1,3854 @@ +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7fb610c07060) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7fb610c070c0) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7fb610c48c60) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7fb610c48cc0) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7fb610bdc958) 0 empty + std::input_iterator_tag (0x0x7fb610c48d20) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7fb610bdc9c0) 0 empty + std::forward_iterator_tag (0x0x7fb610bdca28) 0 empty + std::input_iterator_tag (0x0x7fb610c48d80) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7fb610bdca90) 0 empty + std::bidirectional_iterator_tag (0x0x7fb610bdcaf8) 0 empty + std::forward_iterator_tag (0x0x7fb610bdcb60) 0 empty + std::input_iterator_tag (0x0x7fb610c48de0) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7fb610c79960) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7fb610c79ba0) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7fb610c79c60) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7fb610c79cc0) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7fb610c79d80) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7fb610c79de0) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7fb610d642a0) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7fb610d64300) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7fb610d64360) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7fb610bdcea0) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7fb610d643c0) 0 nearly-empty + primary-for std::bad_exception (0x0x7fb610bdcea0) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7fb610bdcf08) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7fb610d64420) 0 nearly-empty + primary-for std::bad_alloc (0x0x7fb610bdcf08) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7fb610d64480) 0 empty + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7fb60fb587e0) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7fb60fb58840) 0 + +Class QtPrivate::big_ + size=2 align=1 + base size=2 base align=1 +QtPrivate::big_ (0x0x7fb60fb58a20) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7fb60f8c8300) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7fb60f8c8360) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7fb60f8c83c0) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7fb60f8c8420) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7fb60f8c8540) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7fb60f8cd680) 0 + QAtomicInteger (0x0x7fb60f8cd6e8) 0 + QBasicAtomicInteger (0x0x7fb60f70e060) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7fb60f4e4180) 0 empty + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7fb60f1ae0c0) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7fb60f452820) 0 + QGenericArgument (0x0x7fb60f1ae120) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7fb60f1ae2a0) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7fb60f1ae3c0) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7fb60f1ae600) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7fb60f1ae660) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7fb60f1ae780) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7fb60f1ae7e0) 0 + +Class QtPrivate::QContainerImplHelper + size=1 align=1 + base size=0 base align=1 +QtPrivate::QContainerImplHelper (0x0x7fb60f1aeae0) 0 empty + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7fb60f1aee40) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 (int (*)(...))__cxxabiv1::__forced_unwind::~__forced_unwind +24 (int (*)(...))__cxxabiv1::__forced_unwind::~__forced_unwind +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7fb60f1aeea0) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7fb60efe4960) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7fb60efe49c0) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7fb60efe4a80) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7fb60efe4ae0) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7fb60efe4b40) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7fb60efe4ba0) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7fb60efe4cc0) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7fb60efe4d20) 0 + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7fb60f0ed4e0) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7fb60f0ed540) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7fb60f0ed6c0) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7fb60f0ed7e0) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7fb60f0ed960) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7fb60f0eda20) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7fb60f0ed9c0) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7fb60f0edba0) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7fb60f0ede40) 0 + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7fb60ea19060) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7fb60ea190c0) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7fb60ea19120) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7fb60ea19180) 0 + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureE) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=16 align=8 + base size=16 base align=8 +std::ios_base::failure (0x0x7fb60ead6000) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureE) + 16u) + std::exception (0x0x7fb60ea195a0) 0 nearly-empty + primary-for std::ios_base::failure (0x0x7fb60ead6000) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7fb60ea19600) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7fb60ea19660) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7fb60ea196c0) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7fb60ea19540) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7fb60ea19840) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7fb60ea19f00) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7fb60e693e38 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +32 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 (int (*)(...))std::basic_istream::_ZTv0_n24_NSiD1Ev +72 (int (*)(...))std::basic_istream::_ZTv0_n24_NSiD0Ev + +Construction vtable for std::basic_ostream (0x0x7fb60e693f08 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +32 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSoD1Ev +72 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSoD0Ev + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7fb60e6933a8 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +32 (int (*)(...))std::basic_istream<_CharT, _Traits>::~basic_istream > +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 (int (*)(...))std::basic_istream::_ZTv0_n24_NSt13basic_istreamIwSt11char_traitsIwEED1Ev +72 (int (*)(...))std::basic_istream::_ZTv0_n24_NSt13basic_istreamIwSt11char_traitsIwEED0Ev + +Construction vtable for std::basic_ostream (0x0x7fb60e6934e0 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +32 (int (*)(...))std::basic_ostream<_CharT, _Traits>::~basic_ostream > +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSt13basic_ostreamIwSt11char_traitsIwEED1Ev +72 (int (*)(...))std::basic_ostream::_ZTv0_n24_NSt13basic_ostreamIwSt11char_traitsIwEED0Ev + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7fb60e6e1300) 0 + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7fb60e6e1660) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7fb60e6e1600) 0 + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7fb60e6e1b40) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7fb60e5678a0) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7fb60e766750) 0 + std::iterator (0x0x7fb60e567960) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7fb60e7667b8) 0 + std::_Bit_iterator_base (0x0x7fb60e766820) 0 + std::iterator (0x0x7fb60e5679c0) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7fb60e766888) 0 + std::_Bit_iterator_base (0x0x7fb60e7668f0) 0 + std::iterator (0x0x7fb60e567a20) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7fb60e567de0) 0 + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7fb60e34d240) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7fb60e34d300) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7fb60e34d3c0) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7fb60e34d7e0) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7fb60e34db40) 0 + +Class QtMetaTypePrivate::VectorBoolElements + size=1 align=1 + base size=0 base align=1 +QtMetaTypePrivate::VectorBoolElements (0x0x7fb60e34dc60) 0 empty + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7fb60e146480) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7fb60e146660) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7fb60e146720) 0 + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7fb60df0ba80) 0 + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7fb60df0bc00) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7fb60df0bde0) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7fb60df0bd80) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7fb60dbea120) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7fb60dbea180) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7fb60dbea240) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 (int (*)(...))QAbstractAnimation::~QAbstractAnimation +48 (int (*)(...))QAbstractAnimation::~QAbstractAnimation +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7fb60df5f5b0) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7fb60dbea1e0) 0 + primary-for QAbstractAnimation (0x0x7fb60df5f5b0) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7fb60dbea300) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7fb60df5f618) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7fb60dbea2a0) 0 + primary-for QAnimationDriver (0x0x7fb60df5f618) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7fb60dbea3c0) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 (int (*)(...))QAnimationGroup::~QAnimationGroup +48 (int (*)(...))QAnimationGroup::~QAnimationGroup +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7fb60df5f680) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7fb60df5f6e8) 0 + primary-for QAnimationGroup (0x0x7fb60df5f680) + QObject (0x0x7fb60dbea360) 0 + primary-for QAbstractAnimation (0x0x7fb60df5f6e8) + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7fb60dbea480) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7fb60df5f750) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7fb60df5f7b8) 0 + primary-for QParallelAnimationGroup (0x0x7fb60df5f750) + QAbstractAnimation (0x0x7fb60df5f820) 0 + primary-for QAnimationGroup (0x0x7fb60df5f7b8) + QObject (0x0x7fb60dbea420) 0 + primary-for QAbstractAnimation (0x0x7fb60df5f820) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7fb60dbea540) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7fb60df5f888) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7fb60df5f8f0) 0 + primary-for QPauseAnimation (0x0x7fb60df5f888) + QObject (0x0x7fb60dbea4e0) 0 + primary-for QAbstractAnimation (0x0x7fb60df5f8f0) + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7fb60dbea720) 0 + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7fb60dbea900) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7fb60dbea9c0) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7fb60dbead20) 0 + +Class QHashData + size=48 align=8 + base size=48 base align=8 +QHashData (0x0x7fb60dbeacc0) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7fb60dbead80) 0 empty + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7fb60da72300) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 (int (*)(...))QIODevice::~QIODevice +48 (int (*)(...))QIODevice::~QIODevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7fb60dacc138) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7fb60da722a0) 0 + primary-for QIODevice (0x0x7fb60dacc138) + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7fb60da72420) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7fb60da724e0) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7fb60da72660) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7fb60da72600) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7fb60dacc340) 0 + QList (0x0x7fb60dacc3a8) 0 + QListSpecialMethods (0x0x7fb60da72840) 0 empty + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7fb60da72b40) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7fb60da72c00) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7fb60da72ba0) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7fb60da72c60) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7fb60da72ae0) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7fb60da72f60) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7fb60d8f6060) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7fb60d8f6000) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7fb60d8f6120) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7fb60d8f60c0) 0 + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7fb60d8f6cc0) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7fb60dacce38) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7fb60daccea0) 0 + primary-for QVariantAnimation (0x0x7fb60dacce38) + QObject (0x0x7fb60d8f6c60) 0 + primary-for QAbstractAnimation (0x0x7fb60daccea0) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7fb60d8f6d80) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7fb60daccf70) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7fb60dacc068) 0 + primary-for QPropertyAnimation (0x0x7fb60daccf70) + QAbstractAnimation (0x0x7fb60daccc98) 0 + primary-for QVariantAnimation (0x0x7fb60dacc068) + QObject (0x0x7fb60d8f6d20) 0 + primary-for QAbstractAnimation (0x0x7fb60daccc98) + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7fb60d8f6e40) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7fb60daccd00) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7fb60daccd68) 0 + primary-for QSequentialAnimationGroup (0x0x7fb60daccd00) + QAbstractAnimation (0x0x7fb60d5a6000) 0 + primary-for QAnimationGroup (0x0x7fb60daccd68) + QObject (0x0x7fb60d8f6de0) 0 + primary-for QAbstractAnimation (0x0x7fb60d5a6000) + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7fb60d8f6f00) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 (int (*)(...))QTextCodec::~QTextCodec +64 (int (*)(...))QTextCodec::~QTextCodec + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7fb60d8f6ea0) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7fb60d5c2060) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7fb60d5c20c0) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7fb60d5c2120) 0 + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7fb60d5c2300) 0 empty + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7fb60d5c2a80) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7fb60d5c2ba0) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7fb60d5c2cc0) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7fb60d5c2e40) 0 empty + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7fb60d5c2f00) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7fb60d5a62d8) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7fb60d5a6340) 0 + primary-for QBuffer (0x0x7fb60d5a62d8) + QObject (0x0x7fb60d5c2ea0) 0 + primary-for QIODevice (0x0x7fb60d5a6340) + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7fb60d5c2f60) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7fb60d3c92a0) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7fb60d3c9300) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7fb60d3c93c0) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7fb60d3c9660) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7fb60d3c98a0) 0 + +Class QDebug::Stream + size=80 align=8 + base size=76 base align=8 +QDebug::Stream (0x0x7fb60d3c9f00) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7fb60d3c9ea0) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7fb60d18a060) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7fb60d18a120) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7fb60d18a1e0) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7fb60d5a67b8) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7fb60d5a6820) 0 + primary-for QFileDevice (0x0x7fb60d5a67b8) + QObject (0x0x7fb60d18a180) 0 + primary-for QIODevice (0x0x7fb60d5a6820) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7fb60d18a360) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7fb60d5a6958) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7fb60d5a69c0) 0 + primary-for QFile (0x0x7fb60d5a6958) + QIODevice (0x0x7fb60d5a6a28) 0 + primary-for QFileDevice (0x0x7fb60d5a69c0) + QObject (0x0x7fb60d18a300) 0 + primary-for QIODevice (0x0x7fb60d5a6a28) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7fb60d18a480) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7fb60d18a720) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7fb60d18aa20) 0 + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7fb60d18ac00) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7fb60d5a6f08) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7fb60d18aba0) 0 + primary-for QFileSelector (0x0x7fb60d5a6f08) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7fb60d18acc0) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7fb60d5a6f70) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7fb60d18ac60) 0 + primary-for QFileSystemWatcher (0x0x7fb60d5a6f70) + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7fb60d18ad20) 0 + +Class QLoggingCategory::AtomicBools + size=3 align=1 + base size=3 base align=1 +QLoggingCategory::AtomicBools (0x0x7fb60d18aea0) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7fb60d18ae40) 0 + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7fb60d318060) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7fb60d318240) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7fb60d306138) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7fb60d3061a0) 0 + primary-for QProcess (0x0x7fb60d306138) + QObject (0x0x7fb60d3181e0) 0 + primary-for QIODevice (0x0x7fb60d3061a0) + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7fb60d3182a0) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7fb60d318420) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7fb60d306208) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7fb60d306270) 0 + primary-for QSaveFile (0x0x7fb60d306208) + QIODevice (0x0x7fb60d3062d8) 0 + primary-for QFileDevice (0x0x7fb60d306270) + QObject (0x0x7fb60d3183c0) 0 + primary-for QIODevice (0x0x7fb60d3062d8) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7fb60d3184e0) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7fb60d306340) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7fb60d318480) 0 + primary-for QSettings (0x0x7fb60d306340) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7fb60d318540) 0 empty + +Class QStorageInfo + size=8 align=8 + base size=8 base align=8 +QStorageInfo (0x0x7fb60d318660) 0 + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7fb60d318900) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7fb60d318a20) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7fb60d3064e0) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7fb60d306548) 0 + primary-for QTemporaryFile (0x0x7fb60d3064e0) + QFileDevice (0x0x7fb60d3065b0) 0 + primary-for QFile (0x0x7fb60d306548) + QIODevice (0x0x7fb60d306618) 0 + primary-for QFileDevice (0x0x7fb60d3065b0) + QObject (0x0x7fb60d3189c0) 0 + primary-for QIODevice (0x0x7fb60d306618) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7fb60d318b40) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7fb60d0bd060) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7fb60d0bd1e0) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7fb60d0bd300) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7fb60d0bd480) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 (int (*)(...))QAbstractItemModel::~QAbstractItemModel +48 (int (*)(...))QAbstractItemModel::~QAbstractItemModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7fb60d306af8) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7fb60d0bd420) 0 + primary-for QAbstractItemModel (0x0x7fb60d306af8) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7fb60d0bd7e0) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 (int (*)(...))QAbstractTableModel::~QAbstractTableModel +48 (int (*)(...))QAbstractTableModel::~QAbstractTableModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7fb60d306c98) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7fb60d306d00) 0 + primary-for QAbstractTableModel (0x0x7fb60d306c98) + QObject (0x0x7fb60d0bd780) 0 + primary-for QAbstractItemModel (0x0x7fb60d306d00) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7fb60d0bd8a0) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 (int (*)(...))QAbstractListModel::~QAbstractListModel +48 (int (*)(...))QAbstractListModel::~QAbstractListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7fb60d306d68) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7fb60d306dd0) 0 + primary-for QAbstractListModel (0x0x7fb60d306d68) + QObject (0x0x7fb60d0bd840) 0 + primary-for QAbstractItemModel (0x0x7fb60d306dd0) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7fb60d0bd960) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 (int (*)(...))QAbstractProxyModel::~QAbstractProxyModel +48 (int (*)(...))QAbstractProxyModel::~QAbstractProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QAbstractProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7fb60d306e38) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7fb60d306ea0) 0 + primary-for QAbstractProxyModel (0x0x7fb60d306e38) + QObject (0x0x7fb60d0bd900) 0 + primary-for QAbstractItemModel (0x0x7fb60d306ea0) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7fb60d0bda20) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7fb60d306f08) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7fb60d306f70) 0 + primary-for QIdentityProxyModel (0x0x7fb60d306f08) + QAbstractItemModel (0x0x7fb60cdee000) 0 + primary-for QAbstractProxyModel (0x0x7fb60d306f70) + QObject (0x0x7fb60d0bd9c0) 0 + primary-for QAbstractItemModel (0x0x7fb60cdee000) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7fb60d0bda80) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7fb60d0bdc00) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7fb60cdee0d0) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7fb60d0bdba0) 0 + primary-for QItemSelectionModel (0x0x7fb60cdee0d0) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7fb60cdee270) 0 + QList (0x0x7fb60cdee2d8) 0 + QListSpecialMethods (0x0x7fb60d0bde40) 0 empty + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7fb60d0bdf00) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7fb60cdee340) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7fb60cdee3a8) 0 + primary-for QSortFilterProxyModel (0x0x7fb60cdee340) + QAbstractItemModel (0x0x7fb60cdee410) 0 + primary-for QAbstractProxyModel (0x0x7fb60cdee3a8) + QObject (0x0x7fb60d0bdea0) 0 + primary-for QAbstractItemModel (0x0x7fb60cdee410) + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7fb60ceb8000) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7fb60cdee478) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7fb60cdee4e0) 0 + primary-for QStringListModel (0x0x7fb60cdee478) + QAbstractItemModel (0x0x7fb60cdee548) 0 + primary-for QAbstractListModel (0x0x7fb60cdee4e0) + QObject (0x0x7fb60d0bdf60) 0 + primary-for QAbstractItemModel (0x0x7fb60cdee548) + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7fb60ceb8060) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7fb60ceb8120) 0 + +Class QJsonValuePtr + size=24 align=8 + base size=24 base align=8 +QJsonValuePtr (0x0x7fb60ceb81e0) 0 + +Class QJsonValueRefPtr + size=16 align=8 + base size=16 base align=8 +QJsonValueRefPtr (0x0x7fb60ceb8240) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7fb60ceb8300) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7fb60ceb8360) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7fb60ceb82a0) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7fb60ceb83c0) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7fb60ceb8420) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7fb60ceb84e0) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7fb60ceb8540) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7fb60ceb8480) 0 + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7fb60ceb8660) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7fb60cdee5b0) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7fb60ceb8600) 0 + primary-for QEventLoop (0x0x7fb60cdee5b0) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7fb60ceb8780) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7fb60ceb8840) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7fb60ceb88a0) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 (int (*)(...))QAbstractEventDispatcher::~QAbstractEventDispatcher +48 (int (*)(...))QAbstractEventDispatcher::~QAbstractEventDispatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7fb60cdee6e8) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7fb60ceb87e0) 0 + primary-for QAbstractEventDispatcher (0x0x7fb60cdee6e8) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 (int (*)(...))QAbstractNativeEventFilter::~QAbstractNativeEventFilter +24 (int (*)(...))QAbstractNativeEventFilter::~QAbstractNativeEventFilter +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7fb60ceb8900) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7fb60ceb8960) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7fb60ceb8a80) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7fb60cdee7b8) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7fb60ceb8ae0) 0 + primary-for QTimerEvent (0x0x7fb60cdee7b8) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7fb60cdee820) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7fb60ceb8b40) 0 + primary-for QChildEvent (0x0x7fb60cdee820) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7fb60cdee888) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7fb60ceb8ba0) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7fb60cdee888) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7fb60cdee8f0) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7fb60ceb8c00) 0 + primary-for QDeferredDeleteEvent (0x0x7fb60cdee8f0) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7fb60ceb8cc0) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7fb60cdee958) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7fb60ceb8c60) 0 + primary-for QCoreApplication (0x0x7fb60cdee958) + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7fb60ceb8d20) 0 + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7fb60ceb8d80) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7fb60ceb8ea0) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7fb60ccff000) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7fb60ccff060) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7fb60ccff1e0) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7fb60cdeebc8) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7fb60ccff180) 0 + primary-for QMimeData (0x0x7fb60cdeebc8) + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7fb60ccff2a0) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7fb60cdeec30) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7fb60ccff240) 0 + primary-for QObjectCleanupHandler (0x0x7fb60cdeec30) + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7fb60ccff3c0) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7fb60ccff540) 0 + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7fb60ccffc00) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7fb60cdeee38) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7fb60ccffba0) 0 + primary-for QSharedMemory (0x0x7fb60cdeee38) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7fb60ccffcc0) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7fb60cdeeea0) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7fb60ccffc60) 0 + primary-for QSignalMapper (0x0x7fb60cdeeea0) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7fb60ccffd80) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7fb60cdeef08) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7fb60ccffd20) 0 + primary-for QSocketNotifier (0x0x7fb60cdeef08) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7fb60ccffde0) 0 + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7fb60ccfff00) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7fb60cdeef70) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7fb60ccffea0) 0 + primary-for QTimer (0x0x7fb60cdeef70) + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7fb60ca600c0) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7fb60ca62068) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7fb60ca60060) 0 + primary-for QTranslator (0x0x7fb60ca62068) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7fb60ca60120) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7fb60ca602a0) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 (int (*)(...))QFactoryInterface::~QFactoryInterface +24 (int (*)(...))QFactoryInterface::~QFactoryInterface +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7fb60ca60300) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7fb60ca60420) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7fb60ca62138) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7fb60ca603c0) 0 + primary-for QLibrary (0x0x7fb60ca62138) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7fb60ca60540) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7fb60ca606c0) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7fb60ca622d8) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7fb60ca60660) 0 + primary-for QPluginLoader (0x0x7fb60ca622d8) + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7fb60ca60720) 0 + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7fb60ca608a0) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 (int (*)(...))QAbstractState::~QAbstractState +48 (int (*)(...))QAbstractState::~QAbstractState +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7fb60ca623a8) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7fb60ca60840) 0 + primary-for QAbstractState (0x0x7fb60ca623a8) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7fb60ca60960) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 (int (*)(...))QAbstractTransition::~QAbstractTransition +48 (int (*)(...))QAbstractTransition::~QAbstractTransition +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7fb60ca62410) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7fb60ca60900) 0 + primary-for QAbstractTransition (0x0x7fb60ca62410) + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7fb60ca60a20) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7fb60ca62478) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7fb60ca624e0) 0 + primary-for QEventTransition (0x0x7fb60ca62478) + QObject (0x0x7fb60ca609c0) 0 + primary-for QAbstractTransition (0x0x7fb60ca624e0) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7fb60ca60ae0) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7fb60ca62548) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7fb60ca625b0) 0 + primary-for QFinalState (0x0x7fb60ca62548) + QObject (0x0x7fb60ca60a80) 0 + primary-for QAbstractState (0x0x7fb60ca625b0) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7fb60ca60ba0) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7fb60ca62618) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7fb60ca62680) 0 + primary-for QHistoryState (0x0x7fb60ca62618) + QObject (0x0x7fb60ca60b40) 0 + primary-for QAbstractState (0x0x7fb60ca62680) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7fb60ca60c60) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7fb60ca626e8) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7fb60ca62750) 0 + primary-for QSignalTransition (0x0x7fb60ca626e8) + QObject (0x0x7fb60ca60c00) 0 + primary-for QAbstractTransition (0x0x7fb60ca62750) + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7fb60ca60d20) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7fb60ca627b8) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7fb60ca62820) 0 + primary-for QState (0x0x7fb60ca627b8) + QObject (0x0x7fb60ca60cc0) 0 + primary-for QAbstractState (0x0x7fb60ca62820) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7fb60ca60e40) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7fb60ca629c0) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7fb60ca60ea0) 0 + primary-for QStateMachine::SignalEvent (0x0x7fb60ca629c0) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7fb60ca62a28) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7fb60ca60f00) 0 + primary-for QStateMachine::WrappedEvent (0x0x7fb60ca62a28) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7fb60ca62888) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7fb60ca628f0) 0 + primary-for QStateMachine (0x0x7fb60ca62888) + QAbstractState (0x0x7fb60ca62958) 0 + primary-for QState (0x0x7fb60ca628f0) + QObject (0x0x7fb60ca60de0) 0 + primary-for QAbstractState (0x0x7fb60ca62958) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7fb60ca62a90) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7fb60ca60f60) 0 nearly-empty + primary-for QException (0x0x7fb60ca62a90) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7fb60ca62af8) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7fb60ca62b60) 0 nearly-empty + primary-for QUnhandledException (0x0x7fb60ca62af8) + std::exception (0x0x7fb60c7a9000) 0 nearly-empty + primary-for QException (0x0x7fb60ca62b60) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7fb60c7a9060) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7fb60c7a9120) 0 + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QRunnable::~QRunnable +32 (int (*)(...))QRunnable::~QRunnable + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7fb60c7a9180) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7fb60c7a91e0) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7fb60ca62d00) 0 + QBasicMutex (0x0x7fb60c7a9300) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7fb60c7a9360) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7fb60c7a93c0) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7fb60c7a9420) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7fb60c7a95a0) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7fb60c7a9660) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7fb60c7a99c0) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 (int (*)(...))QFutureWatcherBase::~QFutureWatcherBase +48 (int (*)(...))QFutureWatcherBase::~QFutureWatcherBase +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7fb60c834618) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7fb60c7a9960) 0 + primary-for QFutureWatcherBase (0x0x7fb60c834618) + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7fb60c7a9ae0) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7fb60c7a9b40) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7fb60c7a9ba0) 0 + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7fb60c7a9c00) 0 + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7fb60c7a9cc0) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7fb60c8349c0) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7fb60c7a9c60) 0 + primary-for QThread (0x0x7fb60c8349c0) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7fb60c7a9d80) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7fb60c834a28) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7fb60c7a9d20) 0 + primary-for QThreadPool (0x0x7fb60c834a28) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7fb60c7a9de0) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7fb60c7a9ea0) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7fb60c581480) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7fb60c5814e0) 0 + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7fb60c581660) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7fb60c581600) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7fb60c5817e0) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7fb60c5818a0) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7fb60c581a80) 0 + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7fb60c581c00) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7fb60c581c60) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7fb60c581cc0) 0 + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7fb60c581d20) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7fb60c581e40) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7fb60c581f60) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7fb60c6f70c0) 0 + +Class QLinkedListData + size=32 align=8 + base size=32 base align=8 +QLinkedListData (0x0x7fb60c6f71e0) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7fb60c6f7540) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7fb60c6f7660) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7fb60c6f7780) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7fb60c6f7840) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7fb60c6f7960) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7fb60c6f7a80) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7fb60c6f7ba0) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7fb60c6f7cc0) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7fb60c20c000) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7fb60c20c180) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7fb60c20c3c0) 0 empty + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7fb60c20cde0) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7fb60c20cf60) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7fb60c2a0340) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7fb60c20cf00) 0 + primary-for QTimeLine (0x0x7fb60c2a0340) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7fb60c2fc060) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7fb60c2fc000) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7fb60c2fc2a0) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7fb60c2fc300) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7fb60c2a05b0) 0 + QVector (0x0x7fb60c2fc4e0) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7fb60c2fc540) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7fb60c2fc660) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7fb60c2fc780) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7fb60c2fc8a0) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7fb60c2fc900) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7fb60c2fca20) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7fb60c2fcb40) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7fb60c2fcde0) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7fb60bfed0c0) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7fb60bfed360) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7fb60bfed420) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7fb60bfed4e0) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 (int (*)(...))QGeoPositionInfoSource::~QGeoPositionInfoSource +48 (int (*)(...))QGeoPositionInfoSource::~QGeoPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7fb60c2a0888) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7fb60bfed480) 0 + primary-for QGeoPositionInfoSource (0x0x7fb60c2a0888) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7fb60bfed660) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 (int (*)(...))QGeoAreaMonitorSource::~QGeoAreaMonitorSource +48 (int (*)(...))QGeoAreaMonitorSource::~QGeoAreaMonitorSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7fb60c2a09c0) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7fb60bfed600) 0 + primary-for QGeoAreaMonitorSource (0x0x7fb60c2a09c0) + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7fb60c2a0a28) 0 + QGeoShape (0x0x7fb60bfed6c0) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7fb60bfed900) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7fb60bfedba0) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7fb60bfedc60) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 (int (*)(...))QGeoSatelliteInfoSource::~QGeoSatelliteInfoSource +48 (int (*)(...))QGeoSatelliteInfoSource::~QGeoSatelliteInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7fb60c2a0b60) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7fb60bfedc00) 0 + primary-for QGeoSatelliteInfoSource (0x0x7fb60c2a0b60) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 (int (*)(...))QGeoPositionInfoSourceFactory::~QGeoPositionInfoSourceFactory +24 (int (*)(...))QGeoPositionInfoSourceFactory::~QGeoPositionInfoSourceFactory +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7fb60bfedd20) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7fb60c2a0bc8) 0 + QGeoShape (0x0x7fb60bfedde0) 0 + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7fb60c0d21e0) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7fb60c2a0d00) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7fb60c2a0d68) 0 + primary-for QNmeaPositionInfoSource (0x0x7fb60c2a0d00) + QObject (0x0x7fb60c0d2180) 0 + primary-for QGeoPositionInfoSource (0x0x7fb60c2a0d68) + diff --git a/tests/auto/bic/data/QtPositioning.5.6.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.6.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..42ed838 --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.6.0.linux-gcc-amd64.txt @@ -0,0 +1,4118 @@ +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7f7583ec7ae0) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7f7583ec7b40) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7f7583f85780) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7f7583f857e0) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7f7583f02958) 0 empty + std::input_iterator_tag (0x0x7f7583f85840) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7f7583f029c0) 0 empty + std::forward_iterator_tag (0x0x7f7583f02a28) 0 empty + std::input_iterator_tag (0x0x7f7583f858a0) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7f7583f02a90) 0 empty + std::bidirectional_iterator_tag (0x0x7f7583f02af8) 0 empty + std::forward_iterator_tag (0x0x7f7583f02b60) 0 empty + std::input_iterator_tag (0x0x7f7583f85900) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_iter (0x0x7f7583f85de0) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_val (0x0x7f7583f85e40) 0 empty + +Class __gnu_cxx::__ops::_Val_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Val_less_iter (0x0x7f7583f85ea0) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7f7583f85f00) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7f7583f85f60) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7f75840199c0) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7f7584019c00) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7f7584019cc0) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7f7584019d20) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7f7584019de0) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7f7584019e40) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7f7582cc5300) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7f7582cc5360) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7f7582cc53c0) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7f7583f02e38) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7f7582cc5420) 0 nearly-empty + primary-for std::bad_exception (0x0x7f7583f02e38) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7f7583f02ea0) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7f7582cc5480) 0 nearly-empty + primary-for std::bad_alloc (0x0x7f7583f02ea0) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7f7582cc54e0) 0 empty + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7f7582a5ec00) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7f7582a5ec60) 0 + +Class QtPrivate::big_ + size=2 align=1 + base size=2 base align=1 +QtPrivate::big_ (0x0x7f7582a5ee40) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7f7582b81ea0) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7f7582b81f00) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7f7582b81f60) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7f7582be9000) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7f7582be9180) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7f7582bb2618) 0 + QAtomicInteger (0x0x7f7582bb2680) 0 + QBasicAtomicInteger (0x0x7f7582be9cc0) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7f75826b1de0) 0 empty + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7f75824ee2a0) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7f7582724888) 0 + QGenericArgument (0x0x7f75824ee300) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7f75824ee480) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7f75824ee540) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7f75824ee600) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7f75824ee660) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7f75824ee7e0) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7f75824ee8a0) 0 + +Class QtPrivate::QContainerImplHelper + size=1 align=1 + base size=0 base align=1 +QtPrivate::QContainerImplHelper (0x0x7f75824eecc0) 0 empty + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7f758227a060) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7f758227a0c0) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7f758227af60) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7f7582342000) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7f75823420c0) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7f7582342120) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7f7582342180) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7f75823421e0) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7f7582342300) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7f7582342360) 0 + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7f7582342c00) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7f7582342c60) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7f7582342cc0) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7f7582342d20) 0 + +Class std::__cow_string + size=8 align=8 + base size=8 base align=8 +std::__cow_string (0x0x7f7582200120) 0 + +Vtable for std::logic_error +std::logic_error::_ZTVSt11logic_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11logic_error) +16 (int (*)(...))std::logic_error::~logic_error +24 (int (*)(...))std::logic_error::~logic_error +32 (int (*)(...))std::logic_error::what + +Class std::logic_error + size=16 align=8 + base size=16 base align=8 +std::logic_error (0x0x7f7582337c30) 0 + vptr=((& std::logic_error::_ZTVSt11logic_error) + 16u) + std::exception (0x0x7f75822001e0) 0 nearly-empty + primary-for std::logic_error (0x0x7f7582337c30) + +Vtable for std::domain_error +std::domain_error::_ZTVSt12domain_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12domain_error) +16 (int (*)(...))std::domain_error::~domain_error +24 (int (*)(...))std::domain_error::~domain_error +32 (int (*)(...))std::logic_error::what + +Class std::domain_error + size=16 align=8 + base size=16 base align=8 +std::domain_error (0x0x7f7582337c98) 0 + vptr=((& std::domain_error::_ZTVSt12domain_error) + 16u) + std::logic_error (0x0x7f7582337d00) 0 + primary-for std::domain_error (0x0x7f7582337c98) + std::exception (0x0x7f7582200240) 0 nearly-empty + primary-for std::logic_error (0x0x7f7582337d00) + +Vtable for std::invalid_argument +std::invalid_argument::_ZTVSt16invalid_argument: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16invalid_argument) +16 (int (*)(...))std::invalid_argument::~invalid_argument +24 (int (*)(...))std::invalid_argument::~invalid_argument +32 (int (*)(...))std::logic_error::what + +Class std::invalid_argument + size=16 align=8 + base size=16 base align=8 +std::invalid_argument (0x0x7f7582337d68) 0 + vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16u) + std::logic_error (0x0x7f7582337dd0) 0 + primary-for std::invalid_argument (0x0x7f7582337d68) + std::exception (0x0x7f75822002a0) 0 nearly-empty + primary-for std::logic_error (0x0x7f7582337dd0) + +Vtable for std::length_error +std::length_error::_ZTVSt12length_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12length_error) +16 (int (*)(...))std::length_error::~length_error +24 (int (*)(...))std::length_error::~length_error +32 (int (*)(...))std::logic_error::what + +Class std::length_error + size=16 align=8 + base size=16 base align=8 +std::length_error (0x0x7f7582337e38) 0 + vptr=((& std::length_error::_ZTVSt12length_error) + 16u) + std::logic_error (0x0x7f7582337ea0) 0 + primary-for std::length_error (0x0x7f7582337e38) + std::exception (0x0x7f7582200300) 0 nearly-empty + primary-for std::logic_error (0x0x7f7582337ea0) + +Vtable for std::out_of_range +std::out_of_range::_ZTVSt12out_of_range: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12out_of_range) +16 (int (*)(...))std::out_of_range::~out_of_range +24 (int (*)(...))std::out_of_range::~out_of_range +32 (int (*)(...))std::logic_error::what + +Class std::out_of_range + size=16 align=8 + base size=16 base align=8 +std::out_of_range (0x0x7f7582337f08) 0 + vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16u) + std::logic_error (0x0x7f7582337f70) 0 + primary-for std::out_of_range (0x0x7f7582337f08) + std::exception (0x0x7f7582200360) 0 nearly-empty + primary-for std::logic_error (0x0x7f7582337f70) + +Vtable for std::runtime_error +std::runtime_error::_ZTVSt13runtime_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13runtime_error) +16 (int (*)(...))std::runtime_error::~runtime_error +24 (int (*)(...))std::runtime_error::~runtime_error +32 (int (*)(...))std::runtime_error::what + +Class std::runtime_error + size=16 align=8 + base size=16 base align=8 +std::runtime_error (0x0x7f7582337138) 0 + vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16u) + std::exception (0x0x7f75822003c0) 0 nearly-empty + primary-for std::runtime_error (0x0x7f7582337138) + +Vtable for std::range_error +std::range_error::_ZTVSt11range_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11range_error) +16 (int (*)(...))std::range_error::~range_error +24 (int (*)(...))std::range_error::~range_error +32 (int (*)(...))std::runtime_error::what + +Class std::range_error + size=16 align=8 + base size=16 base align=8 +std::range_error (0x0x7f7582337208) 0 + vptr=((& std::range_error::_ZTVSt11range_error) + 16u) + std::runtime_error (0x0x7f7582337270) 0 + primary-for std::range_error (0x0x7f7582337208) + std::exception (0x0x7f7582200420) 0 nearly-empty + primary-for std::runtime_error (0x0x7f7582337270) + +Vtable for std::overflow_error +std::overflow_error::_ZTVSt14overflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt14overflow_error) +16 (int (*)(...))std::overflow_error::~overflow_error +24 (int (*)(...))std::overflow_error::~overflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::overflow_error + size=16 align=8 + base size=16 base align=8 +std::overflow_error (0x0x7f75823372d8) 0 + vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16u) + std::runtime_error (0x0x7f75823373a8) 0 + primary-for std::overflow_error (0x0x7f75823372d8) + std::exception (0x0x7f7582200480) 0 nearly-empty + primary-for std::runtime_error (0x0x7f75823373a8) + +Vtable for std::underflow_error +std::underflow_error::_ZTVSt15underflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt15underflow_error) +16 (int (*)(...))std::underflow_error::~underflow_error +24 (int (*)(...))std::underflow_error::~underflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::underflow_error + size=16 align=8 + base size=16 base align=8 +std::underflow_error (0x0x7f75823374e0) 0 + vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16u) + std::runtime_error (0x0x7f75823375b0) 0 + primary-for std::underflow_error (0x0x7f75823374e0) + std::exception (0x0x7f75822004e0) 0 nearly-empty + primary-for std::runtime_error (0x0x7f75823375b0) + +Class std::ios_base::system_error::error_code + size=16 align=8 + base size=16 base align=8 +std::ios_base::system_error::error_code (0x0x7f7582200600) 0 + +Vtable for std::ios_base::system_error +std::ios_base::system_error::_ZTVNSt8ios_base12system_errorE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base12system_errorE) +16 (int (*)(...))std::ios_base::system_error::~system_error +24 (int (*)(...))std::ios_base::system_error::~system_error +32 (int (*)(...))std::runtime_error::what + +Class std::ios_base::system_error + size=32 align=8 + base size=32 base align=8 +std::ios_base::system_error (0x0x7f7582337958) 0 + vptr=((& std::ios_base::system_error::_ZTVNSt8ios_base12system_errorE) + 16u) + std::runtime_error (0x0x7f7582337a28) 0 + primary-for std::ios_base::system_error (0x0x7f7582337958) + std::exception (0x0x7f75822005a0) 0 nearly-empty + primary-for std::runtime_error (0x0x7f7582337a28) + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=32 align=8 + base size=32 base align=8 +std::ios_base::failure (0x0x7f7582249000) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16u) + std::ios_base::system_error (0x0x7f7582249068) 0 + primary-for std::ios_base::failure (0x0x7f7582249000) + std::runtime_error (0x0x7f75822490d0) 0 + primary-for std::ios_base::system_error (0x0x7f7582249068) + std::exception (0x0x7f7582200660) 0 nearly-empty + primary-for std::runtime_error (0x0x7f75822490d0) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7f75822006c0) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7f7582200720) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7f7582200780) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7f7582200540) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7f7582200ea0) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7f7581f505a0) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7f7581a5d750 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7f7581a5d820 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7f7581a5dbc8 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7f7581a5dc98 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7f7581cd5cc0) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7f7581cd5d20) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7f7581b5f360) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7f7581b5f540) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7f7581b5f780) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7f7581b5f840) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7f7581b5f7e0) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7f7581b5fea0) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7f75819b72a0) 0 + +Class QtPrivate::QHashCombine + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombine (0x0x7f75819b7660) 0 empty + +Class QtPrivate::QHashCombineCommutative + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombineCommutative (0x0x7f75819b76c0) 0 empty + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7f75819b7720) 0 + +Class QListData::NotArrayCompatibleLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotArrayCompatibleLayout (0x0x7f75819b7ae0) 0 empty + +Class QListData::NotIndirectLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotIndirectLayout (0x0x7f75819b7b40) 0 empty + +Class QListData::ArrayCompatibleLayout + size=1 align=1 + base size=1 base align=1 +QListData::ArrayCompatibleLayout (0x0x7f7581b4ec98) 0 empty + QListData::NotIndirectLayout (0x0x7f75819b7ba0) 0 empty + +Class QListData::InlineWithPaddingLayout + size=1 align=1 + base size=1 base align=1 +QListData::InlineWithPaddingLayout (0x0x7f75817db310) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7f75819b7c00) 0 empty + QListData::NotIndirectLayout (0x0x7f75819b7c60) 0 empty + +Class QListData::IndirectLayout + size=1 align=1 + base size=1 base align=1 +QListData::IndirectLayout (0x0x7f7581b4ed00) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7f75819b7cc0) 0 empty + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7f75819b7d20) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7f75819b7a80) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7f758184a900) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7f758184aae0) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7f758184aa80) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7f758152d068) 0 + QList (0x0x7f758152d0d0) 0 + QListSpecialMethods (0x0x7f758184acc0) 0 empty + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7f758184af00) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7f758158f600) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7f758152dd00) 0 + std::iterator (0x0x7f758158f6c0) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7f758152dd68) 0 + std::_Bit_iterator_base (0x0x7f758152ddd0) 0 + std::iterator (0x0x7f758158f720) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7f758152de38) 0 + std::_Bit_iterator_base (0x0x7f758152dea0) 0 + std::iterator (0x0x7f758158f780) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7f758158fb40) 0 + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7f758109f060) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7f758109f120) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7f758109f240) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7f758109f3c0) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7f758109f660) 0 + +Class QtMetaTypePrivate::VectorBoolElements + size=1 align=1 + base size=0 base align=1 +QtMetaTypePrivate::VectorBoolElements (0x0x7f758109f780) 0 empty + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7f75811d2180) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7f75811d2540) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7f75811d2780) 0 + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7f7580faa7e0) 0 + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7f7580faa960) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7f7580faab40) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7f7580faaae0) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7f7580faaea0) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7f7580faaf00) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7f7580caa000) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7f7580ca5000) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7f7580faaf60) 0 + primary-for QAbstractAnimation (0x0x7f7580ca5000) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7f7580caa0c0) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7f7580ca5068) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7f7580caa060) 0 + primary-for QAnimationDriver (0x0x7f7580ca5068) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7f7580caa180) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7f7580ca50d0) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7f7580ca5138) 0 + primary-for QAnimationGroup (0x0x7f7580ca50d0) + QObject (0x0x7f7580caa120) 0 + primary-for QAbstractAnimation (0x0x7f7580ca5138) + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7f7580caa240) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7f7580ca51a0) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7f7580ca5208) 0 + primary-for QParallelAnimationGroup (0x0x7f7580ca51a0) + QAbstractAnimation (0x0x7f7580ca5270) 0 + primary-for QAnimationGroup (0x0x7f7580ca5208) + QObject (0x0x7f7580caa1e0) 0 + primary-for QAbstractAnimation (0x0x7f7580ca5270) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7f7580caa300) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7f7580ca52d8) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7f7580ca5340) 0 + primary-for QPauseAnimation (0x0x7f7580ca52d8) + QObject (0x0x7f7580caa2a0) 0 + primary-for QAbstractAnimation (0x0x7f7580ca5340) + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7f7580d78660) 0 + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7f7580d787e0) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7f7580d788a0) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7f7580d78c60) 0 + +Class QHashData + size=48 align=8 + base size=48 base align=8 +QHashData (0x0x7f7580d78c00) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7f7580d78cc0) 0 empty + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7f7580afc720) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7f7580afc7e0) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7f7580afc780) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7f7580afc840) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7f7580afc6c0) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7f7580afcb40) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7f7580afcc00) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7f7580afcba0) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7f7580afccc0) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7f7580afcc60) 0 + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7f75808c9900) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7f75808f43a8) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7f75808f4410) 0 + primary-for QVariantAnimation (0x0x7f75808f43a8) + QObject (0x0x7f75808c98a0) 0 + primary-for QAbstractAnimation (0x0x7f75808f4410) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7f75808c99c0) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7f75808f44e0) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7f75808f4548) 0 + primary-for QPropertyAnimation (0x0x7f75808f44e0) + QAbstractAnimation (0x0x7f75808f45b0) 0 + primary-for QVariantAnimation (0x0x7f75808f4548) + QObject (0x0x7f75808c9960) 0 + primary-for QAbstractAnimation (0x0x7f75808f45b0) + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7f75808c9a80) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7f75808f4618) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7f75808f4680) 0 + primary-for QSequentialAnimationGroup (0x0x7f75808f4618) + QAbstractAnimation (0x0x7f75808f46e8) 0 + primary-for QAnimationGroup (0x0x7f75808f4680) + QObject (0x0x7f75808c9a20) 0 + primary-for QAbstractAnimation (0x0x7f75808f46e8) + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7f75808c9b40) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 0u +64 0u + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7f75808c9ae0) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7f75808c9c60) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7f75808c9cc0) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7f75808c9d20) 0 + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7f75808c9f00) 0 empty + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7f7580a066c0) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7f7580a06840) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7f7580a069c0) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7f7580a06ba0) 0 empty + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7f7580a06c60) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7f75808f49c0) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7f7580a06c00) 0 + primary-for QIODevice (0x0x7f75808f49c0) + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7f7580a06de0) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7f75808f4af8) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7f75808f4b60) 0 + primary-for QBuffer (0x0x7f75808f4af8) + QObject (0x0x7f7580a06d80) 0 + primary-for QIODevice (0x0x7f75808f4b60) + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7f7580a06e40) 0 + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7f7580a06f00) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7f75807fb2a0) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7f75807fb300) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7f75807fb3c0) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7f75807fb5a0) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7f75807fb7e0) 0 + +Class QDebug::Stream + size=80 align=8 + base size=76 base align=8 +QDebug::Stream (0x0x7f75807fbae0) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7f75807fba80) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7f75807fbc60) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7f75807fbd20) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7f75807fbf00) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7f758055d340) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7f758055d3a8) 0 + primary-for QFileDevice (0x0x7f758055d340) + QObject (0x0x7f75807fbea0) 0 + primary-for QIODevice (0x0x7f758055d3a8) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7f75806330c0) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7f758055d4e0) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7f758055d548) 0 + primary-for QFile (0x0x7f758055d4e0) + QIODevice (0x0x7f758055d5b0) 0 + primary-for QFileDevice (0x0x7f758055d548) + QObject (0x0x7f7580633060) 0 + primary-for QIODevice (0x0x7f758055d5b0) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7f75806331e0) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7f75806334e0) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7f7580633840) 0 + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7f7580633a20) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7f758055da90) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7f75806339c0) 0 + primary-for QFileSelector (0x0x7f758055da90) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7f7580633ae0) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7f758055daf8) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7f7580633a80) 0 + primary-for QFileSystemWatcher (0x0x7f758055daf8) + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7f7580633b40) 0 + +Class QLoggingCategory::AtomicBools + size=4 align=1 + base size=4 base align=1 +QLoggingCategory::AtomicBools (0x0x7f7580633cc0) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7f7580633c60) 0 + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7f7580633e40) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7f75803880c0) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7f758055dd00) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7f758055dd68) 0 + primary-for QProcess (0x0x7f758055dd00) + QObject (0x0x7f7580388060) 0 + primary-for QIODevice (0x0x7f758055dd68) + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7f7580388120) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7f75803882a0) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7f758055ddd0) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7f758055de38) 0 + primary-for QSaveFile (0x0x7f758055ddd0) + QIODevice (0x0x7f758055dea0) 0 + primary-for QFileDevice (0x0x7f758055de38) + QObject (0x0x7f7580388240) 0 + primary-for QIODevice (0x0x7f758055dea0) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7f7580388360) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7f758055df08) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7f7580388300) 0 + primary-for QSettings (0x0x7f758055df08) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7f75803883c0) 0 empty + +Class QStorageInfo + size=8 align=8 + base size=8 base align=8 +QStorageInfo (0x0x7f75803884e0) 0 + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7f75803887e0) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7f7580388900) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7f758043b068) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7f758043b0d0) 0 + primary-for QTemporaryFile (0x0x7f758043b068) + QFileDevice (0x0x7f758043b138) 0 + primary-for QFile (0x0x7f758043b0d0) + QIODevice (0x0x7f758043b1a0) 0 + primary-for QFileDevice (0x0x7f758043b138) + QObject (0x0x7f75803888a0) 0 + primary-for QIODevice (0x0x7f758043b1a0) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7f7580388a20) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7f7580388e40) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7f7580144060) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7f75801441e0) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7f75801443c0) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7f758043b680) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7f7580144360) 0 + primary-for QAbstractItemModel (0x0x7f758043b680) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7f7580144720) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractTableModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7f758043b888) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7f758043b8f0) 0 + primary-for QAbstractTableModel (0x0x7f758043b888) + QObject (0x0x7f75801446c0) 0 + primary-for QAbstractItemModel (0x0x7f758043b8f0) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7f75801447e0) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractListModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7f758043b958) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7f758043b9c0) 0 + primary-for QAbstractListModel (0x0x7f758043b958) + QObject (0x0x7f7580144780) 0 + primary-for QAbstractItemModel (0x0x7f758043b9c0) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7f7580144ae0) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QAbstractProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7f758043baf8) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7f758043bb60) 0 + primary-for QAbstractProxyModel (0x0x7f758043baf8) + QObject (0x0x7f7580144a80) 0 + primary-for QAbstractItemModel (0x0x7f758043bb60) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7f7580144ba0) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7f758043bbc8) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7f758043bc30) 0 + primary-for QIdentityProxyModel (0x0x7f758043bbc8) + QAbstractItemModel (0x0x7f758043bc98) 0 + primary-for QAbstractProxyModel (0x0x7f758043bc30) + QObject (0x0x7f7580144b40) 0 + primary-for QAbstractItemModel (0x0x7f758043bc98) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7f7580144c00) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7f7580144de0) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7f758043bdd0) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7f7580144d80) 0 + primary-for QItemSelectionModel (0x0x7f758043bdd0) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7f757ff17000) 0 + QList (0x0x7f757ff17068) 0 + QListSpecialMethods (0x0x7f757fef2060) 0 empty + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7f757fef2480) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7f757ff17138) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7f757ff171a0) 0 + primary-for QSortFilterProxyModel (0x0x7f757ff17138) + QAbstractItemModel (0x0x7f757ff17208) 0 + primary-for QAbstractProxyModel (0x0x7f757ff171a0) + QObject (0x0x7f757fef2420) 0 + primary-for QAbstractItemModel (0x0x7f757ff17208) + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7f757fef2540) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7f757ff17270) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7f757ff172d8) 0 + primary-for QStringListModel (0x0x7f757ff17270) + QAbstractItemModel (0x0x7f757ff17340) 0 + primary-for QAbstractListModel (0x0x7f757ff172d8) + QObject (0x0x7f757fef24e0) 0 + primary-for QAbstractItemModel (0x0x7f757ff17340) + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7f757fef25a0) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7f757fef2660) 0 + +Class QJsonValuePtr + size=24 align=8 + base size=24 base align=8 +QJsonValuePtr (0x0x7f757fef2720) 0 + +Class QJsonValueRefPtr + size=16 align=8 + base size=16 base align=8 +QJsonValueRefPtr (0x0x7f757fef2780) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7f757fef2840) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7f757fef28a0) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7f757fef27e0) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7f757fef2900) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7f757fef2960) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7f757fef2a20) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7f757fef2a80) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7f757fef29c0) 0 + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7f757fef2ba0) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7f757ff173a8) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7f757fef2b40) 0 + primary-for QEventLoop (0x0x7f757ff173a8) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7f757fef2cc0) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7f757fef2d80) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7f757fef2de0) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7f757ff174e0) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7f757fef2d20) 0 + primary-for QAbstractEventDispatcher (0x0x7f757ff174e0) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7f757fef2e40) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7f757fef2ea0) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7f757fcc7060) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7f757ff175b0) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7f757fcc70c0) 0 + primary-for QTimerEvent (0x0x7f757ff175b0) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7f757ff17618) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7f757fcc7120) 0 + primary-for QChildEvent (0x0x7f757ff17618) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7f757ff17680) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7f757fcc7180) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7f757ff17680) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7f757ff176e8) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7f757fcc71e0) 0 + primary-for QDeferredDeleteEvent (0x0x7f757ff176e8) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7f757fcc72a0) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7f757ff17750) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7f757fcc7240) 0 + primary-for QCoreApplication (0x0x7f757ff17750) + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7f757fcc7300) 0 + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7f757fcc7480) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7f757fcc7600) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7f757fcc7780) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7f757fcc77e0) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7f757fcc79c0) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7f757ff178f0) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7f757fcc7960) 0 + primary-for QMimeData (0x0x7f757ff178f0) + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7f757fcc7a80) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7f757ff17958) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7f757fcc7a20) 0 + primary-for QObjectCleanupHandler (0x0x7f757ff17958) + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7f757fcc7ae0) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7f757fcc7c60) 0 + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7f757fad12a0) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7f757ff17d68) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7f757fad1240) 0 + primary-for QSharedMemory (0x0x7f757ff17d68) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7f757fad1360) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7f757ff17dd0) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7f757fad1300) 0 + primary-for QSignalMapper (0x0x7f757ff17dd0) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7f757fad1420) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7f757ff17e38) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7f757fad13c0) 0 + primary-for QSocketNotifier (0x0x7f757ff17e38) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7f757fad1480) 0 + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7f757fad15a0) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7f757ff17ea0) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7f757fad1540) 0 + primary-for QTimer (0x0x7f757ff17ea0) + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7f757fad1720) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7f757ff17f70) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7f757fad16c0) 0 + primary-for QTranslator (0x0x7f757ff17f70) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7f757fad1780) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7f757fad1960) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7f757fad19c0) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7f757fad1ae0) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7f757ff17c98) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7f757fad1a80) 0 + primary-for QLibrary (0x0x7f757ff17c98) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7f757fad1c00) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7f757fad1de0) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7f757fbab0d0) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7f757fad1d80) 0 + primary-for QPluginLoader (0x0x7f757fbab0d0) + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7f757fad1e40) 0 + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7f757fbf7060) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7f757fbab1a0) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7f757fbf7000) 0 + primary-for QAbstractState (0x0x7f757fbab1a0) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7f757fbf7120) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7f757fbab208) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7f757fbf70c0) 0 + primary-for QAbstractTransition (0x0x7f757fbab208) + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7f757fbf71e0) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7f757fbab270) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7f757fbab2d8) 0 + primary-for QEventTransition (0x0x7f757fbab270) + QObject (0x0x7f757fbf7180) 0 + primary-for QAbstractTransition (0x0x7f757fbab2d8) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7f757fbf72a0) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7f757fbab340) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7f757fbab3a8) 0 + primary-for QFinalState (0x0x7f757fbab340) + QObject (0x0x7f757fbf7240) 0 + primary-for QAbstractState (0x0x7f757fbab3a8) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7f757fbf7360) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7f757fbab410) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7f757fbab478) 0 + primary-for QHistoryState (0x0x7f757fbab410) + QObject (0x0x7f757fbf7300) 0 + primary-for QAbstractState (0x0x7f757fbab478) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7f757fbf7420) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7f757fbab4e0) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7f757fbab548) 0 + primary-for QSignalTransition (0x0x7f757fbab4e0) + QObject (0x0x7f757fbf73c0) 0 + primary-for QAbstractTransition (0x0x7f757fbab548) + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7f757fbf74e0) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7f757fbab5b0) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7f757fbab618) 0 + primary-for QState (0x0x7f757fbab5b0) + QObject (0x0x7f757fbf7480) 0 + primary-for QAbstractState (0x0x7f757fbab618) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7f757fbf7600) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7f757fbab7b8) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7f757fbf7660) 0 + primary-for QStateMachine::SignalEvent (0x0x7f757fbab7b8) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7f757fbab820) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7f757fbf76c0) 0 + primary-for QStateMachine::WrappedEvent (0x0x7f757fbab820) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7f757fbab680) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7f757fbab6e8) 0 + primary-for QStateMachine (0x0x7f757fbab680) + QAbstractState (0x0x7f757fbab750) 0 + primary-for QState (0x0x7f757fbab6e8) + QObject (0x0x7f757fbf75a0) 0 + primary-for QAbstractState (0x0x7f757fbab750) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7f757fbab888) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7f757fbf7720) 0 nearly-empty + primary-for QException (0x0x7f757fbab888) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7f757fbab8f0) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7f757fbab958) 0 nearly-empty + primary-for QUnhandledException (0x0x7f757fbab8f0) + std::exception (0x0x7f757fbf7780) 0 nearly-empty + primary-for QException (0x0x7f757fbab958) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7f757fbf77e0) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7f757fbf78a0) 0 + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7f757fbf7900) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7f757fbf7960) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7f757fbabaf8) 0 + QBasicMutex (0x0x7f757fbf7ae0) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7f757fbf7b40) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7f757fbf7c00) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7f757fbf7c60) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7f757fbf7de0) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7f757fbf7ea0) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7f757f976240) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 0u +48 0u +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7f757f9373a8) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7f757f9761e0) 0 + primary-for QFutureWatcherBase (0x0x7f757f9373a8) + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7f757f976360) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7f757f9763c0) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7f757f976420) 0 + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7f757f976480) 0 + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7f757f976540) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7f757f937820) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7f757f9764e0) 0 + primary-for QThread (0x0x7f757f937820) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7f757f976600) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7f757f937888) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7f757f9765a0) 0 + primary-for QThreadPool (0x0x7f757f937888) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7f757f976660) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7f757f976720) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7f757f976c00) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7f757f976de0) 0 + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7f757f6fb060) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7f757f6fb000) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7f757f6fb1e0) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7f757f6fb2a0) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7f757f6fb540) 0 + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7f757f6fb720) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7f757f6fb780) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7f757f6fb7e0) 0 + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7f757f6fb840) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7f757f6fb9c0) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7f757f6fbb40) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7f757f6fbcc0) 0 + +Class QLinkedListData + size=32 align=8 + base size=32 base align=8 +QLinkedListData (0x0x7f757f6fbe40) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7f757f44d600) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7f757f44d780) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7f757f44d900) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7f757f44d9c0) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7f757f44dc00) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7f757f44de40) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7f757f631000) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7f757f631180) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7f757f6314e0) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7f757f6316c0) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7f757f631a80) 0 empty + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7f757f2f04e0) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7f757f2f0660) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7f757f258bc8) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7f757f2f0600) 0 + primary-for QTimeLine (0x0x7f757f258bc8) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7f757f2f0720) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7f757f2f06c0) 0 + +Class QVersionNumber::SegmentStorage + size=8 align=8 + base size=8 base align=8 +QVersionNumber::SegmentStorage (0x0x7f757f2f0a80) 0 + +Class QVersionNumber + size=8 align=8 + base size=8 base align=8 +QVersionNumber (0x0x7f757f2f0a20) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7f757f2f0d80) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7f757f2f0f00) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7f757f02d068) 0 + QVector (0x0x7f757f012180) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7f757f0121e0) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7f757f012360) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7f757f0124e0) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7f757f012660) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7f757f0126c0) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7f757f0127e0) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7f757f012900) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7f757f012c00) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7f757f012f00) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7f757f118240) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7f757f118300) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7f757f1183c0) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7f757f02d340) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7f757f118360) 0 + primary-for QGeoPositionInfoSource (0x0x7f757f02d340) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7f757f118540) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7f757f02d478) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7f757f1184e0) 0 + primary-for QGeoAreaMonitorSource (0x0x7f757f02d478) + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7f757f02d4e0) 0 + QGeoShape (0x0x7f757f1185a0) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7f757f118840) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7f757f118b40) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7f757f118c00) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7f757f02d618) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7f757f118ba0) 0 + primary-for QGeoSatelliteInfoSource (0x0x7f757f02d618) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7f757f118cc0) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7f757f02d680) 0 + QGeoShape (0x0x7f757f118d80) 0 + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7f757eded1e0) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7f757f02d820) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7f757f02d888) 0 + primary-for QNmeaPositionInfoSource (0x0x7f757f02d820) + QObject (0x0x7f757eded180) 0 + primary-for QGeoPositionInfoSource (0x0x7f757f02d888) + diff --git a/tests/auto/bic/data/QtPositioning.5.7.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.7.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..cf3f81f --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.7.0.linux-gcc-amd64.txt @@ -0,0 +1,4400 @@ +Class std::__failure_type + size=1 align=1 + base size=0 base align=1 +std::__failure_type (0x0x7ff717ebbd80) 0 empty + +Class std::__do_is_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_destructible_impl (0x0x7ff717f63540) 0 empty + +Class std::__do_is_nt_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nt_destructible_impl (0x0x7ff717f63780) 0 empty + +Class std::__do_is_default_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_default_constructible_impl (0x0x7ff717f639c0) 0 empty + +Class std::__do_is_static_castable_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_static_castable_impl (0x0x7ff717f63c00) 0 empty + +Class std::__do_is_direct_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_direct_constructible_impl (0x0x7ff717f63d80) 0 empty + +Class std::__do_is_nary_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nary_constructible_impl (0x0x7ff717f97180) 0 empty + +Class std::__do_common_type_impl + size=1 align=1 + base size=0 base align=1 +std::__do_common_type_impl (0x0x7ff715c1b900) 0 empty + +Class std::__do_member_type_wrapper + size=1 align=1 + base size=0 base align=1 +std::__do_member_type_wrapper (0x0x7ff715c1b9c0) 0 empty + +Class std::__result_of_memfun_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_ref_impl (0x0x7ff715c1bd20) 0 empty + +Class std::__result_of_memfun_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_deref_impl (0x0x7ff715c1bde0) 0 empty + +Class std::__result_of_memobj_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_ref_impl (0x0x7ff715c1bea0) 0 empty + +Class std::__result_of_memobj_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_deref_impl (0x0x7ff715c1bf60) 0 empty + +Class std::__result_of_other_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_other_impl (0x0x7ff715c52240) 0 empty + +Class std::piecewise_construct_t + size=1 align=1 + base size=0 base align=1 +std::piecewise_construct_t (0x0x7ff715c523c0) 0 empty + +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7ff715c52840) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7ff715c528a0) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7ff715cfb540) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7ff715cfb5a0) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7ff715c1f750) 0 empty + std::input_iterator_tag (0x0x7ff715cfb600) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7ff715c1f7b8) 0 empty + std::forward_iterator_tag (0x0x7ff715c1f820) 0 empty + std::input_iterator_tag (0x0x7ff715cfb660) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7ff715c1f888) 0 empty + std::bidirectional_iterator_tag (0x0x7ff715c1f8f0) 0 empty + std::forward_iterator_tag (0x0x7ff715c1f958) 0 empty + std::input_iterator_tag (0x0x7ff715cfb6c0) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_iter (0x0x7ff715d3d360) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_val (0x0x7ff715d3d3c0) 0 empty + +Class __gnu_cxx::__ops::_Val_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Val_less_iter (0x0x7ff715d3d420) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7ff715d3d480) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7ff715d3d4e0) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7ff715a65000) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7ff715a65240) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7ff715a65300) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7ff715a65360) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7ff715a65420) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7ff715a65480) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7ff715a65900) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7ff715a65960) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7ff715a659c0) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7ff715c1fea0) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7ff715a65a20) 0 nearly-empty + primary-for std::bad_exception (0x0x7ff715c1fea0) + +Class std::__exception_ptr::exception_ptr + size=8 align=8 + base size=8 base align=8 +std::__exception_ptr::exception_ptr (0x0x7ff715a65a80) 0 + +Vtable for std::nested_exception +std::nested_exception::_ZTVSt16nested_exception: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16nested_exception) +16 (int (*)(...))std::nested_exception::~nested_exception +24 (int (*)(...))std::nested_exception::~nested_exception + +Class std::nested_exception + size=16 align=8 + base size=16 base align=8 +std::nested_exception (0x0x7ff715a65ae0) 0 + vptr=((& std::nested_exception::_ZTVSt16nested_exception) + 16u) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7ff715b6f0d0) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7ff715a65f00) 0 nearly-empty + primary-for std::bad_alloc (0x0x7ff715b6f0d0) + +Vtable for std::bad_array_new_length +std::bad_array_new_length::_ZTVSt20bad_array_new_length: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt20bad_array_new_length) +16 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +24 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +32 (int (*)(...))std::bad_array_new_length::what + +Class std::bad_array_new_length + size=8 align=8 + base size=8 base align=8 +std::bad_array_new_length (0x0x7ff715b6f138) 0 nearly-empty + vptr=((& std::bad_array_new_length::_ZTVSt20bad_array_new_length) + 16u) + std::bad_alloc (0x0x7ff715b6f1a0) 0 nearly-empty + primary-for std::bad_array_new_length (0x0x7ff715b6f138) + std::exception (0x0x7ff715a65f60) 0 nearly-empty + primary-for std::bad_alloc (0x0x7ff715b6f1a0) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7ff715b81000) 0 empty + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7ff715b81c00) 0 + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7ff715982900) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7ff715982960) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7ff715678840) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7ff7156788a0) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7ff715678960) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7ff7156789c0) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7ff715678a20) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7ff715678a80) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7ff715678ba0) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7ff715678c00) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7ff71542f060) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7ff71542f0c0) 0 + +Class std::_Hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Hash_impl (0x0x7ff7151ca8a0) 0 empty + +Class std::_Fnv_hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Fnv_hash_impl (0x0x7ff7151ca900) 0 empty + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7ff7151fc8a0) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7ff714ff96c0) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7ff7151d5f08) 0 + std::iterator (0x0x7ff714ff9780) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7ff7151d5f70) 0 + std::_Bit_iterator_base (0x0x7ff7151d56e8) 0 + std::iterator (0x0x7ff714ff97e0) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7ff7151d5750) 0 + std::_Bit_iterator_base (0x0x7ff7151d5a28) 0 + std::iterator (0x0x7ff714ff9840) 0 empty + +Class std::random_device + size=5000 align=8 + base size=5000 base align=8 +std::random_device (0x0x7ff714e26660) 0 + +Class std::bernoulli_distribution::param_type + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution::param_type (0x0x7ff714f21420) 0 + +Class std::bernoulli_distribution + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution (0x0x7ff714f213c0) 0 + +Class std::seed_seq + size=24 align=8 + base size=24 base align=8 +std::seed_seq (0x0x7ff714cc13c0) 0 + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7ff713a87e40) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7ff713a87ea0) 0 + +Class QtPrivate::big_ + size=2 align=1 + base size=2 base align=1 +QtPrivate::big_ (0x0x7ff71383c2a0) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7ff7138f3780) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7ff7138f37e0) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7ff7138f3840) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7ff7138f38a0) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7ff7138f3a20) 0 + +Class std::__atomic_flag_base + size=1 align=1 + base size=1 base align=1 +std::__atomic_flag_base (0x0x7ff7138f3e40) 0 + +Class std::atomic_flag + size=1 align=1 + base size=1 base align=1 +std::atomic_flag (0x0x7ff713948138) 0 + std::__atomic_flag_base (0x0x7ff7138f3ea0) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7ff713948888) 0 + QAtomicInteger (0x0x7ff7139488f0) 0 + QBasicAtomicInteger (0x0x7ff71349c420) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7ff7132d4cc0) 0 empty + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7ff713115ba0) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7ff713291b60) 0 + QGenericArgument (0x0x7ff713115c00) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7ff713115d80) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7ff713115e40) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7ff712df1ea0) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7ff712df1f00) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7ff712e970c0) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7ff712e97180) 0 + +Class QtPrivate::QContainerImplHelper + size=1 align=1 + base size=0 base align=1 +QtPrivate::QContainerImplHelper (0x0x7ff712e975a0) 0 empty + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7ff712e97600) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7ff712e97660) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7ff712e976c0) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7ff712e97720) 0 + +Class std::__cow_string + size=8 align=8 + base size=8 base align=8 +std::__cow_string (0x0x7ff712e97ae0) 0 + +Vtable for std::logic_error +std::logic_error::_ZTVSt11logic_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11logic_error) +16 (int (*)(...))std::logic_error::~logic_error +24 (int (*)(...))std::logic_error::~logic_error +32 (int (*)(...))std::logic_error::what + +Class std::logic_error + size=16 align=8 + base size=16 base align=8 +std::logic_error (0x0x7ff712e10d00) 0 + vptr=((& std::logic_error::_ZTVSt11logic_error) + 16u) + std::exception (0x0x7ff712e97ba0) 0 nearly-empty + primary-for std::logic_error (0x0x7ff712e10d00) + +Vtable for std::domain_error +std::domain_error::_ZTVSt12domain_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12domain_error) +16 (int (*)(...))std::domain_error::~domain_error +24 (int (*)(...))std::domain_error::~domain_error +32 (int (*)(...))std::logic_error::what + +Class std::domain_error + size=16 align=8 + base size=16 base align=8 +std::domain_error (0x0x7ff712e10dd0) 0 + vptr=((& std::domain_error::_ZTVSt12domain_error) + 16u) + std::logic_error (0x0x7ff712fa9000) 0 + primary-for std::domain_error (0x0x7ff712e10dd0) + std::exception (0x0x7ff712e97c00) 0 nearly-empty + primary-for std::logic_error (0x0x7ff712fa9000) + +Vtable for std::invalid_argument +std::invalid_argument::_ZTVSt16invalid_argument: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16invalid_argument) +16 (int (*)(...))std::invalid_argument::~invalid_argument +24 (int (*)(...))std::invalid_argument::~invalid_argument +32 (int (*)(...))std::logic_error::what + +Class std::invalid_argument + size=16 align=8 + base size=16 base align=8 +std::invalid_argument (0x0x7ff712fa9068) 0 + vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16u) + std::logic_error (0x0x7ff712fa90d0) 0 + primary-for std::invalid_argument (0x0x7ff712fa9068) + std::exception (0x0x7ff712e97c60) 0 nearly-empty + primary-for std::logic_error (0x0x7ff712fa90d0) + +Vtable for std::length_error +std::length_error::_ZTVSt12length_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12length_error) +16 (int (*)(...))std::length_error::~length_error +24 (int (*)(...))std::length_error::~length_error +32 (int (*)(...))std::logic_error::what + +Class std::length_error + size=16 align=8 + base size=16 base align=8 +std::length_error (0x0x7ff712fa9138) 0 + vptr=((& std::length_error::_ZTVSt12length_error) + 16u) + std::logic_error (0x0x7ff712fa91a0) 0 + primary-for std::length_error (0x0x7ff712fa9138) + std::exception (0x0x7ff712e97cc0) 0 nearly-empty + primary-for std::logic_error (0x0x7ff712fa91a0) + +Vtable for std::out_of_range +std::out_of_range::_ZTVSt12out_of_range: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12out_of_range) +16 (int (*)(...))std::out_of_range::~out_of_range +24 (int (*)(...))std::out_of_range::~out_of_range +32 (int (*)(...))std::logic_error::what + +Class std::out_of_range + size=16 align=8 + base size=16 base align=8 +std::out_of_range (0x0x7ff712fa9208) 0 + vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16u) + std::logic_error (0x0x7ff712fa9270) 0 + primary-for std::out_of_range (0x0x7ff712fa9208) + std::exception (0x0x7ff712e97d20) 0 nearly-empty + primary-for std::logic_error (0x0x7ff712fa9270) + +Vtable for std::runtime_error +std::runtime_error::_ZTVSt13runtime_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13runtime_error) +16 (int (*)(...))std::runtime_error::~runtime_error +24 (int (*)(...))std::runtime_error::~runtime_error +32 (int (*)(...))std::runtime_error::what + +Class std::runtime_error + size=16 align=8 + base size=16 base align=8 +std::runtime_error (0x0x7ff712fa92d8) 0 + vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16u) + std::exception (0x0x7ff712e97d80) 0 nearly-empty + primary-for std::runtime_error (0x0x7ff712fa92d8) + +Vtable for std::range_error +std::range_error::_ZTVSt11range_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11range_error) +16 (int (*)(...))std::range_error::~range_error +24 (int (*)(...))std::range_error::~range_error +32 (int (*)(...))std::runtime_error::what + +Class std::range_error + size=16 align=8 + base size=16 base align=8 +std::range_error (0x0x7ff712fa9340) 0 + vptr=((& std::range_error::_ZTVSt11range_error) + 16u) + std::runtime_error (0x0x7ff712fa93a8) 0 + primary-for std::range_error (0x0x7ff712fa9340) + std::exception (0x0x7ff712e97de0) 0 nearly-empty + primary-for std::runtime_error (0x0x7ff712fa93a8) + +Vtable for std::overflow_error +std::overflow_error::_ZTVSt14overflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt14overflow_error) +16 (int (*)(...))std::overflow_error::~overflow_error +24 (int (*)(...))std::overflow_error::~overflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::overflow_error + size=16 align=8 + base size=16 base align=8 +std::overflow_error (0x0x7ff712fa9410) 0 + vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16u) + std::runtime_error (0x0x7ff712fa9478) 0 + primary-for std::overflow_error (0x0x7ff712fa9410) + std::exception (0x0x7ff712e97e40) 0 nearly-empty + primary-for std::runtime_error (0x0x7ff712fa9478) + +Vtable for std::underflow_error +std::underflow_error::_ZTVSt15underflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt15underflow_error) +16 (int (*)(...))std::underflow_error::~underflow_error +24 (int (*)(...))std::underflow_error::~underflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::underflow_error + size=16 align=8 + base size=16 base align=8 +std::underflow_error (0x0x7ff712fa94e0) 0 + vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16u) + std::runtime_error (0x0x7ff712fa9548) 0 + primary-for std::underflow_error (0x0x7ff712fa94e0) + std::exception (0x0x7ff712e97ea0) 0 nearly-empty + primary-for std::runtime_error (0x0x7ff712fa9548) + +Vtable for std::_V2::error_category +std::_V2::error_category::_ZTVNSt3_V214error_categoryE: 10u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt3_V214error_categoryE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))std::_V2::error_category::_M_message +48 (int (*)(...))__cxa_pure_virtual +56 (int (*)(...))std::_V2::error_category::default_error_condition +64 (int (*)(...))std::_V2::error_category::equivalent +72 (int (*)(...))std::_V2::error_category::equivalent + +Class std::_V2::error_category + size=8 align=8 + base size=8 base align=8 +std::_V2::error_category (0x0x7ff712bcb060) 0 nearly-empty + vptr=((& std::_V2::error_category::_ZTVNSt3_V214error_categoryE) + 16u) + +Class std::error_code + size=16 align=8 + base size=16 base align=8 +std::error_code (0x0x7ff712bcb2a0) 0 + +Class std::error_condition + size=16 align=8 + base size=16 base align=8 +std::error_condition (0x0x7ff712bcb420) 0 + +Vtable for std::system_error +std::system_error::_ZTVSt12system_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12system_error) +16 (int (*)(...))std::system_error::~system_error +24 (int (*)(...))std::system_error::~system_error +32 (int (*)(...))std::runtime_error::what + +Class std::system_error + size=32 align=8 + base size=32 base align=8 +std::system_error (0x0x7ff712fa9a28) 0 + vptr=((& std::system_error::_ZTVSt12system_error) + 16u) + std::runtime_error (0x0x7ff712fa9a90) 0 + primary-for std::system_error (0x0x7ff712fa9a28) + std::exception (0x0x7ff712bcb660) 0 nearly-empty + primary-for std::runtime_error (0x0x7ff712fa9a90) + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=32 align=8 + base size=32 base align=8 +std::ios_base::failure (0x0x7ff712c27680) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16u) + std::system_error (0x0x7ff712c276e8) 0 + primary-for std::ios_base::failure (0x0x7ff712c27680) + std::runtime_error (0x0x7ff712c27750) 0 + primary-for std::system_error (0x0x7ff712c276e8) + std::exception (0x0x7ff712bcb960) 0 nearly-empty + primary-for std::runtime_error (0x0x7ff712c27750) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7ff712bcb9c0) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7ff712bcba20) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7ff712bcba80) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7ff712bcb900) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7ff712cff240) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7ff712cff900) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7ff71291b000 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7ff71291b0d0 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7ff71291b478 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7ff71291b548 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7ff7129391e0) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7ff712939240) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7ff712637660) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7ff712637840) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7ff712637a80) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7ff712637b40) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7ff712637ae0) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7ff712463ae0) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7ff7121f6660) 0 + +Class QtPrivate::QHashCombine + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombine (0x0x7ff7121f6a80) 0 empty + +Class QtPrivate::QHashCombineCommutative + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombineCommutative (0x0x7ff7121f6ae0) 0 empty + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7ff7121f6b40) 0 + +Class QListData::NotArrayCompatibleLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotArrayCompatibleLayout (0x0x7ff7121f6f00) 0 empty + +Class QListData::NotIndirectLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotIndirectLayout (0x0x7ff7121f6f60) 0 empty + +Class QListData::ArrayCompatibleLayout + size=1 align=1 + base size=1 base align=1 +QListData::ArrayCompatibleLayout (0x0x7ff7121fc618) 0 empty + QListData::NotIndirectLayout (0x0x7ff711ff3000) 0 empty + +Class QListData::InlineWithPaddingLayout + size=1 align=1 + base size=1 base align=1 +QListData::InlineWithPaddingLayout (0x0x7ff711fec850) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7ff711ff3060) 0 empty + QListData::NotIndirectLayout (0x0x7ff711ff30c0) 0 empty + +Class QListData::IndirectLayout + size=1 align=1 + base size=1 base align=1 +QListData::IndirectLayout (0x0x7ff7121fc680) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7ff711ff3120) 0 empty + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7ff711ff3180) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7ff7121f6ea0) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7ff711ff3d20) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7ff712181e40) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7ff712181de0) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7ff712186f08) 0 + QList (0x0x7ff712186f70) 0 + QListSpecialMethods (0x0x7ff7121bc060) 0 empty + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7ff7121bc360) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7ff7121bc780) 0 + +Class std::allocator_arg_t + size=1 align=1 + base size=0 base align=1 +std::allocator_arg_t (0x0x7ff7121bcde0) 0 empty + +Class std::__uses_alloc_base + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc_base (0x0x7ff7121bcf60) 0 empty + +Class std::__uses_alloc0::_Sink + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc0::_Sink (0x0x7ff711c36060) 0 empty + +Class std::__uses_alloc0 + size=1 align=1 + base size=1 base align=1 +std::__uses_alloc0 (0x0x7ff711dfdf70) 0 + std::__uses_alloc_base (0x0x7ff711c36000) 0 empty + +Class std::_Swallow_assign + size=1 align=1 + base size=0 base align=1 +std::_Swallow_assign (0x0x7ff711d2c0c0) 0 empty + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7ff711d2c300) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7ff711d2c3c0) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7ff711d2c4e0) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7ff711d2c660) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7ff711d2ca20) 0 + +Class QtMetaTypePrivate::VectorBoolElements + size=1 align=1 + base size=0 base align=1 +QtMetaTypePrivate::VectorBoolElements (0x0x7ff711d2cb40) 0 empty + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7ff711ab54e0) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7ff711ab58a0) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7ff711ab5ae0) 0 + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7ff7118cf840) 0 + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7ff7118cf9c0) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7ff7118cfba0) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7ff7118cfb40) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7ff7118cff00) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7ff7118cff60) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7ff7115d6060) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7ff711b9c8f0) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7ff7115d6000) 0 + primary-for QAbstractAnimation (0x0x7ff711b9c8f0) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7ff7115d6120) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7ff711b9c958) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7ff7115d60c0) 0 + primary-for QAnimationDriver (0x0x7ff711b9c958) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7ff7115d61e0) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7ff711b9c9c0) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7ff711b9ca28) 0 + primary-for QAnimationGroup (0x0x7ff711b9c9c0) + QObject (0x0x7ff7115d6180) 0 + primary-for QAbstractAnimation (0x0x7ff711b9ca28) + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7ff7115d62a0) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7ff711b9ca90) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7ff711b9caf8) 0 + primary-for QParallelAnimationGroup (0x0x7ff711b9ca90) + QAbstractAnimation (0x0x7ff711b9cb60) 0 + primary-for QAnimationGroup (0x0x7ff711b9caf8) + QObject (0x0x7ff7115d6240) 0 + primary-for QAbstractAnimation (0x0x7ff711b9cb60) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7ff7115d6360) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7ff711b9cbc8) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7ff711b9cc30) 0 + primary-for QPauseAnimation (0x0x7ff711b9cbc8) + QObject (0x0x7ff7115d6300) 0 + primary-for QAbstractAnimation (0x0x7ff711b9cc30) + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7ff7116b36c0) 0 + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7ff711782780) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7ff711782840) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7ff711782c00) 0 + +Class QHashData + size=48 align=8 + base size=44 base align=8 +QHashData (0x0x7ff711782ba0) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7ff711782c60) 0 empty + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7ff7114ba6c0) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7ff7114ba780) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7ff7114ba720) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7ff7114ba7e0) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7ff7114ba660) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7ff71123cae0) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7ff71123ccc0) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7ff71123cc60) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7ff71123cd80) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7ff71123cd20) 0 + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7ff710fcc720) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7ff710fd7208) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7ff710fd7270) 0 + primary-for QVariantAnimation (0x0x7ff710fd7208) + QObject (0x0x7ff710fcc6c0) 0 + primary-for QAbstractAnimation (0x0x7ff710fd7270) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7ff710fcc7e0) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7ff710fd7340) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7ff710fd73a8) 0 + primary-for QPropertyAnimation (0x0x7ff710fd7340) + QAbstractAnimation (0x0x7ff710fd7410) 0 + primary-for QVariantAnimation (0x0x7ff710fd73a8) + QObject (0x0x7ff710fcc780) 0 + primary-for QAbstractAnimation (0x0x7ff710fd7410) + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7ff710fcc8a0) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7ff710fd7478) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7ff710fd74e0) 0 + primary-for QSequentialAnimationGroup (0x0x7ff710fd7478) + QAbstractAnimation (0x0x7ff710fd7548) 0 + primary-for QAnimationGroup (0x0x7ff710fd74e0) + QObject (0x0x7ff710fcc840) 0 + primary-for QAbstractAnimation (0x0x7ff710fd7548) + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7ff710fcc960) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 0u +64 0u + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7ff710fcc900) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7ff710fccae0) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7ff710fccb40) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7ff710fccba0) 0 + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7ff710fccd80) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7ff710fccf00) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7ff7111170c0) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7ff7111172a0) 0 empty + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7ff711117360) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7ff710fd77b8) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7ff711117300) 0 + primary-for QIODevice (0x0x7ff710fd77b8) + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7ff711117540) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7ff710fd78f0) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7ff710fd7958) 0 + primary-for QBuffer (0x0x7ff710fd78f0) + QObject (0x0x7ff7111174e0) 0 + primary-for QIODevice (0x0x7ff710fd7958) + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7ff7111175a0) 0 + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7ff711117660) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7ff711117960) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7ff711117ba0) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7ff711117de0) 0 + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7ff710b800c0) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7ff710b80240) 0 + +Class QDebug::Stream + size=80 align=8 + base size=76 base align=8 +QDebug::Stream (0x0x7ff710b806c0) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7ff710b80660) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7ff710d08780) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7ff710d08840) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7ff710d08900) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7ff710d09888) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7ff710d098f0) 0 + primary-for QFileDevice (0x0x7ff710d09888) + QObject (0x0x7ff710d088a0) 0 + primary-for QIODevice (0x0x7ff710d098f0) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7ff710d08ae0) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7ff710d09a28) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7ff710d09a90) 0 + primary-for QFile (0x0x7ff710d09a28) + QIODevice (0x0x7ff710d09af8) 0 + primary-for QFileDevice (0x0x7ff710d09a90) + QObject (0x0x7ff710d08a80) 0 + primary-for QIODevice (0x0x7ff710d09af8) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7ff710d08c60) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7ff710d08f60) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7ff710a503c0) 0 + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7ff710a50600) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7ff710af2000) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7ff710a505a0) 0 + primary-for QFileSelector (0x0x7ff710af2000) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7ff710a506c0) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7ff710af2068) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7ff710a50660) 0 + primary-for QFileSystemWatcher (0x0x7ff710af2068) + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7ff710a50720) 0 + +Class QLoggingCategory::AtomicBools + size=4 align=1 + base size=4 base align=1 +QLoggingCategory::AtomicBools (0x0x7ff710a508a0) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7ff710a50840) 0 + +Vtable for std::type_info +std::type_info::_ZTVSt9type_info: 8u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9type_info) +16 (int (*)(...))std::type_info::~type_info +24 (int (*)(...))std::type_info::~type_info +32 (int (*)(...))std::type_info::__is_pointer_p +40 (int (*)(...))std::type_info::__is_function_p +48 (int (*)(...))std::type_info::__do_catch +56 (int (*)(...))std::type_info::__do_upcast + +Class std::type_info + size=16 align=8 + base size=16 base align=8 +std::type_info (0x0x7ff710a50a20) 0 + vptr=((& std::type_info::_ZTVSt9type_info) + 16u) + +Vtable for std::bad_cast +std::bad_cast::_ZTVSt8bad_cast: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8bad_cast) +16 (int (*)(...))std::bad_cast::~bad_cast +24 (int (*)(...))std::bad_cast::~bad_cast +32 (int (*)(...))std::bad_cast::what + +Class std::bad_cast + size=8 align=8 + base size=8 base align=8 +std::bad_cast (0x0x7ff710af2138) 0 nearly-empty + vptr=((& std::bad_cast::_ZTVSt8bad_cast) + 16u) + std::exception (0x0x7ff710a50a80) 0 nearly-empty + primary-for std::bad_cast (0x0x7ff710af2138) + +Vtable for std::bad_typeid +std::bad_typeid::_ZTVSt10bad_typeid: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt10bad_typeid) +16 (int (*)(...))std::bad_typeid::~bad_typeid +24 (int (*)(...))std::bad_typeid::~bad_typeid +32 (int (*)(...))std::bad_typeid::what + +Class std::bad_typeid + size=8 align=8 + base size=8 base align=8 +std::bad_typeid (0x0x7ff710af21a0) 0 nearly-empty + vptr=((& std::bad_typeid::_ZTVSt10bad_typeid) + 16u) + std::exception (0x0x7ff710a50ae0) 0 nearly-empty + primary-for std::bad_typeid (0x0x7ff710af21a0) + +Vtable for std::bad_function_call +std::bad_function_call::_ZTVSt17bad_function_call: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt17bad_function_call) +16 (int (*)(...))std::bad_function_call::~bad_function_call +24 (int (*)(...))std::bad_function_call::~bad_function_call +32 (int (*)(...))std::bad_function_call::what + +Class std::bad_function_call + size=8 align=8 + base size=8 base align=8 +std::bad_function_call (0x0x7ff7108d5410) 0 nearly-empty + vptr=((& std::bad_function_call::_ZTVSt17bad_function_call) + 16u) + std::exception (0x0x7ff710873ba0) 0 nearly-empty + primary-for std::bad_function_call (0x0x7ff7108d5410) + +Class std::_Nocopy_types + size=16 align=8 + base size=16 base align=8 +std::_Nocopy_types (0x0x7ff710873c60) 0 + +Class std::_Any_data + size=16 align=8 + base size=16 base align=8 +std::_Any_data (0x0x7ff710873cc0) 0 + +Class std::_Function_base + size=24 align=8 + base size=24 base align=8 +std::_Function_base (0x0x7ff710873de0) 0 + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7ff71093d300) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7ff71093d540) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7ff7108d5c30) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7ff7108d5c98) 0 + primary-for QProcess (0x0x7ff7108d5c30) + QObject (0x0x7ff71093d4e0) 0 + primary-for QIODevice (0x0x7ff7108d5c98) + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7ff71093d5a0) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7ff71093d720) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7ff7108d5d00) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7ff7108d5d68) 0 + primary-for QSaveFile (0x0x7ff7108d5d00) + QIODevice (0x0x7ff7108d5dd0) 0 + primary-for QFileDevice (0x0x7ff7108d5d68) + QObject (0x0x7ff71093d6c0) 0 + primary-for QIODevice (0x0x7ff7108d5dd0) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7ff71093d7e0) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7ff7108d5e38) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7ff71093d780) 0 + primary-for QSettings (0x0x7ff7108d5e38) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7ff71093d840) 0 empty + +Class QStorageInfo + size=8 align=8 + base size=8 base align=8 +QStorageInfo (0x0x7ff71093d9c0) 0 + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7ff71093dcc0) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7ff71093dde0) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7ff71066b000) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7ff71066b068) 0 + primary-for QTemporaryFile (0x0x7ff71066b000) + QFileDevice (0x0x7ff71066b0d0) 0 + primary-for QFile (0x0x7ff71066b068) + QIODevice (0x0x7ff71066b138) 0 + primary-for QFileDevice (0x0x7ff71066b0d0) + QObject (0x0x7ff71093dd80) 0 + primary-for QIODevice (0x0x7ff71066b138) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7ff71093df00) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7ff71037f3c0) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7ff71037f5a0) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7ff71037f720) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7ff710498600) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7ff710484f70) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7ff7104985a0) 0 + primary-for QAbstractItemModel (0x0x7ff710484f70) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7ff710498960) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractTableModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7ff7104f71a0) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7ff7104f7208) 0 + primary-for QAbstractTableModel (0x0x7ff7104f71a0) + QObject (0x0x7ff710498900) 0 + primary-for QAbstractItemModel (0x0x7ff7104f7208) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7ff710498a20) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractListModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7ff7104f7270) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7ff7104f72d8) 0 + primary-for QAbstractListModel (0x0x7ff7104f7270) + QObject (0x0x7ff7104989c0) 0 + primary-for QAbstractItemModel (0x0x7ff7104f72d8) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7ff710498d20) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QAbstractProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7ff7104f7410) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7ff7104f7478) 0 + primary-for QAbstractProxyModel (0x0x7ff7104f7410) + QObject (0x0x7ff710498cc0) 0 + primary-for QAbstractItemModel (0x0x7ff7104f7478) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7ff710498de0) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7ff7104f74e0) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7ff7104f7548) 0 + primary-for QIdentityProxyModel (0x0x7ff7104f74e0) + QAbstractItemModel (0x0x7ff7104f75b0) 0 + primary-for QAbstractProxyModel (0x0x7ff7104f7548) + QObject (0x0x7ff710498d80) 0 + primary-for QAbstractItemModel (0x0x7ff7104f75b0) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7ff710498e40) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7ff71021d0c0) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7ff7104f7820) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7ff71021d060) 0 + primary-for QItemSelectionModel (0x0x7ff7104f7820) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7ff7104f7a28) 0 + QList (0x0x7ff7104f7a90) 0 + QListSpecialMethods (0x0x7ff71021d360) 0 empty + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7ff71021d780) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7ff7104f7b60) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7ff7104f7bc8) 0 + primary-for QSortFilterProxyModel (0x0x7ff7104f7b60) + QAbstractItemModel (0x0x7ff7104f7c30) 0 + primary-for QAbstractProxyModel (0x0x7ff7104f7bc8) + QObject (0x0x7ff71021d720) 0 + primary-for QAbstractItemModel (0x0x7ff7104f7c30) + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7ff71021d840) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7ff7104f7c98) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7ff7104f7d00) 0 + primary-for QStringListModel (0x0x7ff7104f7c98) + QAbstractItemModel (0x0x7ff7104f7d68) 0 + primary-for QAbstractListModel (0x0x7ff7104f7d00) + QObject (0x0x7ff71021d7e0) 0 + primary-for QAbstractItemModel (0x0x7ff7104f7d68) + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7ff71021d8a0) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7ff71021d960) 0 + +Class QJsonValuePtr + size=24 align=8 + base size=24 base align=8 +QJsonValuePtr (0x0x7ff71021da20) 0 + +Class QJsonValueRefPtr + size=16 align=8 + base size=16 base align=8 +QJsonValueRefPtr (0x0x7ff71021da80) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7ff71021db40) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7ff71021dba0) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7ff71021dae0) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7ff71021dc60) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7ff71021dcc0) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7ff71021dd80) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7ff71021dde0) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7ff71021dd20) 0 + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7ff71002d000) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7ff7104f7f70) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7ff71021df60) 0 + primary-for QEventLoop (0x0x7ff7104f7f70) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7ff71002d180) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7ff71002d240) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7ff71002d2a0) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7ff7100380d0) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7ff71002d1e0) 0 + primary-for QAbstractEventDispatcher (0x0x7ff7100380d0) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7ff71002d300) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7ff71002d360) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7ff71002d4e0) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7ff7100381a0) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7ff71002d540) 0 + primary-for QTimerEvent (0x0x7ff7100381a0) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7ff710038208) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7ff71002d5a0) 0 + primary-for QChildEvent (0x0x7ff710038208) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7ff710038270) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7ff71002d600) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7ff710038270) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7ff7100382d8) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7ff71002d660) 0 + primary-for QDeferredDeleteEvent (0x0x7ff7100382d8) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7ff71002d720) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7ff710038340) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7ff71002d6c0) 0 + primary-for QCoreApplication (0x0x7ff710038340) + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7ff71002d7e0) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7ff71002d960) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7ff71002db40) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7ff71002dba0) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7ff71002dd80) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7ff7100384e0) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7ff71002dd20) 0 + primary-for QMimeData (0x0x7ff7100384e0) + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7ff71002de40) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7ff710038548) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7ff71002dde0) 0 + primary-for QObjectCleanupHandler (0x0x7ff710038548) + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7ff70fd690c0) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7ff7100385b0) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7ff70fd69060) 0 + primary-for QSharedMemory (0x0x7ff7100385b0) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7ff70fd69180) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7ff710038618) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7ff70fd69120) 0 + primary-for QSignalMapper (0x0x7ff710038618) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7ff70fd69240) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7ff710038680) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7ff70fd691e0) 0 + primary-for QSocketNotifier (0x0x7ff710038680) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7ff70fd692a0) 0 + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7ff70fd693c0) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7ff7100386e8) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7ff70fd69360) 0 + primary-for QTimer (0x0x7ff7100386e8) + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7ff70fd69540) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7ff7100387b8) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7ff70fd694e0) 0 + primary-for QTranslator (0x0x7ff7100387b8) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7ff70fd695a0) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7ff70fd69780) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7ff70fd697e0) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7ff70fd69900) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7ff710038888) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7ff70fd698a0) 0 + primary-for QLibrary (0x0x7ff710038888) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7ff70fd69a80) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7ff70fd69c60) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7ff710038a28) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7ff70fd69c00) 0 + primary-for QPluginLoader (0x0x7ff710038a28) + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7ff70fd69cc0) 0 + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7ff70fd69ea0) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7ff710038af8) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7ff70fd69e40) 0 + primary-for QAbstractState (0x0x7ff710038af8) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7ff70fd69f60) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7ff710038b60) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7ff70fd69f00) 0 + primary-for QAbstractTransition (0x0x7ff710038b60) + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7ff70febb060) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7ff710038bc8) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7ff710038c30) 0 + primary-for QEventTransition (0x0x7ff710038bc8) + QObject (0x0x7ff70febb000) 0 + primary-for QAbstractTransition (0x0x7ff710038c30) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7ff70febb120) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7ff710038c98) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7ff710038d00) 0 + primary-for QFinalState (0x0x7ff710038c98) + QObject (0x0x7ff70febb0c0) 0 + primary-for QAbstractState (0x0x7ff710038d00) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7ff70febb1e0) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7ff710038d68) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7ff710038dd0) 0 + primary-for QHistoryState (0x0x7ff710038d68) + QObject (0x0x7ff70febb180) 0 + primary-for QAbstractState (0x0x7ff710038dd0) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7ff70febb2a0) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7ff710038e38) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7ff710038ea0) 0 + primary-for QSignalTransition (0x0x7ff710038e38) + QObject (0x0x7ff70febb240) 0 + primary-for QAbstractTransition (0x0x7ff710038ea0) + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7ff70febb360) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7ff710038f08) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7ff710038f70) 0 + primary-for QState (0x0x7ff710038f08) + QObject (0x0x7ff70febb300) 0 + primary-for QAbstractState (0x0x7ff710038f70) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7ff70febb480) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7ff70ff22138) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7ff70febb4e0) 0 + primary-for QStateMachine::SignalEvent (0x0x7ff70ff22138) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7ff70ff221a0) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7ff70febb540) 0 + primary-for QStateMachine::WrappedEvent (0x0x7ff70ff221a0) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7ff70ff22000) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7ff70ff22068) 0 + primary-for QStateMachine (0x0x7ff70ff22000) + QAbstractState (0x0x7ff70ff220d0) 0 + primary-for QState (0x0x7ff70ff22068) + QObject (0x0x7ff70febb420) 0 + primary-for QAbstractState (0x0x7ff70ff220d0) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7ff70ff22208) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7ff70febb5a0) 0 nearly-empty + primary-for QException (0x0x7ff70ff22208) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7ff70ff22270) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7ff70ff222d8) 0 nearly-empty + primary-for QUnhandledException (0x0x7ff70ff22270) + std::exception (0x0x7ff70febb600) 0 nearly-empty + primary-for QException (0x0x7ff70ff222d8) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7ff70febb660) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7ff70febb720) 0 + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7ff70febb780) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7ff70febb7e0) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7ff70ff22410) 0 + QBasicMutex (0x0x7ff70febba20) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7ff70febba80) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7ff70febbb40) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7ff70febbba0) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7ff70febbd20) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7ff70febbde0) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7ff70fc9f180) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 0u +48 0u +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7ff70ff22c98) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7ff70fc9f120) 0 + primary-for QFutureWatcherBase (0x0x7ff70ff22c98) + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7ff70fc9f2a0) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7ff70fc9f540) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7ff70fc9f5a0) 0 + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7ff70fc9f600) 0 + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7ff70fc9f6c0) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7ff70fd07270) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7ff70fc9f660) 0 + primary-for QThread (0x0x7ff70fd07270) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7ff70fc9f780) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7ff70fd072d8) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7ff70fc9f720) 0 + primary-for QThreadPool (0x0x7ff70fd072d8) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7ff70fc9f7e0) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7ff70fc9f8a0) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7ff70fc9fd80) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7ff70fa64000) 0 + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7ff70fa64240) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7ff70fa641e0) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7ff70fa643c0) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7ff70fa64480) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7ff70fb2e240) 0 + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7ff70fb2e420) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7ff70fb2e480) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7ff70fb2e4e0) 0 + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7ff70fb2e540) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7ff70fb2e6c0) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7ff70fb2e840) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7ff70fb2e9c0) 0 + +Class QLinkedListData + size=32 align=8 + base size=25 base align=8 +QLinkedListData (0x0x7ff70fb2eb40) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7ff70f8a3300) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7ff70f8a3480) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7ff70f8a3600) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7ff70f8a36c0) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7ff70f8a3900) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7ff70f8a3b40) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7ff70f8a3cc0) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7ff70f8a3e40) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7ff70f72c2a0) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7ff70f72c480) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7ff70f72c840) 0 empty + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7ff70f4102a0) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7ff70f410480) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7ff70f4410d0) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7ff70f410420) 0 + primary-for QTimeLine (0x0x7ff70f4410d0) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7ff70f410540) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7ff70f4104e0) 0 + +Class QVersionNumber::SegmentStorage + size=8 align=8 + base size=8 base align=8 +QVersionNumber::SegmentStorage (0x0x7ff70f4108a0) 0 + +Class QVersionNumber + size=8 align=8 + base size=8 base align=8 +QVersionNumber (0x0x7ff70f410840) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7ff70f53e660) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7ff70f20d360) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7ff70f20b8f0) 0 + QVector (0x0x7ff70f20d600) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7ff70f20d660) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7ff70f20d7e0) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7ff70f20d960) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7ff70f20dae0) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7ff70f20db40) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7ff70f20dc60) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7ff70f20dd80) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7ff70f32b0c0) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7ff70f32b3c0) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7ff70f32b6c0) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7ff70f32b780) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7ff70f32b840) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7ff70f20bf70) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7ff70f32b7e0) 0 + primary-for QGeoPositionInfoSource (0x0x7ff70f20bf70) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7ff70f32ba20) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7ff70f20bb60) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7ff70f32b9c0) 0 + primary-for QGeoAreaMonitorSource (0x0x7ff70f20bb60) + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7ff70f20bc98) 0 + QGeoShape (0x0x7ff70f32ba80) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7ff70f32bd20) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7ff70f004060) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7ff70f004120) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7ff70effc068) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7ff70f0040c0) 0 + primary-for QGeoSatelliteInfoSource (0x0x7ff70effc068) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7ff70f0041e0) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7ff70effc0d0) 0 + QGeoShape (0x0x7ff70f0042a0) 0 + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7ff70f0046c0) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7ff70effc270) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7ff70effc2d8) 0 + primary-for QNmeaPositionInfoSource (0x0x7ff70effc270) + QObject (0x0x7ff70f004660) 0 + primary-for QGeoPositionInfoSource (0x0x7ff70effc2d8) + diff --git a/tests/auto/bic/data/QtPositioning.5.8.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.8.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..9c9d1d8 --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.8.0.linux-gcc-amd64.txt @@ -0,0 +1,4425 @@ +Class std::__failure_type + size=1 align=1 + base size=0 base align=1 +std::__failure_type (0x0x7fe59698d300) 0 empty + +Class std::__do_is_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_destructible_impl (0x0x7fe5969d5a80) 0 empty + +Class std::__do_is_nt_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nt_destructible_impl (0x0x7fe5969d5cc0) 0 empty + +Class std::__do_is_default_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_default_constructible_impl (0x0x7fe5969d5f00) 0 empty + +Class std::__do_is_static_castable_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_static_castable_impl (0x0x7fe596a03180) 0 empty + +Class std::__do_is_direct_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_direct_constructible_impl (0x0x7fe596a03300) 0 empty + +Class std::__do_is_nary_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nary_constructible_impl (0x0x7fe596a036c0) 0 empty + +Class std::__do_common_type_impl + size=1 align=1 + base size=0 base align=1 +std::__do_common_type_impl (0x0x7fe59468fe40) 0 empty + +Class std::__do_member_type_wrapper + size=1 align=1 + base size=0 base align=1 +std::__do_member_type_wrapper (0x0x7fe59468ff00) 0 empty + +Class std::__result_of_memfun_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_ref_impl (0x0x7fe5946be2a0) 0 empty + +Class std::__result_of_memfun_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_deref_impl (0x0x7fe5946be360) 0 empty + +Class std::__result_of_memobj_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_ref_impl (0x0x7fe5946be420) 0 empty + +Class std::__result_of_memobj_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_deref_impl (0x0x7fe5946be4e0) 0 empty + +Class std::__result_of_other_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_other_impl (0x0x7fe5946be780) 0 empty + +Class std::piecewise_construct_t + size=1 align=1 + base size=0 base align=1 +std::piecewise_construct_t (0x0x7fe5946be960) 0 empty + +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7fe5946bede0) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7fe5946bee40) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7fe594775ae0) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7fe594775b40) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7fe594666a90) 0 empty + std::input_iterator_tag (0x0x7fe594775ba0) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7fe594666af8) 0 empty + std::forward_iterator_tag (0x0x7fe594666b60) 0 empty + std::input_iterator_tag (0x0x7fe594775c00) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7fe594666bc8) 0 empty + std::bidirectional_iterator_tag (0x0x7fe594666c30) 0 empty + std::forward_iterator_tag (0x0x7fe594666c98) 0 empty + std::input_iterator_tag (0x0x7fe594775c60) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_iter (0x0x7fe5947b1900) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_val (0x0x7fe5947b1960) 0 empty + +Class __gnu_cxx::__ops::_Val_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Val_less_iter (0x0x7fe5947b19c0) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7fe5947b1a20) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7fe5947b1a80) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7fe59448c5a0) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7fe59448c7e0) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7fe59448c8a0) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7fe59448c900) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7fe59448c9c0) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7fe59448ca20) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7fe59448cea0) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7fe59448cf00) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7fe59448cf60) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7fe5947e9208) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7fe5945ce000) 0 nearly-empty + primary-for std::bad_exception (0x0x7fe5947e9208) + +Class std::__exception_ptr::exception_ptr + size=8 align=8 + base size=8 base align=8 +std::__exception_ptr::exception_ptr (0x0x7fe5945ce060) 0 + +Vtable for std::nested_exception +std::nested_exception::_ZTVSt16nested_exception: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16nested_exception) +16 (int (*)(...))std::nested_exception::~nested_exception +24 (int (*)(...))std::nested_exception::~nested_exception + +Class std::nested_exception + size=16 align=8 + base size=16 base align=8 +std::nested_exception (0x0x7fe5945ce0c0) 0 + vptr=((& std::nested_exception::_ZTVSt16nested_exception) + 16u) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7fe5947e9410) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7fe5945ce4e0) 0 nearly-empty + primary-for std::bad_alloc (0x0x7fe5947e9410) + +Vtable for std::bad_array_new_length +std::bad_array_new_length::_ZTVSt20bad_array_new_length: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt20bad_array_new_length) +16 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +24 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +32 (int (*)(...))std::bad_array_new_length::what + +Class std::bad_array_new_length + size=8 align=8 + base size=8 base align=8 +std::bad_array_new_length (0x0x7fe5947e9478) 0 nearly-empty + vptr=((& std::bad_array_new_length::_ZTVSt20bad_array_new_length) + 16u) + std::bad_alloc (0x0x7fe5947e94e0) 0 nearly-empty + primary-for std::bad_array_new_length (0x0x7fe5947e9478) + std::exception (0x0x7fe5945ce540) 0 nearly-empty + primary-for std::bad_alloc (0x0x7fe5947e94e0) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7fe5945ce5a0) 0 empty + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7fe5942a31e0) 0 + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7fe5942a3ea0) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7fe5942a3f00) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7fe5940e4de0) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7fe5940e4e40) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7fe5940e4f00) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7fe5940e4f60) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7fe59416a000) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7fe59416a060) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7fe59416a180) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7fe59416a1e0) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7fe59416a600) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7fe59416a660) 0 + +Class std::_Hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Hash_impl (0x0x7fe593fa1e40) 0 empty + +Class std::_Fnv_hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Fnv_hash_impl (0x0x7fe593fa1ea0) 0 empty + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7fe593c77e40) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7fe593e16c60) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7fe593ade0d0) 0 + std::iterator (0x0x7fe593e16d20) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7fe593ade138) 0 + std::_Bit_iterator_base (0x0x7fe593ade1a0) 0 + std::iterator (0x0x7fe593e16d80) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7fe593ade208) 0 + std::_Bit_iterator_base (0x0x7fe593ade270) 0 + std::iterator (0x0x7fe593e16de0) 0 empty + +Class std::random_device + size=5000 align=8 + base size=5000 base align=8 +std::random_device (0x0x7fe593c02c00) 0 + +Class std::bernoulli_distribution::param_type + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution::param_type (0x0x7fe5939359c0) 0 + +Class std::bernoulli_distribution + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution (0x0x7fe593935960) 0 + +Class std::seed_seq + size=24 align=8 + base size=24 base align=8 +std::seed_seq (0x0x7fe5936be960) 0 + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7fe59228c420) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7fe59228c480) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7fe59234a6c0) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7fe59234a720) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7fe59234a780) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7fe5923a1240) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7fe5923a1540) 0 + +Class std::__atomic_flag_base + size=1 align=1 + base size=1 base align=1 +std::__atomic_flag_base (0x0x7fe5923a1ae0) 0 + +Class std::atomic_flag + size=1 align=1 + base size=1 base align=1 +std::atomic_flag (0x0x7fe5923a0680) 0 + std::__atomic_flag_base (0x0x7fe5923a1b40) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7fe5923a0dd0) 0 + QAtomicInteger (0x0x7fe5923a0e38) 0 + QBasicAtomicInteger (0x0x7fe591f412a0) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7fe591d5eb40) 0 empty + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7fe591b94ba0) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7fe591b5f270) 0 + QGenericArgument (0x0x7fe591b94c00) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7fe591b94d80) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7fe591b94e40) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7fe59186eea0) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7fe59186ef00) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7fe5919163c0) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7fe591916480) 0 + +Class QtPrivate::QContainerImplHelper + size=1 align=1 + base size=0 base align=1 +QtPrivate::QContainerImplHelper (0x0x7fe5919168a0) 0 empty + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7fe591916900) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7fe591916960) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7fe5919169c0) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7fe591916a20) 0 + +Class std::__cow_string + size=8 align=8 + base size=8 base align=8 +std::__cow_string (0x0x7fe591916de0) 0 + +Vtable for std::logic_error +std::logic_error::_ZTVSt11logic_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11logic_error) +16 (int (*)(...))std::logic_error::~logic_error +24 (int (*)(...))std::logic_error::~logic_error +32 (int (*)(...))std::logic_error::what + +Class std::logic_error + size=16 align=8 + base size=16 base align=8 +std::logic_error (0x0x7fe591992820) 0 + vptr=((& std::logic_error::_ZTVSt11logic_error) + 16u) + std::exception (0x0x7fe591916ea0) 0 nearly-empty + primary-for std::logic_error (0x0x7fe591992820) + +Vtable for std::domain_error +std::domain_error::_ZTVSt12domain_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12domain_error) +16 (int (*)(...))std::domain_error::~domain_error +24 (int (*)(...))std::domain_error::~domain_error +32 (int (*)(...))std::logic_error::what + +Class std::domain_error + size=16 align=8 + base size=16 base align=8 +std::domain_error (0x0x7fe591992888) 0 + vptr=((& std::domain_error::_ZTVSt12domain_error) + 16u) + std::logic_error (0x0x7fe5919928f0) 0 + primary-for std::domain_error (0x0x7fe591992888) + std::exception (0x0x7fe591916f00) 0 nearly-empty + primary-for std::logic_error (0x0x7fe5919928f0) + +Vtable for std::invalid_argument +std::invalid_argument::_ZTVSt16invalid_argument: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16invalid_argument) +16 (int (*)(...))std::invalid_argument::~invalid_argument +24 (int (*)(...))std::invalid_argument::~invalid_argument +32 (int (*)(...))std::logic_error::what + +Class std::invalid_argument + size=16 align=8 + base size=16 base align=8 +std::invalid_argument (0x0x7fe591992958) 0 + vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16u) + std::logic_error (0x0x7fe5919929c0) 0 + primary-for std::invalid_argument (0x0x7fe591992958) + std::exception (0x0x7fe591916f60) 0 nearly-empty + primary-for std::logic_error (0x0x7fe5919929c0) + +Vtable for std::length_error +std::length_error::_ZTVSt12length_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12length_error) +16 (int (*)(...))std::length_error::~length_error +24 (int (*)(...))std::length_error::~length_error +32 (int (*)(...))std::logic_error::what + +Class std::length_error + size=16 align=8 + base size=16 base align=8 +std::length_error (0x0x7fe591992a28) 0 + vptr=((& std::length_error::_ZTVSt12length_error) + 16u) + std::logic_error (0x0x7fe591992a90) 0 + primary-for std::length_error (0x0x7fe591992a28) + std::exception (0x0x7fe591a38000) 0 nearly-empty + primary-for std::logic_error (0x0x7fe591992a90) + +Vtable for std::out_of_range +std::out_of_range::_ZTVSt12out_of_range: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12out_of_range) +16 (int (*)(...))std::out_of_range::~out_of_range +24 (int (*)(...))std::out_of_range::~out_of_range +32 (int (*)(...))std::logic_error::what + +Class std::out_of_range + size=16 align=8 + base size=16 base align=8 +std::out_of_range (0x0x7fe591992af8) 0 + vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16u) + std::logic_error (0x0x7fe591992b60) 0 + primary-for std::out_of_range (0x0x7fe591992af8) + std::exception (0x0x7fe591a38060) 0 nearly-empty + primary-for std::logic_error (0x0x7fe591992b60) + +Vtable for std::runtime_error +std::runtime_error::_ZTVSt13runtime_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13runtime_error) +16 (int (*)(...))std::runtime_error::~runtime_error +24 (int (*)(...))std::runtime_error::~runtime_error +32 (int (*)(...))std::runtime_error::what + +Class std::runtime_error + size=16 align=8 + base size=16 base align=8 +std::runtime_error (0x0x7fe591992bc8) 0 + vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16u) + std::exception (0x0x7fe591a380c0) 0 nearly-empty + primary-for std::runtime_error (0x0x7fe591992bc8) + +Vtable for std::range_error +std::range_error::_ZTVSt11range_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11range_error) +16 (int (*)(...))std::range_error::~range_error +24 (int (*)(...))std::range_error::~range_error +32 (int (*)(...))std::runtime_error::what + +Class std::range_error + size=16 align=8 + base size=16 base align=8 +std::range_error (0x0x7fe591992c30) 0 + vptr=((& std::range_error::_ZTVSt11range_error) + 16u) + std::runtime_error (0x0x7fe591992c98) 0 + primary-for std::range_error (0x0x7fe591992c30) + std::exception (0x0x7fe591a38120) 0 nearly-empty + primary-for std::runtime_error (0x0x7fe591992c98) + +Vtable for std::overflow_error +std::overflow_error::_ZTVSt14overflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt14overflow_error) +16 (int (*)(...))std::overflow_error::~overflow_error +24 (int (*)(...))std::overflow_error::~overflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::overflow_error + size=16 align=8 + base size=16 base align=8 +std::overflow_error (0x0x7fe591992d00) 0 + vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16u) + std::runtime_error (0x0x7fe591992d68) 0 + primary-for std::overflow_error (0x0x7fe591992d00) + std::exception (0x0x7fe591a38180) 0 nearly-empty + primary-for std::runtime_error (0x0x7fe591992d68) + +Vtable for std::underflow_error +std::underflow_error::_ZTVSt15underflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt15underflow_error) +16 (int (*)(...))std::underflow_error::~underflow_error +24 (int (*)(...))std::underflow_error::~underflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::underflow_error + size=16 align=8 + base size=16 base align=8 +std::underflow_error (0x0x7fe591992dd0) 0 + vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16u) + std::runtime_error (0x0x7fe591992e38) 0 + primary-for std::underflow_error (0x0x7fe591992dd0) + std::exception (0x0x7fe591a381e0) 0 nearly-empty + primary-for std::runtime_error (0x0x7fe591992e38) + +Vtable for std::_V2::error_category +std::_V2::error_category::_ZTVNSt3_V214error_categoryE: 10u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt3_V214error_categoryE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))std::_V2::error_category::_M_message +48 (int (*)(...))__cxa_pure_virtual +56 (int (*)(...))std::_V2::error_category::default_error_condition +64 (int (*)(...))std::_V2::error_category::equivalent +72 (int (*)(...))std::_V2::error_category::equivalent + +Class std::_V2::error_category + size=8 align=8 + base size=8 base align=8 +std::_V2::error_category (0x0x7fe591a38360) 0 nearly-empty + vptr=((& std::_V2::error_category::_ZTVNSt3_V214error_categoryE) + 16u) + +Class std::error_code + size=16 align=8 + base size=16 base align=8 +std::error_code (0x0x7fe591a385a0) 0 + +Class std::error_condition + size=16 align=8 + base size=16 base align=8 +std::error_condition (0x0x7fe591a38720) 0 + +Vtable for std::system_error +std::system_error::_ZTVSt12system_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12system_error) +16 (int (*)(...))std::system_error::~system_error +24 (int (*)(...))std::system_error::~system_error +32 (int (*)(...))std::runtime_error::what + +Class std::system_error + size=32 align=8 + base size=32 base align=8 +std::system_error (0x0x7fe591678270) 0 + vptr=((& std::system_error::_ZTVSt12system_error) + 16u) + std::runtime_error (0x0x7fe5916782d8) 0 + primary-for std::system_error (0x0x7fe591678270) + std::exception (0x0x7fe591a38960) 0 nearly-empty + primary-for std::runtime_error (0x0x7fe5916782d8) + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=32 align=8 + base size=32 base align=8 +std::ios_base::failure (0x0x7fe591678ea0) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16u) + std::system_error (0x0x7fe591678f08) 0 + primary-for std::ios_base::failure (0x0x7fe591678ea0) + std::runtime_error (0x0x7fe591678f70) 0 + primary-for std::system_error (0x0x7fe591678f08) + std::exception (0x0x7fe591a38c60) 0 nearly-empty + primary-for std::runtime_error (0x0x7fe591678f70) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7fe591a38cc0) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7fe591a38d20) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7fe591a38d80) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7fe591a38c00) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7fe591785540) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7fe591785c00) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7fe59133e888 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7fe59133e958 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7fe59133ed00 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7fe59133edd0 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7fe5913294e0) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7fe591329540) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7fe5910b5960) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7fe5910b5cc0) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7fe5911b20c0) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7fe5911b2180) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7fe5911b2120) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7fe590f8a120) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7fe590f8af60) 0 + +Class QtPrivate::QHashCombine + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombine (0x0x7fe590d08660) 0 empty + +Class QtPrivate::QHashCombineCommutative + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombineCommutative (0x0x7fe590d086c0) 0 empty + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7fe590d08720) 0 + +Class QListData::NotArrayCompatibleLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotArrayCompatibleLayout (0x0x7fe590d08ae0) 0 empty + +Class QListData::NotIndirectLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotIndirectLayout (0x0x7fe590d08b40) 0 empty + +Class QListData::ArrayCompatibleLayout + size=1 align=1 + base size=1 base align=1 +QListData::ArrayCompatibleLayout (0x0x7fe590d7e340) 0 empty + QListData::NotIndirectLayout (0x0x7fe590d08ba0) 0 empty + +Class QListData::InlineWithPaddingLayout + size=1 align=1 + base size=1 base align=1 +QListData::InlineWithPaddingLayout (0x0x7fe590af8af0) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7fe590d08c00) 0 empty + QListData::NotIndirectLayout (0x0x7fe590d08c60) 0 empty + +Class QListData::IndirectLayout + size=1 align=1 + base size=1 base align=1 +QListData::IndirectLayout (0x0x7fe590d7e3a8) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7fe590d08cc0) 0 empty + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7fe590d08d20) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7fe590d08a80) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7fe590b6d900) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7fe5908a5ba0) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7fe5908a5b40) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7fe5908a4d68) 0 + QList (0x0x7fe5908a4dd0) 0 + QListSpecialMethods (0x0x7fe5908a5d80) 0 empty + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7fe590920240) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7fe590920660) 0 + +Class std::allocator_arg_t + size=1 align=1 + base size=0 base align=1 +std::allocator_arg_t (0x0x7fe590920cc0) 0 empty + +Class std::__uses_alloc_base + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc_base (0x0x7fe590920e40) 0 empty + +Class std::__uses_alloc0::_Sink + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc0::_Sink (0x0x7fe590920f00) 0 empty + +Class std::__uses_alloc0 + size=1 align=1 + base size=1 base align=1 +std::__uses_alloc0 (0x0x7fe59091fe38) 0 + std::__uses_alloc_base (0x0x7fe590920ea0) 0 empty + +Class std::_Swallow_assign + size=1 align=1 + base size=0 base align=1 +std::_Swallow_assign (0x0x7fe590755f60) 0 empty + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7fe5904541e0) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7fe5904542a0) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7fe5904543c0) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7fe590454540) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7fe590454900) 0 + +Class QtMetaTypePrivate::VectorBoolElements + size=1 align=1 + base size=0 base align=1 +QtMetaTypePrivate::VectorBoolElements (0x0x7fe590454a20) 0 empty + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7fe59060f3c0) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7fe59060f900) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7fe59060fcc0) 0 + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7fe5903cfd80) 0 + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7fe5903cff00) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7fe590042120) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7fe5900420c0) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7fe5900426c0) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7fe590042720) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7fe5900427e0) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7fe5902abdd0) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7fe590042780) 0 + primary-for QAbstractAnimation (0x0x7fe5902abdd0) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7fe5900428a0) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7fe5902abe38) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7fe590042840) 0 + primary-for QAnimationDriver (0x0x7fe5902abe38) + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7fe590042960) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7fe5902abea0) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7fe590042900) 0 + primary-for QEventLoop (0x0x7fe5902abea0) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7fe590042ae0) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7fe590042ba0) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7fe590042c00) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7fe5902ab9c0) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7fe590042b40) 0 + primary-for QAbstractEventDispatcher (0x0x7fe5902ab9c0) + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7fe590042c60) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7fe590042ea0) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7fe5901c62a0) 0 + +Class QHashData + size=48 align=8 + base size=44 base align=8 +QHashData (0x0x7fe5901c6240) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7fe5901c6300) 0 empty + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7fe5901c6ea0) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7fe5901c6f60) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7fe5901c6f00) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7fe58ffa9000) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7fe5901c6e40) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7fe58fc37480) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7fe58fc377e0) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7fe58fc37780) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7fe58fc378a0) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7fe58fc37840) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7fe58fa69300) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7fe58fa69600) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7fe58fb44660) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7fe58fb43888) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7fe58fb44600) 0 + primary-for QAbstractItemModel (0x0x7fe58fb43888) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7fe58fb449c0) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractTableModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7fe58fb43a90) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7fe58fb43af8) 0 + primary-for QAbstractTableModel (0x0x7fe58fb43a90) + QObject (0x0x7fe58fb44960) 0 + primary-for QAbstractItemModel (0x0x7fe58fb43af8) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7fe58fb44a80) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractListModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7fe58fb43b60) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7fe58fb43bc8) 0 + primary-for QAbstractListModel (0x0x7fe58fb43b60) + QObject (0x0x7fe58fb44a20) 0 + primary-for QAbstractItemModel (0x0x7fe58fb43bc8) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7fe58fb44d20) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7fe58fb44de0) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QAbstractProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7fe58fb43d00) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7fe58fb43d68) 0 + primary-for QAbstractProxyModel (0x0x7fe58fb43d00) + QObject (0x0x7fe58fb44d80) 0 + primary-for QAbstractItemModel (0x0x7fe58fb43d68) + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7fe58fb44ea0) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7fe58fb43dd0) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7fe58fb44e40) 0 + primary-for QAbstractState (0x0x7fe58fb43dd0) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7fe58fb44f60) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7fe58fb43e38) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7fe58fb44f00) 0 + primary-for QAbstractTransition (0x0x7fe58fb43e38) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7fe58f89d060) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7fe58fb43ea0) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7fe58fb43f08) 0 + primary-for QAnimationGroup (0x0x7fe58fb43ea0) + QObject (0x0x7fe58f89d000) 0 + primary-for QAbstractAnimation (0x0x7fe58fb43f08) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7fe58f89d540) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7fe58f89d840) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7fe58f89da80) 0 + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7fe58f89de40) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7fe58f8caf08) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7fe58f89dde0) 0 + primary-for QIODevice (0x0x7fe58f8caf08) + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7fe58f9ae060) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7fe58f98a068) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7fe58f98a0d0) 0 + primary-for QBuffer (0x0x7fe58f98a068) + QObject (0x0x7fe58f9ae000) 0 + primary-for QIODevice (0x0x7fe58f98a0d0) + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7fe58f9ae120) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7fe58f9ae0c0) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7fe58f9ae2a0) 0 + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7fe58f9ae480) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7fe58f9ae900) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7fe58f9ae9c0) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7fe58f76ba80) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7fe58f76bf00) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7fe58f7a2208) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7fe58f76bf60) 0 + primary-for QTimerEvent (0x0x7fe58f7a2208) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7fe58f7a2270) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7fe58f7e9000) 0 + primary-for QChildEvent (0x0x7fe58f7a2270) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7fe58f7a23a8) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7fe58f7e91e0) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7fe58f7a23a8) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7fe58f7a2410) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7fe58f7e9240) 0 + primary-for QDeferredDeleteEvent (0x0x7fe58f7a2410) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7fe58f7e9300) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7fe58f7a2478) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7fe58f7e92a0) 0 + primary-for QCoreApplication (0x0x7fe58f7a2478) + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7fe58f7e9360) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7fe58f7e93c0) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7fe58f7e9660) 0 + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7fe58f7e96c0) 0 + +Class QtPrivate::StreamStateSaver + size=16 align=8 + base size=12 base align=8 +QtPrivate::StreamStateSaver (0x0x7fe58f7e9780) 0 + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7fe58f7e9960) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7fe58f7e9c60) 0 + +Class QDateTime::ShortData + size=8 align=8 + base size=8 base align=8 +QDateTime::ShortData (0x0x7fe58f4fd180) 0 + +Class QDateTime::Data + size=8 align=8 + base size=8 base align=8 +QDateTime::Data (0x0x7fe58f4fd1e0) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7fe58f4fd120) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7fe58f5ae000) 0 + +Class std::chrono::_V2::system_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::system_clock (0x0x7fe58f2223c0) 0 empty + +Class std::chrono::_V2::steady_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::steady_clock (0x0x7fe58f368240) 0 empty + +Class QDeadlineTimer + size=16 align=8 + base size=16 base align=8 +QDeadlineTimer (0x0x7fe58f3682a0) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7fe58f0a9120) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7fe58f0a9360) 0 + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7fe58f0a95a0) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7fe58f0a9720) 0 + +Class QDebug::Stream + size=80 align=8 + base size=76 base align=8 +QDebug::Stream (0x0x7fe58f0a9ba0) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7fe58f0a9b40) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7fe58eef1de0) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7fe58eef1ea0) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7fe58eef1f60) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7fe58ef03b60) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7fe58ef03bc8) 0 + primary-for QFileDevice (0x0x7fe58ef03b60) + QObject (0x0x7fe58eef1f00) 0 + primary-for QIODevice (0x0x7fe58ef03bc8) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7fe58efb1180) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7fe58ef03d00) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7fe58ef03d68) 0 + primary-for QFile (0x0x7fe58ef03d00) + QIODevice (0x0x7fe58ef03dd0) 0 + primary-for QFileDevice (0x0x7fe58ef03d68) + QObject (0x0x7fe58efb1120) 0 + primary-for QIODevice (0x0x7fe58ef03dd0) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7fe58efb1300) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7fe58efb1780) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7fe58efb1d20) 0 + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7fe58efb1f00) 0 + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7fe58ed8d1e0) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7fe58ed7cbc8) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7fe58ed7cc30) 0 + primary-for QEventTransition (0x0x7fe58ed7cbc8) + QObject (0x0x7fe58ed8d180) 0 + primary-for QAbstractTransition (0x0x7fe58ed7cc30) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7fe58ed7cc98) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7fe58ed8d240) 0 nearly-empty + primary-for QException (0x0x7fe58ed7cc98) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7fe58ed7cd00) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7fe58ed7cd68) 0 nearly-empty + primary-for QUnhandledException (0x0x7fe58ed7cd00) + std::exception (0x0x7fe58ed8d2a0) 0 nearly-empty + primary-for QException (0x0x7fe58ed7cd68) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7fe58ed8d300) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7fe58ed8d3c0) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7fe58ed8d420) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7fe58ed8d540) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7fe58ed7cdd0) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7fe58ed8d4e0) 0 + primary-for QFileSelector (0x0x7fe58ed7cdd0) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7fe58ed8d600) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7fe58ed7ce38) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7fe58ed8d5a0) 0 + primary-for QFileSystemWatcher (0x0x7fe58ed7ce38) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7fe58ed8d6c0) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7fe58ed7cea0) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7fe58ed7cf08) 0 + primary-for QFinalState (0x0x7fe58ed7cea0) + QObject (0x0x7fe58ed8d660) 0 + primary-for QAbstractState (0x0x7fe58ed7cf08) + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7fe58ed8d720) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7fe58ed8d780) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7fe58ea54068) 0 + QBasicMutex (0x0x7fe58ed8d9c0) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7fe58ed8dd80) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7fe58ed8de40) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7fe58ed8dea0) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7fe58eaf7060) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7fe58eaf7120) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7fe58eaf7480) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 0u +48 0u +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7fe58ea54c30) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7fe58eaf7420) 0 + primary-for QFutureWatcherBase (0x0x7fe58ea54c30) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7fe58eaf7600) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7fe58e7db068) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7fe58e7db0d0) 0 + primary-for QHistoryState (0x0x7fe58e7db068) + QObject (0x0x7fe58eaf75a0) 0 + primary-for QAbstractState (0x0x7fe58e7db0d0) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7fe58eaf76c0) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7fe58e7db138) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7fe58e7db1a0) 0 + primary-for QIdentityProxyModel (0x0x7fe58e7db138) + QAbstractItemModel (0x0x7fe58e7db208) 0 + primary-for QAbstractProxyModel (0x0x7fe58e7db1a0) + QObject (0x0x7fe58eaf7660) 0 + primary-for QAbstractItemModel (0x0x7fe58e7db208) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7fe58eaf7720) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7fe58eaf7ae0) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7fe58e7db548) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7fe58eaf7a80) 0 + primary-for QItemSelectionModel (0x0x7fe58e7db548) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7fe58e7db750) 0 + QList (0x0x7fe58e7db7b8) 0 + QListSpecialMethods (0x0x7fe58eaf7d80) 0 empty + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7fe58e8c8300) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7fe58e8c8540) 0 + +Class QJsonValuePtr + size=24 align=8 + base size=24 base align=8 +QJsonValuePtr (0x0x7fe58e8c8600) 0 + +Class QJsonValueRefPtr + size=16 align=8 + base size=16 base align=8 +QJsonValueRefPtr (0x0x7fe58e8c8660) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7fe58e8c8720) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7fe58e8c8780) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7fe58e8c86c0) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7fe58e8c8840) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7fe58e8c88a0) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7fe58e8c8960) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7fe58e8c89c0) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7fe58e8c8900) 0 + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7fe58e8c8ba0) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7fe58e7dbbc8) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7fe58e8c8b40) 0 + primary-for QLibrary (0x0x7fe58e7dbbc8) + +Class QVersionNumber::SegmentStorage + size=8 align=8 + base size=8 base align=8 +QVersionNumber::SegmentStorage (0x0x7fe58e8c8d80) 0 + +Class QVersionNumber + size=8 align=8 + base size=8 base align=8 +QVersionNumber (0x0x7fe58e8c8d20) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7fe58e6f5b40) 0 empty + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7fe58e6f5ba0) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7fe58e6f5ea0) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7fe58e7931e0) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7fe58e7934e0) 0 + +Class QLinkedListData + size=32 align=8 + base size=25 base align=8 +QLinkedListData (0x0x7fe58e7937e0) 0 + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7fe58e793f60) 0 + +Class QLoggingCategory::AtomicBools + size=4 align=1 + base size=4 base align=1 +QLoggingCategory::AtomicBools (0x0x7fe58e4d6120) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7fe58e4d60c0) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7fe58e4d62a0) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7fe58e4d65a0) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7fe58e4d6900) 0 + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7fe58e4d6960) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7fe58e4d6c60) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7fe58e217000) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7fe58e217060) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7fe58e2173c0) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7fe58e7c58f0) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7fe58e217360) 0 + primary-for QMimeData (0x0x7fe58e7c58f0) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7fe58e217420) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7fe58e217780) 0 + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7fe58e217840) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7fe58e7c5a90) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7fe58e2177e0) 0 + primary-for QObjectCleanupHandler (0x0x7fe58e7c5a90) + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7fe58e217900) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7fe58e7c5af8) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7fe58e7c5b60) 0 + primary-for QParallelAnimationGroup (0x0x7fe58e7c5af8) + QAbstractAnimation (0x0x7fe58e7c5bc8) 0 + primary-for QAnimationGroup (0x0x7fe58e7c5b60) + QObject (0x0x7fe58e2178a0) 0 + primary-for QAbstractAnimation (0x0x7fe58e7c5bc8) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7fe58e2179c0) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7fe58e7c5c30) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7fe58e7c5c98) 0 + primary-for QPauseAnimation (0x0x7fe58e7c5c30) + QObject (0x0x7fe58e217960) 0 + primary-for QAbstractAnimation (0x0x7fe58e7c5c98) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7fe58e217ba0) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7fe58e217f00) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7fe58e7c5e38) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7fe58e217ea0) 0 + primary-for QPluginLoader (0x0x7fe58e7c5e38) + +Vtable for std::type_info +std::type_info::_ZTVSt9type_info: 8u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9type_info) +16 (int (*)(...))std::type_info::~type_info +24 (int (*)(...))std::type_info::~type_info +32 (int (*)(...))std::type_info::__is_pointer_p +40 (int (*)(...))std::type_info::__is_function_p +48 (int (*)(...))std::type_info::__do_catch +56 (int (*)(...))std::type_info::__do_upcast + +Class std::type_info + size=16 align=8 + base size=16 base align=8 +std::type_info (0x0x7fe58e217f60) 0 + vptr=((& std::type_info::_ZTVSt9type_info) + 16u) + +Vtable for std::bad_cast +std::bad_cast::_ZTVSt8bad_cast: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8bad_cast) +16 (int (*)(...))std::bad_cast::~bad_cast +24 (int (*)(...))std::bad_cast::~bad_cast +32 (int (*)(...))std::bad_cast::what + +Class std::bad_cast + size=8 align=8 + base size=8 base align=8 +std::bad_cast (0x0x7fe58e7c5ea0) 0 nearly-empty + vptr=((& std::bad_cast::_ZTVSt8bad_cast) + 16u) + std::exception (0x0x7fe58e2cb000) 0 nearly-empty + primary-for std::bad_cast (0x0x7fe58e7c5ea0) + +Vtable for std::bad_typeid +std::bad_typeid::_ZTVSt10bad_typeid: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt10bad_typeid) +16 (int (*)(...))std::bad_typeid::~bad_typeid +24 (int (*)(...))std::bad_typeid::~bad_typeid +32 (int (*)(...))std::bad_typeid::what + +Class std::bad_typeid + size=8 align=8 + base size=8 base align=8 +std::bad_typeid (0x0x7fe58e7c5f08) 0 nearly-empty + vptr=((& std::bad_typeid::_ZTVSt10bad_typeid) + 16u) + std::exception (0x0x7fe58e2cb060) 0 nearly-empty + primary-for std::bad_typeid (0x0x7fe58e7c5f08) + +Vtable for std::bad_function_call +std::bad_function_call::_ZTVSt17bad_function_call: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt17bad_function_call) +16 (int (*)(...))std::bad_function_call::~bad_function_call +24 (int (*)(...))std::bad_function_call::~bad_function_call +32 (int (*)(...))std::bad_function_call::what + +Class std::bad_function_call + size=8 align=8 + base size=8 base align=8 +std::bad_function_call (0x0x7fe58e03e1a0) 0 nearly-empty + vptr=((& std::bad_function_call::_ZTVSt17bad_function_call) + 16u) + std::exception (0x0x7fe58e04f120) 0 nearly-empty + primary-for std::bad_function_call (0x0x7fe58e03e1a0) + +Class std::_Nocopy_types + size=16 align=8 + base size=16 base align=8 +std::_Nocopy_types (0x0x7fe58e04f1e0) 0 + +Class std::_Any_data + size=16 align=8 + base size=16 base align=8 +std::_Any_data (0x0x7fe58e04f240) 0 + +Class std::_Function_base + size=24 align=8 + base size=24 base align=8 +std::_Function_base (0x0x7fe58e04f360) 0 + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7fe58e04f840) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7fe58e04fc00) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7fe58e03ea90) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7fe58e03eaf8) 0 + primary-for QProcess (0x0x7fe58e03ea90) + QObject (0x0x7fe58e04fba0) 0 + primary-for QIODevice (0x0x7fe58e03eaf8) + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7fe58e04fcc0) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7fe58e03eb60) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7fe58e03ebc8) 0 + primary-for QVariantAnimation (0x0x7fe58e03eb60) + QObject (0x0x7fe58e04fc60) 0 + primary-for QAbstractAnimation (0x0x7fe58e03ebc8) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7fe58e04fd80) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7fe58e03ec98) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7fe58e03ed00) 0 + primary-for QPropertyAnimation (0x0x7fe58e03ec98) + QAbstractAnimation (0x0x7fe58e03ed68) 0 + primary-for QVariantAnimation (0x0x7fe58e03ed00) + QObject (0x0x7fe58e04fd20) 0 + primary-for QAbstractAnimation (0x0x7fe58e03ed68) + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7fe58e04fe40) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7fe58e199120) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7fe58e199180) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7fe58e1991e0) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7fe58e1995a0) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7fe58e199960) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7fe58e199c60) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7fe58e199f60) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7fe58df99540) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7fe58df998a0) 0 + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7fe58df99c00) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7fe58df99d80) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7fe58ddead68) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7fe58ddeadd0) 0 + primary-for QSaveFile (0x0x7fe58ddead68) + QIODevice (0x0x7fe58ddeae38) 0 + primary-for QFileDevice (0x0x7fe58ddeadd0) + QObject (0x0x7fe58df99d20) 0 + primary-for QIODevice (0x0x7fe58ddeae38) + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7fe58df99e40) 0 + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7fe58df99f00) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7fe58ddeaea0) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7fe58ddeaf08) 0 + primary-for QSequentialAnimationGroup (0x0x7fe58ddeaea0) + QAbstractAnimation (0x0x7fe58ddeaf70) 0 + primary-for QAnimationGroup (0x0x7fe58ddeaf08) + QObject (0x0x7fe58df99ea0) 0 + primary-for QAbstractAnimation (0x0x7fe58ddeaf70) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7fe58dc7f000) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7fe58dc7b000) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7fe58df99f60) 0 + primary-for QSettings (0x0x7fe58dc7b000) + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7fe58dc7f0c0) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7fe58dc7b068) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7fe58dc7f060) 0 + primary-for QSharedMemory (0x0x7fe58dc7b068) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7fe58dc7f180) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7fe58dc7b0d0) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7fe58dc7f120) 0 + primary-for QSignalMapper (0x0x7fe58dc7b0d0) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7fe58dc7f240) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7fe58dc7b138) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7fe58dc7b1a0) 0 + primary-for QSignalTransition (0x0x7fe58dc7b138) + QObject (0x0x7fe58dc7f1e0) 0 + primary-for QAbstractTransition (0x0x7fe58dc7b1a0) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7fe58dc7f300) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7fe58dc7b208) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7fe58dc7f2a0) 0 + primary-for QSocketNotifier (0x0x7fe58dc7b208) + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7fe58dc7f3c0) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7fe58dc7b270) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7fe58dc7b2d8) 0 + primary-for QSortFilterProxyModel (0x0x7fe58dc7b270) + QAbstractItemModel (0x0x7fe58dc7b340) 0 + primary-for QAbstractProxyModel (0x0x7fe58dc7b2d8) + QObject (0x0x7fe58dc7f360) 0 + primary-for QAbstractItemModel (0x0x7fe58dc7b340) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7fe58dc7f5a0) 0 empty + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7fe58dc7f780) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7fe58dc7b4e0) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7fe58dc7b548) 0 + primary-for QState (0x0x7fe58dc7b4e0) + QObject (0x0x7fe58dc7f720) 0 + primary-for QAbstractState (0x0x7fe58dc7b548) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7fe58dc7f8a0) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7fe58dc7b6e8) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7fe58dc7f900) 0 + primary-for QStateMachine::SignalEvent (0x0x7fe58dc7b6e8) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7fe58dc7b750) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7fe58dc7f960) 0 + primary-for QStateMachine::WrappedEvent (0x0x7fe58dc7b750) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7fe58dc7b5b0) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7fe58dc7b618) 0 + primary-for QStateMachine (0x0x7fe58dc7b5b0) + QAbstractState (0x0x7fe58dc7b680) 0 + primary-for QState (0x0x7fe58dc7b618) + QObject (0x0x7fe58dc7f840) 0 + primary-for QAbstractState (0x0x7fe58dc7b680) + +Class QStorageInfo + size=8 align=8 + base size=8 base align=8 +QStorageInfo (0x0x7fe58dc7f9c0) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7fe58dc7fe40) 0 empty + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7fe58d9d5900) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7fe58da30208) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7fe58da30270) 0 + primary-for QStringListModel (0x0x7fe58da30208) + QAbstractItemModel (0x0x7fe58da302d8) 0 + primary-for QAbstractListModel (0x0x7fe58da30270) + QObject (0x0x7fe58d9d58a0) 0 + primary-for QAbstractItemModel (0x0x7fe58da302d8) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7fe58d9d5960) 0 + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7fe58d9d5a20) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7fe58d9d5b40) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7fe58da30340) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7fe58da303a8) 0 + primary-for QTemporaryFile (0x0x7fe58da30340) + QFileDevice (0x0x7fe58da30410) 0 + primary-for QFile (0x0x7fe58da303a8) + QIODevice (0x0x7fe58da30478) 0 + primary-for QFileDevice (0x0x7fe58da30410) + QObject (0x0x7fe58d9d5ae0) 0 + primary-for QIODevice (0x0x7fe58da30478) + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7fe58d9d5ba0) 0 + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7fe58d9d5d80) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 0u +64 0u + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7fe58d9d5d20) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7fe58d9d5f00) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7fe58d9d5f60) 0 + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7fe58db15060) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7fe58da30680) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7fe58db15000) 0 + primary-for QThread (0x0x7fe58da30680) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7fe58db15120) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7fe58da306e8) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7fe58db150c0) 0 + primary-for QThreadPool (0x0x7fe58da306e8) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7fe58db15180) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7fe58db152a0) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7fe58da30750) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7fe58db15240) 0 + primary-for QTimeLine (0x0x7fe58da30750) + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7fe58db15360) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7fe58da307b8) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7fe58db15300) 0 + primary-for QTimer (0x0x7fe58da307b8) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7fe58dbaf180) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7fe58dbaf120) 0 + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7fe58dbaf7e0) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7fe58dba2958) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7fe58dbaf780) 0 + primary-for QTranslator (0x0x7fe58dba2958) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7fe58dbaf900) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7fe58d8e3f00) 0 + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7fe58d9892a0) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7fe58d9895a0) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7fe58d989600) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7fe58d626ba0) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7fe58d6661a0) 0 + QVector (0x0x7fe58d696000) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7fe58d696060) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7fe58d696360) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7fe58d696660) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7fe58d696960) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7fe58d6969c0) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7fe58d696de0) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7fe58d696f00) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7fe58d76a3c0) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7fe58d76a840) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7fe58d76acc0) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7fe58d76ad80) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7fe58d76ae40) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7fe58d666ea0) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7fe58d76ade0) 0 + primary-for QGeoPositionInfoSource (0x0x7fe58d666ea0) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7fe58d41e060) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7fe58d6664e0) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7fe58d41e000) 0 + primary-for QGeoAreaMonitorSource (0x0x7fe58d6664e0) + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7fe58d6666e8) 0 + QGeoShape (0x0x7fe58d41e0c0) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7fe58d41e4e0) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7fe58d41e960) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7fe58d41ea20) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7fe58d440208) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7fe58d41e9c0) 0 + primary-for QGeoSatelliteInfoSource (0x0x7fe58d440208) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7fe58d41eae0) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7fe58d440270) 0 + QGeoShape (0x0x7fe58d41eba0) 0 + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7fe58d4d2180) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7fe58d4404e0) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7fe58d440548) 0 + primary-for QNmeaPositionInfoSource (0x0x7fe58d4404e0) + QObject (0x0x7fe58d4d2120) 0 + primary-for QGeoPositionInfoSource (0x0x7fe58d440548) + diff --git a/tests/auto/bic/data/QtPositioning.5.9.0.linux-gcc-amd64.txt b/tests/auto/bic/data/QtPositioning.5.9.0.linux-gcc-amd64.txt new file mode 100644 index 0000000..db5f7e8 --- /dev/null +++ b/tests/auto/bic/data/QtPositioning.5.9.0.linux-gcc-amd64.txt @@ -0,0 +1,4446 @@ +Class std::__failure_type + size=1 align=1 + base size=0 base align=1 +std::__failure_type (0x0x7fd646d253c0) 0 empty + +Class std::__do_is_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_destructible_impl (0x0x7fd646d6eb40) 0 empty + +Class std::__do_is_nt_destructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nt_destructible_impl (0x0x7fd646d6ed80) 0 empty + +Class std::__do_is_default_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_default_constructible_impl (0x0x7fd646d9a000) 0 empty + +Class std::__do_is_static_castable_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_static_castable_impl (0x0x7fd646d9a240) 0 empty + +Class std::__do_is_direct_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_direct_constructible_impl (0x0x7fd646d9a3c0) 0 empty + +Class std::__do_is_nary_constructible_impl + size=1 align=1 + base size=0 base align=1 +std::__do_is_nary_constructible_impl (0x0x7fd646d9a780) 0 empty + +Class std::__do_common_type_impl + size=1 align=1 + base size=0 base align=1 +std::__do_common_type_impl (0x0x7fd644a28f00) 0 empty + +Class std::__do_member_type_wrapper + size=1 align=1 + base size=0 base align=1 +std::__do_member_type_wrapper (0x0x7fd644a56000) 0 empty + +Class std::__result_of_memfun_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_ref_impl (0x0x7fd644a56360) 0 empty + +Class std::__result_of_memfun_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memfun_deref_impl (0x0x7fd644a56420) 0 empty + +Class std::__result_of_memobj_ref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_ref_impl (0x0x7fd644a564e0) 0 empty + +Class std::__result_of_memobj_deref_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_memobj_deref_impl (0x0x7fd644a565a0) 0 empty + +Class std::__result_of_other_impl + size=1 align=1 + base size=0 base align=1 +std::__result_of_other_impl (0x0x7fd644a56840) 0 empty + +Class std::piecewise_construct_t + size=1 align=1 + base size=0 base align=1 +std::piecewise_construct_t (0x0x7fd644a56a20) 0 empty + +Class std::__true_type + size=1 align=1 + base size=0 base align=1 +std::__true_type (0x0x7fd644a56ea0) 0 empty + +Class std::__false_type + size=1 align=1 + base size=0 base align=1 +std::__false_type (0x0x7fd644a56f00) 0 empty + +Class std::input_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::input_iterator_tag (0x0x7fd644b0eba0) 0 empty + +Class std::output_iterator_tag + size=1 align=1 + base size=0 base align=1 +std::output_iterator_tag (0x0x7fd644b0ec00) 0 empty + +Class std::forward_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::forward_iterator_tag (0x0x7fd6449fdbc8) 0 empty + std::input_iterator_tag (0x0x7fd644b0ec60) 0 empty + +Class std::bidirectional_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::bidirectional_iterator_tag (0x0x7fd6449fdc30) 0 empty + std::forward_iterator_tag (0x0x7fd6449fdc98) 0 empty + std::input_iterator_tag (0x0x7fd644b0ecc0) 0 empty + +Class std::random_access_iterator_tag + size=1 align=1 + base size=1 base align=1 +std::random_access_iterator_tag (0x0x7fd6449fdd00) 0 empty + std::bidirectional_iterator_tag (0x0x7fd6449fdd68) 0 empty + std::forward_iterator_tag (0x0x7fd6449fddd0) 0 empty + std::input_iterator_tag (0x0x7fd644b0ed20) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_iter (0x0x7fd644b3c9c0) 0 empty + +Class __gnu_cxx::__ops::_Iter_less_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_less_val (0x0x7fd644b3ca20) 0 empty + +Class __gnu_cxx::__ops::_Val_less_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Val_less_iter (0x0x7fd644b3ca80) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_iter + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_iter (0x0x7fd644b3cae0) 0 empty + +Class __gnu_cxx::__ops::_Iter_equal_to_val + size=1 align=1 + base size=0 base align=1 +__gnu_cxx::__ops::_Iter_equal_to_val (0x0x7fd644b3cb40) 0 empty + +Class wait + size=4 align=4 + base size=4 base align=4 +wait (0x0x7fd644822660) 0 + +Class __locale_struct + size=232 align=8 + base size=232 base align=8 +__locale_struct (0x0x7fd6448228a0) 0 + +Class timespec + size=16 align=8 + base size=16 base align=8 +timespec (0x0x7fd644822960) 0 + +Class timeval + size=16 align=8 + base size=16 base align=8 +timeval (0x0x7fd6448229c0) 0 + +Class pthread_attr_t + size=56 align=8 + base size=56 base align=8 +pthread_attr_t (0x0x7fd644822a80) 0 + +Class __pthread_internal_list + size=16 align=8 + base size=16 base align=8 +__pthread_internal_list (0x0x7fd644822ae0) 0 + +Class random_data + size=48 align=8 + base size=48 base align=8 +random_data (0x0x7fd644822f60) 0 + +Class drand48_data + size=24 align=8 + base size=24 base align=8 +drand48_data (0x0x7fd6448af000) 0 + +Vtable for std::exception +std::exception::_ZTVSt9exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9exception) +16 (int (*)(...))std::exception::~exception +24 (int (*)(...))std::exception::~exception +32 (int (*)(...))std::exception::what + +Class std::exception + size=8 align=8 + base size=8 base align=8 +std::exception (0x0x7fd6448af060) 0 nearly-empty + vptr=((& std::exception::_ZTVSt9exception) + 16u) + +Vtable for std::bad_exception +std::bad_exception::_ZTVSt13bad_exception: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13bad_exception) +16 (int (*)(...))std::bad_exception::~bad_exception +24 (int (*)(...))std::bad_exception::~bad_exception +32 (int (*)(...))std::bad_exception::what + +Class std::bad_exception + size=8 align=8 + base size=8 base align=8 +std::bad_exception (0x0x7fd644b4d340) 0 nearly-empty + vptr=((& std::bad_exception::_ZTVSt13bad_exception) + 16u) + std::exception (0x0x7fd6448af0c0) 0 nearly-empty + primary-for std::bad_exception (0x0x7fd644b4d340) + +Class std::__exception_ptr::exception_ptr + size=8 align=8 + base size=8 base align=8 +std::__exception_ptr::exception_ptr (0x0x7fd6448af120) 0 + +Vtable for std::nested_exception +std::nested_exception::_ZTVSt16nested_exception: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16nested_exception) +16 (int (*)(...))std::nested_exception::~nested_exception +24 (int (*)(...))std::nested_exception::~nested_exception + +Class std::nested_exception + size=16 align=8 + base size=16 base align=8 +std::nested_exception (0x0x7fd6448af180) 0 + vptr=((& std::nested_exception::_ZTVSt16nested_exception) + 16u) + +Vtable for std::bad_alloc +std::bad_alloc::_ZTVSt9bad_alloc: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9bad_alloc) +16 (int (*)(...))std::bad_alloc::~bad_alloc +24 (int (*)(...))std::bad_alloc::~bad_alloc +32 (int (*)(...))std::bad_alloc::what + +Class std::bad_alloc + size=8 align=8 + base size=8 base align=8 +std::bad_alloc (0x0x7fd644b4d548) 0 nearly-empty + vptr=((& std::bad_alloc::_ZTVSt9bad_alloc) + 16u) + std::exception (0x0x7fd6448af5a0) 0 nearly-empty + primary-for std::bad_alloc (0x0x7fd644b4d548) + +Vtable for std::bad_array_new_length +std::bad_array_new_length::_ZTVSt20bad_array_new_length: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt20bad_array_new_length) +16 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +24 (int (*)(...))std::bad_array_new_length::~bad_array_new_length +32 (int (*)(...))std::bad_array_new_length::what + +Class std::bad_array_new_length + size=8 align=8 + base size=8 base align=8 +std::bad_array_new_length (0x0x7fd644b4d5b0) 0 nearly-empty + vptr=((& std::bad_array_new_length::_ZTVSt20bad_array_new_length) + 16u) + std::bad_alloc (0x0x7fd644b4d618) 0 nearly-empty + primary-for std::bad_array_new_length (0x0x7fd644b4d5b0) + std::exception (0x0x7fd6448af600) 0 nearly-empty + primary-for std::bad_alloc (0x0x7fd644b4d618) + +Class std::nothrow_t + size=1 align=1 + base size=0 base align=1 +std::nothrow_t (0x0x7fd6448af660) 0 empty + +Class __exception + size=40 align=8 + base size=40 base align=8 +__exception (0x0x7fd6446342a0) 0 + +Class lconv + size=96 align=8 + base size=96 base align=8 +lconv (0x0x7fd644634f60) 0 + +Vtable for __cxxabiv1::__forced_unwind +__cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN10__cxxabiv115__forced_unwindE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class __cxxabiv1::__forced_unwind + size=8 align=8 + base size=8 base align=8 +__cxxabiv1::__forced_unwind (0x0x7fd64446f000) 0 nearly-empty + vptr=((& __cxxabiv1::__forced_unwind::_ZTVN10__cxxabiv115__forced_unwindE) + 16u) + +Class sched_param + size=4 align=4 + base size=4 base align=4 +sched_param (0x0x7fd64446fea0) 0 + +Class __sched_param + size=4 align=4 + base size=4 base align=4 +__sched_param (0x0x7fd64446ff00) 0 + +Class timex + size=208 align=8 + base size=208 base align=8 +timex (0x0x7fd644501000) 0 + +Class tm + size=56 align=8 + base size=56 base align=8 +tm (0x0x7fd644501060) 0 + +Class itimerspec + size=32 align=8 + base size=32 base align=8 +itimerspec (0x0x7fd6445010c0) 0 + +Class _pthread_cleanup_buffer + size=32 align=8 + base size=32 base align=8 +_pthread_cleanup_buffer (0x0x7fd644501120) 0 + +Class __pthread_cleanup_frame + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_frame (0x0x7fd644501240) 0 + +Class __pthread_cleanup_class + size=24 align=8 + base size=24 base align=8 +__pthread_cleanup_class (0x0x7fd6445012a0) 0 + +Class _IO_marker + size=24 align=8 + base size=24 base align=8 +_IO_marker (0x0x7fd6445016c0) 0 + +Class _IO_FILE + size=216 align=8 + base size=216 base align=8 +_IO_FILE (0x0x7fd644501720) 0 + +Class std::_Hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Hash_impl (0x0x7fd644337f00) 0 empty + +Class std::_Fnv_hash_impl + size=1 align=1 + base size=0 base align=1 +std::_Fnv_hash_impl (0x0x7fd644337f60) 0 empty + +Class std::__numeric_limits_base + size=1 align=1 + base size=0 base align=1 +std::__numeric_limits_base (0x0x7fd64400ef00) 0 empty + +Class std::_Bit_reference + size=16 align=8 + base size=16 base align=8 +std::_Bit_reference (0x0x7fd6441b0d20) 0 + +Class std::_Bit_iterator_base + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator_base (0x0x7fd6441af208) 0 + std::iterator (0x0x7fd6441b0de0) 0 empty + +Class std::_Bit_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_iterator (0x0x7fd6441af270) 0 + std::_Bit_iterator_base (0x0x7fd6441af2d8) 0 + std::iterator (0x0x7fd6441b0e40) 0 empty + +Class std::_Bit_const_iterator + size=16 align=8 + base size=12 base align=8 +std::_Bit_const_iterator (0x0x7fd6441af340) 0 + std::_Bit_iterator_base (0x0x7fd6441af3a8) 0 + std::iterator (0x0x7fd6441b0ea0) 0 empty + +Class std::random_device + size=5000 align=8 + base size=5000 base align=8 +std::random_device (0x0x7fd643f5acc0) 0 + +Class std::bernoulli_distribution::param_type + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution::param_type (0x0x7fd643ccca80) 0 + +Class std::bernoulli_distribution + size=8 align=8 + base size=8 base align=8 +std::bernoulli_distribution (0x0x7fd643ccca20) 0 + +Class std::seed_seq + size=24 align=8 + base size=24 base align=8 +std::seed_seq (0x0x7fd643a38a20) 0 + +Class qIsNull(double)::U + size=8 align=8 + base size=8 base align=8 +qIsNull(double)::U (0x0x7fd6426244e0) 0 + +Class qIsNull(float)::U + size=4 align=4 + base size=4 base align=4 +qIsNull(float)::U (0x0x7fd642624540) 0 + +Class QSysInfo + size=1 align=1 + base size=0 base align=1 +QSysInfo (0x0x7fd6426f5060) 0 empty + +Class QMessageLogContext + size=32 align=8 + base size=32 base align=8 +QMessageLogContext (0x0x7fd6426f50c0) 0 + +Class QMessageLogger + size=32 align=8 + base size=32 base align=8 +QMessageLogger (0x0x7fd6426f5120) 0 + +Class QFlag + size=4 align=4 + base size=4 base align=4 +QFlag (0x0x7fd6426f5180) 0 + +Class QIncompatibleFlag + size=4 align=4 + base size=4 base align=4 +QIncompatibleFlag (0x0x7fd6426f5420) 0 + +Class std::__atomic_flag_base + size=1 align=1 + base size=1 base align=1 +std::__atomic_flag_base (0x0x7fd6426f5960) 0 + +Class std::atomic_flag + size=1 align=1 + base size=1 base align=1 +std::atomic_flag (0x0x7fd642743208) 0 + std::__atomic_flag_base (0x0x7fd6426f59c0) 0 + +Class QAtomicInt + size=4 align=4 + base size=4 base align=4 +QAtomicInt (0x0x7fd642743958) 0 + QAtomicInteger (0x0x7fd6427439c0) 0 + QBasicAtomicInteger (0x0x7fd64231d120) 0 + +Class QInternal + size=1 align=1 + base size=0 base align=1 +QInternal (0x0x7fd642191600) 0 empty + +Class QGenericArgument + size=16 align=8 + base size=16 base align=8 +QGenericArgument (0x0x7fd641f87660) 0 + +Class QGenericReturnArgument + size=16 align=8 + base size=16 base align=8 +QGenericReturnArgument (0x0x7fd641f29270) 0 + QGenericArgument (0x0x7fd641f876c0) 0 + +Class QMetaObject + size=48 align=8 + base size=48 base align=8 +QMetaObject (0x0x7fd641f87840) 0 + +Class QMetaObject::Connection + size=8 align=8 + base size=8 base align=8 +QMetaObject::Connection (0x0x7fd641f87900) 0 + +Class QLatin1Char + size=1 align=1 + base size=1 base align=1 +QLatin1Char (0x0x7fd641c4a960) 0 + +Class QChar + size=2 align=2 + base size=2 base align=2 +QChar (0x0x7fd641c4a9c0) 0 + +Class QtPrivate::RefCount + size=4 align=4 + base size=4 base align=4 +QtPrivate::RefCount (0x0x7fd641c4ac60) 0 + +Class QArrayData + size=24 align=8 + base size=24 base align=8 +QArrayData (0x0x7fd641c4ad20) 0 + +Class QtPrivate::QContainerImplHelper + size=1 align=1 + base size=0 base align=1 +QtPrivate::QContainerImplHelper (0x0x7fd641d5b1e0) 0 empty + +Class std::locale + size=8 align=8 + base size=8 base align=8 +std::locale (0x0x7fd641d5b240) 0 + +Vtable for std::locale::facet +std::locale::facet::_ZTVNSt6locale5facetE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt6locale5facetE) +16 (int (*)(...))std::locale::facet::~facet +24 (int (*)(...))std::locale::facet::~facet + +Class std::locale::facet + size=16 align=8 + base size=12 base align=8 +std::locale::facet (0x0x7fd641d5b2a0) 0 + vptr=((& std::locale::facet::_ZTVNSt6locale5facetE) + 16u) + +Class std::locale::id + size=8 align=8 + base size=8 base align=8 +std::locale::id (0x0x7fd641d5b300) 0 + +Class std::locale::_Impl + size=40 align=8 + base size=40 base align=8 +std::locale::_Impl (0x0x7fd641d5b360) 0 + +Class std::__cow_string + size=8 align=8 + base size=8 base align=8 +std::__cow_string (0x0x7fd641d5b720) 0 + +Vtable for std::logic_error +std::logic_error::_ZTVSt11logic_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11logic_error) +16 (int (*)(...))std::logic_error::~logic_error +24 (int (*)(...))std::logic_error::~logic_error +32 (int (*)(...))std::logic_error::what + +Class std::logic_error + size=16 align=8 + base size=16 base align=8 +std::logic_error (0x0x7fd641d63888) 0 + vptr=((& std::logic_error::_ZTVSt11logic_error) + 16u) + std::exception (0x0x7fd641d5b7e0) 0 nearly-empty + primary-for std::logic_error (0x0x7fd641d63888) + +Vtable for std::domain_error +std::domain_error::_ZTVSt12domain_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12domain_error) +16 (int (*)(...))std::domain_error::~domain_error +24 (int (*)(...))std::domain_error::~domain_error +32 (int (*)(...))std::logic_error::what + +Class std::domain_error + size=16 align=8 + base size=16 base align=8 +std::domain_error (0x0x7fd641d638f0) 0 + vptr=((& std::domain_error::_ZTVSt12domain_error) + 16u) + std::logic_error (0x0x7fd641d63958) 0 + primary-for std::domain_error (0x0x7fd641d638f0) + std::exception (0x0x7fd641d5b840) 0 nearly-empty + primary-for std::logic_error (0x0x7fd641d63958) + +Vtable for std::invalid_argument +std::invalid_argument::_ZTVSt16invalid_argument: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt16invalid_argument) +16 (int (*)(...))std::invalid_argument::~invalid_argument +24 (int (*)(...))std::invalid_argument::~invalid_argument +32 (int (*)(...))std::logic_error::what + +Class std::invalid_argument + size=16 align=8 + base size=16 base align=8 +std::invalid_argument (0x0x7fd641d639c0) 0 + vptr=((& std::invalid_argument::_ZTVSt16invalid_argument) + 16u) + std::logic_error (0x0x7fd641d63a28) 0 + primary-for std::invalid_argument (0x0x7fd641d639c0) + std::exception (0x0x7fd641d5b8a0) 0 nearly-empty + primary-for std::logic_error (0x0x7fd641d63a28) + +Vtable for std::length_error +std::length_error::_ZTVSt12length_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12length_error) +16 (int (*)(...))std::length_error::~length_error +24 (int (*)(...))std::length_error::~length_error +32 (int (*)(...))std::logic_error::what + +Class std::length_error + size=16 align=8 + base size=16 base align=8 +std::length_error (0x0x7fd641d63a90) 0 + vptr=((& std::length_error::_ZTVSt12length_error) + 16u) + std::logic_error (0x0x7fd641d63af8) 0 + primary-for std::length_error (0x0x7fd641d63a90) + std::exception (0x0x7fd641d5b900) 0 nearly-empty + primary-for std::logic_error (0x0x7fd641d63af8) + +Vtable for std::out_of_range +std::out_of_range::_ZTVSt12out_of_range: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12out_of_range) +16 (int (*)(...))std::out_of_range::~out_of_range +24 (int (*)(...))std::out_of_range::~out_of_range +32 (int (*)(...))std::logic_error::what + +Class std::out_of_range + size=16 align=8 + base size=16 base align=8 +std::out_of_range (0x0x7fd641d63b60) 0 + vptr=((& std::out_of_range::_ZTVSt12out_of_range) + 16u) + std::logic_error (0x0x7fd641d63bc8) 0 + primary-for std::out_of_range (0x0x7fd641d63b60) + std::exception (0x0x7fd641d5b960) 0 nearly-empty + primary-for std::logic_error (0x0x7fd641d63bc8) + +Vtable for std::runtime_error +std::runtime_error::_ZTVSt13runtime_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt13runtime_error) +16 (int (*)(...))std::runtime_error::~runtime_error +24 (int (*)(...))std::runtime_error::~runtime_error +32 (int (*)(...))std::runtime_error::what + +Class std::runtime_error + size=16 align=8 + base size=16 base align=8 +std::runtime_error (0x0x7fd641d63c30) 0 + vptr=((& std::runtime_error::_ZTVSt13runtime_error) + 16u) + std::exception (0x0x7fd641d5b9c0) 0 nearly-empty + primary-for std::runtime_error (0x0x7fd641d63c30) + +Vtable for std::range_error +std::range_error::_ZTVSt11range_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt11range_error) +16 (int (*)(...))std::range_error::~range_error +24 (int (*)(...))std::range_error::~range_error +32 (int (*)(...))std::runtime_error::what + +Class std::range_error + size=16 align=8 + base size=16 base align=8 +std::range_error (0x0x7fd641d63c98) 0 + vptr=((& std::range_error::_ZTVSt11range_error) + 16u) + std::runtime_error (0x0x7fd641d63d00) 0 + primary-for std::range_error (0x0x7fd641d63c98) + std::exception (0x0x7fd641d5ba20) 0 nearly-empty + primary-for std::runtime_error (0x0x7fd641d63d00) + +Vtable for std::overflow_error +std::overflow_error::_ZTVSt14overflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt14overflow_error) +16 (int (*)(...))std::overflow_error::~overflow_error +24 (int (*)(...))std::overflow_error::~overflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::overflow_error + size=16 align=8 + base size=16 base align=8 +std::overflow_error (0x0x7fd641d63d68) 0 + vptr=((& std::overflow_error::_ZTVSt14overflow_error) + 16u) + std::runtime_error (0x0x7fd641d63dd0) 0 + primary-for std::overflow_error (0x0x7fd641d63d68) + std::exception (0x0x7fd641d5ba80) 0 nearly-empty + primary-for std::runtime_error (0x0x7fd641d63dd0) + +Vtable for std::underflow_error +std::underflow_error::_ZTVSt15underflow_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt15underflow_error) +16 (int (*)(...))std::underflow_error::~underflow_error +24 (int (*)(...))std::underflow_error::~underflow_error +32 (int (*)(...))std::runtime_error::what + +Class std::underflow_error + size=16 align=8 + base size=16 base align=8 +std::underflow_error (0x0x7fd641d63e38) 0 + vptr=((& std::underflow_error::_ZTVSt15underflow_error) + 16u) + std::runtime_error (0x0x7fd641d63ea0) 0 + primary-for std::underflow_error (0x0x7fd641d63e38) + std::exception (0x0x7fd641d5bae0) 0 nearly-empty + primary-for std::runtime_error (0x0x7fd641d63ea0) + +Vtable for std::_V2::error_category +std::_V2::error_category::_ZTVNSt3_V214error_categoryE: 10u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt3_V214error_categoryE) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))std::_V2::error_category::_M_message +48 (int (*)(...))__cxa_pure_virtual +56 (int (*)(...))std::_V2::error_category::default_error_condition +64 (int (*)(...))std::_V2::error_category::equivalent +72 (int (*)(...))std::_V2::error_category::equivalent + +Class std::_V2::error_category + size=8 align=8 + base size=8 base align=8 +std::_V2::error_category (0x0x7fd641d5bc60) 0 nearly-empty + vptr=((& std::_V2::error_category::_ZTVNSt3_V214error_categoryE) + 16u) + +Class std::error_code + size=16 align=8 + base size=16 base align=8 +std::error_code (0x0x7fd641d5bea0) 0 + +Class std::error_condition + size=16 align=8 + base size=16 base align=8 +std::error_condition (0x0x7fd641a4b060) 0 + +Vtable for std::system_error +std::system_error::_ZTVSt12system_error: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt12system_error) +16 (int (*)(...))std::system_error::~system_error +24 (int (*)(...))std::system_error::~system_error +32 (int (*)(...))std::runtime_error::what + +Class std::system_error + size=32 align=8 + base size=32 base align=8 +std::system_error (0x0x7fd641a472d8) 0 + vptr=((& std::system_error::_ZTVSt12system_error) + 16u) + std::runtime_error (0x0x7fd641a47340) 0 + primary-for std::system_error (0x0x7fd641a472d8) + std::exception (0x0x7fd641a4b2a0) 0 nearly-empty + primary-for std::runtime_error (0x0x7fd641a47340) + +Vtable for std::ios_base::failure +std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTINSt8ios_base7failureB5cxx11E) +16 (int (*)(...))std::ios_base::failure::~failure +24 (int (*)(...))std::ios_base::failure::~failure +32 (int (*)(...))std::ios_base::failure::what + +Class std::ios_base::failure + size=32 align=8 + base size=32 base align=8 +std::ios_base::failure (0x0x7fd641a47f08) 0 + vptr=((& std::ios_base::failure::_ZTVNSt8ios_base7failureB5cxx11E) + 16u) + std::system_error (0x0x7fd641a47f70) 0 + primary-for std::ios_base::failure (0x0x7fd641a47f08) + std::runtime_error (0x0x7fd641ab2000) 0 + primary-for std::system_error (0x0x7fd641a47f70) + std::exception (0x0x7fd641a4b5a0) 0 nearly-empty + primary-for std::runtime_error (0x0x7fd641ab2000) + +Class std::ios_base::_Callback_list + size=24 align=8 + base size=24 base align=8 +std::ios_base::_Callback_list (0x0x7fd641a4b600) 0 + +Class std::ios_base::_Words + size=16 align=8 + base size=16 base align=8 +std::ios_base::_Words (0x0x7fd641a4b660) 0 + +Class std::ios_base::Init + size=1 align=1 + base size=0 base align=1 +std::ios_base::Init (0x0x7fd641a4b6c0) 0 empty + +Vtable for std::ios_base +std::ios_base::_ZTVSt8ios_base: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8ios_base) +16 (int (*)(...))std::ios_base::~ios_base +24 (int (*)(...))std::ios_base::~ios_base + +Class std::ios_base + size=216 align=8 + base size=216 base align=8 +std::ios_base (0x0x7fd641a4b540) 0 + vptr=((& std::ios_base::_ZTVSt8ios_base) + 16u) + +Class std::ctype_base + size=1 align=1 + base size=0 base align=1 +std::ctype_base (0x0x7fd641a4be40) 0 empty + +Class std::__num_base + size=1 align=1 + base size=0 base align=1 +std::__num_base (0x0x7fd641bc2540) 0 empty + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSo: 2u entries +0 ((& std::basic_ostream::_ZTVSo) + 24u) +8 ((& std::basic_ostream::_ZTVSo) + 64u) + +VTT for std::basic_ostream +std::basic_ostream::_ZTTSt13basic_ostreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_ostream::_ZTVSt13basic_ostreamIwSt11char_traitsIwEE) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSi: 2u entries +0 ((& std::basic_istream::_ZTVSi) + 24u) +8 ((& std::basic_istream::_ZTVSi) + 64u) + +VTT for std::basic_istream +std::basic_istream::_ZTTSt13basic_istreamIwSt11char_traitsIwEE: 2u entries +0 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_istream::_ZTVSt13basic_istreamIwSt11char_traitsIwEE) + 64u) + +Construction vtable for std::basic_istream (0x0x7fd6417089c0 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd0_Si: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISi) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISi) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7fd641708a90 instance) in std::basic_iostream +std::basic_iostream::_ZTCSd16_So: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISo) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISo) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSd: 7u entries +0 ((& std::basic_iostream::_ZTVSd) + 24u) +8 ((& std::basic_iostream::_ZTCSd0_Si) + 24u) +16 ((& std::basic_iostream::_ZTCSd0_Si) + 64u) +24 ((& std::basic_iostream::_ZTCSd16_So) + 24u) +32 ((& std::basic_iostream::_ZTCSd16_So) + 64u) +40 ((& std::basic_iostream::_ZTVSd) + 104u) +48 ((& std::basic_iostream::_ZTVSd) + 64u) + +Construction vtable for std::basic_istream (0x0x7fd641708e38 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E: 10u entries +0 24u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551592u +48 (int (*)(...))-24 +56 (int (*)(...))(& _ZTISt13basic_istreamIwSt11char_traitsIwEE) +64 0u +72 0u + +Construction vtable for std::basic_ostream (0x0x7fd641708f08 instance) in std::basic_iostream +std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E: 10u entries +0 8u +8 (int (*)(...))0 +16 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +24 0u +32 0u +40 18446744073709551608u +48 (int (*)(...))-8 +56 (int (*)(...))(& _ZTISt13basic_ostreamIwSt11char_traitsIwEE) +64 0u +72 0u + +VTT for std::basic_iostream +std::basic_iostream::_ZTTSt14basic_iostreamIwSt11char_traitsIwEE: 7u entries +0 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 24u) +8 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 24u) +16 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE0_St13basic_istreamIwS1_E) + 64u) +24 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 24u) +32 ((& std::basic_iostream::_ZTCSt14basic_iostreamIwSt11char_traitsIwEE16_St13basic_ostreamIwS1_E) + 64u) +40 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 104u) +48 ((& std::basic_iostream::_ZTVSt14basic_iostreamIwSt11char_traitsIwEE) + 64u) + +Class QByteArrayDataPtr + size=8 align=8 + base size=8 base align=8 +QByteArrayDataPtr (0x0x7fd641957de0) 0 + +Class QByteArray + size=8 align=8 + base size=8 base align=8 +QByteArray (0x0x7fd641957e40) 0 + +Class QByteRef + size=16 align=8 + base size=12 base align=8 +QByteRef (0x0x7fd6414b5300) 0 + +Class QLatin1String + size=16 align=8 + base size=16 base align=8 +QLatin1String (0x0x7fd6414b5600) 0 + +Class QStringDataPtr + size=8 align=8 + base size=8 base align=8 +QStringDataPtr (0x0x7fd6414b5960) 0 + +Class QString::Null + size=1 align=1 + base size=0 base align=1 +QString::Null (0x0x7fd6414b5a20) 0 empty + +Class QString + size=8 align=8 + base size=8 base align=8 +QString (0x0x7fd6414b59c0) 0 + +Class QCharRef + size=16 align=8 + base size=12 base align=8 +QCharRef (0x0x7fd6412e4a20) 0 + +Class QStringRef + size=16 align=8 + base size=16 base align=8 +QStringRef (0x0x7fd6410727e0) 0 + +Class QtPrivate::QHashCombine + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombine (0x0x7fd641072d20) 0 empty + +Class QtPrivate::QHashCombineCommutative + size=1 align=1 + base size=0 base align=1 +QtPrivate::QHashCombineCommutative (0x0x7fd641072d80) 0 empty + +Class std::__detail::_List_node_base + size=16 align=8 + base size=16 base align=8 +std::__detail::_List_node_base (0x0x7fd641072de0) 0 + +Class QListData::NotArrayCompatibleLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotArrayCompatibleLayout (0x0x7fd640e3d1e0) 0 empty + +Class QListData::NotIndirectLayout + size=1 align=1 + base size=0 base align=1 +QListData::NotIndirectLayout (0x0x7fd640e3d240) 0 empty + +Class QListData::ArrayCompatibleLayout + size=1 align=1 + base size=1 base align=1 +QListData::ArrayCompatibleLayout (0x0x7fd641106478) 0 empty + QListData::NotIndirectLayout (0x0x7fd640e3d2a0) 0 empty + +Class QListData::InlineWithPaddingLayout + size=1 align=1 + base size=1 base align=1 +QListData::InlineWithPaddingLayout (0x0x7fd640ed2af0) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7fd640e3d300) 0 empty + QListData::NotIndirectLayout (0x0x7fd640e3d360) 0 empty + +Class QListData::IndirectLayout + size=1 align=1 + base size=1 base align=1 +QListData::IndirectLayout (0x0x7fd6411064e0) 0 empty + QListData::NotArrayCompatibleLayout (0x0x7fd640e3d3c0) 0 empty + +Class QListData::Data + size=24 align=8 + base size=24 base align=8 +QListData::Data (0x0x7fd640e3d420) 0 + +Class QListData + size=8 align=8 + base size=8 base align=8 +QListData (0x0x7fd640e3d180) 0 + +Class QRegExp + size=8 align=8 + base size=8 base align=8 +QRegExp (0x0x7fd640bfa000) 0 + +Class QStringMatcher::Data + size=272 align=8 + base size=272 base align=8 +QStringMatcher::Data (0x0x7fd640c7e240) 0 + +Class QStringMatcher + size=1048 align=8 + base size=1048 base align=8 +QStringMatcher (0x0x7fd640c7e1e0) 0 + +Class QStringList + size=8 align=8 + base size=8 base align=8 +QStringList (0x0x7fd640c6df08) 0 + QList (0x0x7fd640c6df70) 0 + QListSpecialMethods (0x0x7fd640c7e420) 0 empty + +Class QScopedPointerPodDeleter + size=1 align=1 + base size=0 base align=1 +QScopedPointerPodDeleter (0x0x7fd640c7e840) 0 empty + +Class std::_Rb_tree_node_base + size=32 align=8 + base size=32 base align=8 +std::_Rb_tree_node_base (0x0x7fd640c7ec60) 0 + +Class std::allocator_arg_t + size=1 align=1 + base size=0 base align=1 +std::allocator_arg_t (0x0x7fd640a52300) 0 empty + +Class std::__uses_alloc_base + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc_base (0x0x7fd640a52480) 0 empty + +Class std::__uses_alloc0::_Sink + size=1 align=1 + base size=0 base align=1 +std::__uses_alloc0::_Sink (0x0x7fd640a52540) 0 empty + +Class std::__uses_alloc0 + size=1 align=1 + base size=1 base align=1 +std::__uses_alloc0 (0x0x7fd640cd4888) 0 + std::__uses_alloc_base (0x0x7fd640a524e0) 0 empty + +Class std::_Swallow_assign + size=1 align=1 + base size=0 base align=1 +std::_Swallow_assign (0x0x7fd6407fe5a0) 0 empty + +Class QtPrivate::AbstractDebugStreamFunction + size=16 align=8 + base size=16 base align=8 +QtPrivate::AbstractDebugStreamFunction (0x0x7fd6407fe7e0) 0 + +Class QtPrivate::AbstractComparatorFunction + size=24 align=8 + base size=24 base align=8 +QtPrivate::AbstractComparatorFunction (0x0x7fd6407fe8a0) 0 + +Class QtPrivate::AbstractConverterFunction + size=8 align=8 + base size=8 base align=8 +QtPrivate::AbstractConverterFunction (0x0x7fd6407fe9c0) 0 + +Class QMetaType + size=80 align=8 + base size=80 base align=8 +QMetaType (0x0x7fd6407feb40) 0 + +Class QtMetaTypePrivate::VariantData + size=24 align=8 + base size=20 base align=8 +QtMetaTypePrivate::VariantData (0x0x7fd6407fef60) 0 + +Class QtMetaTypePrivate::VectorBoolElements + size=1 align=1 + base size=0 base align=1 +QtMetaTypePrivate::VectorBoolElements (0x0x7fd6409530c0) 0 empty + +Class QtMetaTypePrivate::QSequentialIterableImpl + size=104 align=8 + base size=104 base align=8 +QtMetaTypePrivate::QSequentialIterableImpl (0x0x7fd640953a20) 0 + +Class QtMetaTypePrivate::QAssociativeIterableImpl + size=112 align=8 + base size=112 base align=8 +QtMetaTypePrivate::QAssociativeIterableImpl (0x0x7fd640953f00) 0 + +Class QtMetaTypePrivate::QPairVariantInterfaceImpl + size=40 align=8 + base size=40 base align=8 +QtMetaTypePrivate::QPairVariantInterfaceImpl (0x0x7fd64062f2a0) 0 + +Class QtPrivate::QSlotObjectBase + size=16 align=8 + base size=16 base align=8 +QtPrivate::QSlotObjectBase (0x0x7fd6403e7180) 0 + +Class std::chrono::_V2::system_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::system_clock (0x0x7fd640464660) 0 empty + +Class std::chrono::_V2::steady_clock + size=1 align=1 + base size=0 base align=1 +std::chrono::_V2::steady_clock (0x0x7fd6405b04e0) 0 empty + +Vtable for QObjectData +QObjectData::_ZTV11QObjectData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QObjectData) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))__cxa_pure_virtual + +Class QObjectData + size=48 align=8 + base size=48 base align=8 +QObjectData (0x0x7fd6405b0540) 0 + vptr=((& QObjectData::_ZTV11QObjectData) + 16u) + +Class QObject::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObject::QPrivateSignal (0x0x7fd6405b0720) 0 empty + +Vtable for QObject +QObject::_ZTV7QObject: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QObject) +16 (int (*)(...))QObject::metaObject +24 (int (*)(...))QObject::qt_metacast +32 (int (*)(...))QObject::qt_metacall +40 (int (*)(...))QObject::~QObject +48 (int (*)(...))QObject::~QObject +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObject + size=16 align=8 + base size=16 base align=8 +QObject (0x0x7fd6405b06c0) 0 + vptr=((& QObject::_ZTV7QObject) + 16u) + +Vtable for QObjectUserData +QObjectUserData::_ZTV15QObjectUserData: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QObjectUserData) +16 (int (*)(...))QObjectUserData::~QObjectUserData +24 (int (*)(...))QObjectUserData::~QObjectUserData + +Class QObjectUserData + size=8 align=8 + base size=8 base align=8 +QObjectUserData (0x0x7fd6405b0e40) 0 nearly-empty + vptr=((& QObjectUserData::_ZTV15QObjectUserData) + 16u) + +Class QSignalBlocker + size=16 align=8 + base size=10 base align=8 +QSignalBlocker (0x0x7fd6405b0ea0) 0 + +Class QAbstractAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractAnimation::QPrivateSignal (0x0x7fd6405b0f60) 0 empty + +Vtable for QAbstractAnimation +QAbstractAnimation::_ZTV18QAbstractAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractAnimation) +16 (int (*)(...))QAbstractAnimation::metaObject +24 (int (*)(...))QAbstractAnimation::qt_metacast +32 (int (*)(...))QAbstractAnimation::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAbstractAnimation + size=16 align=8 + base size=16 base align=8 +QAbstractAnimation (0x0x7fd6401ae888) 0 + vptr=((& QAbstractAnimation::_ZTV18QAbstractAnimation) + 16u) + QObject (0x0x7fd6405b0f00) 0 + primary-for QAbstractAnimation (0x0x7fd6401ae888) + +Class QAnimationDriver::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationDriver::QPrivateSignal (0x0x7fd6402b2060) 0 empty + +Vtable for QAnimationDriver +QAnimationDriver::_ZTV16QAnimationDriver: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QAnimationDriver) +16 (int (*)(...))QAnimationDriver::metaObject +24 (int (*)(...))QAnimationDriver::qt_metacast +32 (int (*)(...))QAnimationDriver::qt_metacall +40 (int (*)(...))QAnimationDriver::~QAnimationDriver +48 (int (*)(...))QAnimationDriver::~QAnimationDriver +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAnimationDriver::advance +120 (int (*)(...))QAnimationDriver::elapsed +128 (int (*)(...))QAnimationDriver::start +136 (int (*)(...))QAnimationDriver::stop + +Class QAnimationDriver + size=16 align=8 + base size=16 base align=8 +QAnimationDriver (0x0x7fd6401ae8f0) 0 + vptr=((& QAnimationDriver::_ZTV16QAnimationDriver) + 16u) + QObject (0x0x7fd6402b2000) 0 + primary-for QAnimationDriver (0x0x7fd6401ae8f0) + +Class QEventLoop::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventLoop::QPrivateSignal (0x0x7fd6402b2120) 0 empty + +Vtable for QEventLoop +QEventLoop::_ZTV10QEventLoop: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QEventLoop) +16 (int (*)(...))QEventLoop::metaObject +24 (int (*)(...))QEventLoop::qt_metacast +32 (int (*)(...))QEventLoop::qt_metacall +40 (int (*)(...))QEventLoop::~QEventLoop +48 (int (*)(...))QEventLoop::~QEventLoop +56 (int (*)(...))QEventLoop::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QEventLoop + size=16 align=8 + base size=16 base align=8 +QEventLoop (0x0x7fd6401ae958) 0 + vptr=((& QEventLoop::_ZTV10QEventLoop) + 16u) + QObject (0x0x7fd6402b20c0) 0 + primary-for QEventLoop (0x0x7fd6401ae958) + +Class QEventLoopLocker + size=8 align=8 + base size=8 base align=8 +QEventLoopLocker (0x0x7fd6402b2300) 0 + +Class QAbstractEventDispatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractEventDispatcher::QPrivateSignal (0x0x7fd6402b23c0) 0 empty + +Class QAbstractEventDispatcher::TimerInfo + size=12 align=4 + base size=12 base align=4 +QAbstractEventDispatcher::TimerInfo (0x0x7fd6402b2420) 0 + +Vtable for QAbstractEventDispatcher +QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher: 28u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QAbstractEventDispatcher) +16 (int (*)(...))QAbstractEventDispatcher::metaObject +24 (int (*)(...))QAbstractEventDispatcher::qt_metacast +32 (int (*)(...))QAbstractEventDispatcher::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual +184 (int (*)(...))__cxa_pure_virtual +192 (int (*)(...))__cxa_pure_virtual +200 (int (*)(...))__cxa_pure_virtual +208 (int (*)(...))QAbstractEventDispatcher::startingUp +216 (int (*)(...))QAbstractEventDispatcher::closingDown + +Class QAbstractEventDispatcher + size=16 align=8 + base size=16 base align=8 +QAbstractEventDispatcher (0x0x7fd6401aea90) 0 + vptr=((& QAbstractEventDispatcher::_ZTV24QAbstractEventDispatcher) + 16u) + QObject (0x0x7fd6402b2360) 0 + primary-for QAbstractEventDispatcher (0x0x7fd6401aea90) + +Vtable for std::type_info +std::type_info::_ZTVSt9type_info: 8u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt9type_info) +16 (int (*)(...))std::type_info::~type_info +24 (int (*)(...))std::type_info::~type_info +32 (int (*)(...))std::type_info::__is_pointer_p +40 (int (*)(...))std::type_info::__is_function_p +48 (int (*)(...))std::type_info::__do_catch +56 (int (*)(...))std::type_info::__do_upcast + +Class std::type_info + size=16 align=8 + base size=16 base align=8 +std::type_info (0x0x7fd6402b2480) 0 + vptr=((& std::type_info::_ZTVSt9type_info) + 16u) + +Vtable for std::bad_cast +std::bad_cast::_ZTVSt8bad_cast: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt8bad_cast) +16 (int (*)(...))std::bad_cast::~bad_cast +24 (int (*)(...))std::bad_cast::~bad_cast +32 (int (*)(...))std::bad_cast::what + +Class std::bad_cast + size=8 align=8 + base size=8 base align=8 +std::bad_cast (0x0x7fd6401aeaf8) 0 nearly-empty + vptr=((& std::bad_cast::_ZTVSt8bad_cast) + 16u) + std::exception (0x0x7fd6402b24e0) 0 nearly-empty + primary-for std::bad_cast (0x0x7fd6401aeaf8) + +Vtable for std::bad_typeid +std::bad_typeid::_ZTVSt10bad_typeid: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt10bad_typeid) +16 (int (*)(...))std::bad_typeid::~bad_typeid +24 (int (*)(...))std::bad_typeid::~bad_typeid +32 (int (*)(...))std::bad_typeid::what + +Class std::bad_typeid + size=8 align=8 + base size=8 base align=8 +std::bad_typeid (0x0x7fd6401aeb60) 0 nearly-empty + vptr=((& std::bad_typeid::_ZTVSt10bad_typeid) + 16u) + std::exception (0x0x7fd6402b2540) 0 nearly-empty + primary-for std::bad_typeid (0x0x7fd6401aeb60) + +Vtable for std::bad_function_call +std::bad_function_call::_ZTVSt17bad_function_call: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTISt17bad_function_call) +16 (int (*)(...))std::bad_function_call::~bad_function_call +24 (int (*)(...))std::bad_function_call::~bad_function_call +32 (int (*)(...))std::bad_function_call::what + +Class std::bad_function_call + size=8 align=8 + base size=8 base align=8 +std::bad_function_call (0x0x7fd63ffc4d68) 0 nearly-empty + vptr=((& std::bad_function_call::_ZTVSt17bad_function_call) + 16u) + std::exception (0x0x7fd64008d600) 0 nearly-empty + primary-for std::bad_function_call (0x0x7fd63ffc4d68) + +Class std::_Nocopy_types + size=16 align=8 + base size=16 base align=8 +std::_Nocopy_types (0x0x7fd64008d6c0) 0 + +Class std::_Any_data + size=16 align=8 + base size=16 base align=8 +std::_Any_data (0x0x7fd64008d720) 0 + +Class std::_Function_base + size=24 align=8 + base size=24 base align=8 +std::_Function_base (0x0x7fd64008d840) 0 + +Class QMapNodeBase + size=24 align=8 + base size=24 base align=8 +QMapNodeBase (0x0x7fd64008dd20) 0 + +Class QMapDataBase + size=40 align=8 + base size=40 base align=8 +QMapDataBase (0x0x7fd64008dde0) 0 + +Class QHashData::Node + size=16 align=8 + base size=16 base align=8 +QHashData::Node (0x0x7fd6401881e0) 0 + +Class QHashData + size=48 align=8 + base size=44 base align=8 +QHashData (0x0x7fd640188180) 0 + +Class QHashDummyValue + size=1 align=1 + base size=0 base align=1 +QHashDummyValue (0x0x7fd640188240) 0 empty + +Class QVariant::PrivateShared + size=16 align=8 + base size=12 base align=8 +QVariant::PrivateShared (0x0x7fd640188d80) 0 + +Class QVariant::Private::Data + size=8 align=8 + base size=8 base align=8 +QVariant::Private::Data (0x0x7fd640188e40) 0 + +Class QVariant::Private + size=16 align=8 + base size=12 base align=8 +QVariant::Private (0x0x7fd640188de0) 0 + +Class QVariant::Handler + size=72 align=8 + base size=72 base align=8 +QVariant::Handler (0x0x7fd640188ea0) 0 + +Class QVariant + size=16 align=8 + base size=16 base align=8 +QVariant (0x0x7fd640188d20) 0 + +Class QVariantComparisonHelper + size=8 align=8 + base size=8 base align=8 +QVariantComparisonHelper (0x0x7fd63fc211e0) 0 + +Class QSequentialIterable::const_iterator + size=112 align=8 + base size=112 base align=8 +QSequentialIterable::const_iterator (0x0x7fd63fc214e0) 0 + +Class QSequentialIterable + size=104 align=8 + base size=104 base align=8 +QSequentialIterable (0x0x7fd63fc21480) 0 + +Class QAssociativeIterable::const_iterator + size=120 align=8 + base size=120 base align=8 +QAssociativeIterable::const_iterator (0x0x7fd63fc215a0) 0 + +Class QAssociativeIterable + size=112 align=8 + base size=112 base align=8 +QAssociativeIterable (0x0x7fd63fc21540) 0 + +Class QModelIndex + size=24 align=8 + base size=24 base align=8 +QModelIndex (0x0x7fd63fa78000) 0 + +Class QPersistentModelIndex + size=8 align=8 + base size=8 base align=8 +QPersistentModelIndex (0x0x7fd63fa782a0) 0 + +Class QAbstractItemModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractItemModel::QPrivateSignal (0x0x7fd63fb1b2a0) 0 empty + +Vtable for QAbstractItemModel +QAbstractItemModel::_ZTV18QAbstractItemModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractItemModel) +16 (int (*)(...))QAbstractItemModel::metaObject +24 (int (*)(...))QAbstractItemModel::qt_metacast +32 (int (*)(...))QAbstractItemModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractItemModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractItemModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractItemModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractItemModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractItemModel + size=16 align=8 + base size=16 base align=8 +QAbstractItemModel (0x0x7fd63fb0bf08) 0 + vptr=((& QAbstractItemModel::_ZTV18QAbstractItemModel) + 16u) + QObject (0x0x7fd63fb1b240) 0 + primary-for QAbstractItemModel (0x0x7fd63fb0bf08) + +Class QAbstractTableModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTableModel::QPrivateSignal (0x0x7fd63fb1b600) 0 empty + +Vtable for QAbstractTableModel +QAbstractTableModel::_ZTV19QAbstractTableModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTableModel) +16 (int (*)(...))QAbstractTableModel::metaObject +24 (int (*)(...))QAbstractTableModel::qt_metacast +32 (int (*)(...))QAbstractTableModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractTableModel::index +120 (int (*)(...))QAbstractTableModel::parent +128 (int (*)(...))QAbstractTableModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractTableModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractTableModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractTableModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractTableModel + size=16 align=8 + base size=16 base align=8 +QAbstractTableModel (0x0x7fd63fb75138) 0 + vptr=((& QAbstractTableModel::_ZTV19QAbstractTableModel) + 16u) + QAbstractItemModel (0x0x7fd63fb751a0) 0 + primary-for QAbstractTableModel (0x0x7fd63fb75138) + QObject (0x0x7fd63fb1b5a0) 0 + primary-for QAbstractItemModel (0x0x7fd63fb751a0) + +Class QAbstractListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractListModel::QPrivateSignal (0x0x7fd63fb1b6c0) 0 empty + +Vtable for QAbstractListModel +QAbstractListModel::_ZTV18QAbstractListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QAbstractListModel) +16 (int (*)(...))QAbstractListModel::metaObject +24 (int (*)(...))QAbstractListModel::qt_metacast +32 (int (*)(...))QAbstractListModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QAbstractListModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))QAbstractItemModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QAbstractItemModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QAbstractListModel::flags +328 (int (*)(...))QAbstractItemModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QAbstractListModel + size=16 align=8 + base size=16 base align=8 +QAbstractListModel (0x0x7fd63fb75208) 0 + vptr=((& QAbstractListModel::_ZTV18QAbstractListModel) + 16u) + QAbstractItemModel (0x0x7fd63fb75270) 0 + primary-for QAbstractListModel (0x0x7fd63fb75208) + QObject (0x0x7fd63fb1b660) 0 + primary-for QAbstractItemModel (0x0x7fd63fb75270) + +Vtable for QAbstractNativeEventFilter +QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI26QAbstractNativeEventFilter) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QAbstractNativeEventFilter + size=16 align=8 + base size=16 base align=8 +QAbstractNativeEventFilter (0x0x7fd63fb1b960) 0 + vptr=((& QAbstractNativeEventFilter::_ZTV26QAbstractNativeEventFilter) + 16u) + +Class QAbstractProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractProxyModel::QPrivateSignal (0x0x7fd63fb1ba20) 0 empty + +Vtable for QAbstractProxyModel +QAbstractProxyModel::_ZTV19QAbstractProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractProxyModel) +16 (int (*)(...))QAbstractProxyModel::metaObject +24 (int (*)(...))QAbstractProxyModel::qt_metacast +32 (int (*)(...))QAbstractProxyModel::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractProxyModel::sibling +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QAbstractProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QAbstractProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QAbstractItemModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QAbstractItemModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QAbstractProxyModel::setSourceModel +392 (int (*)(...))__cxa_pure_virtual +400 (int (*)(...))__cxa_pure_virtual +408 (int (*)(...))QAbstractProxyModel::mapSelectionToSource +416 (int (*)(...))QAbstractProxyModel::mapSelectionFromSource + +Class QAbstractProxyModel + size=16 align=8 + base size=16 base align=8 +QAbstractProxyModel (0x0x7fd63fb753a8) 0 + vptr=((& QAbstractProxyModel::_ZTV19QAbstractProxyModel) + 16u) + QAbstractItemModel (0x0x7fd63fb75410) 0 + primary-for QAbstractProxyModel (0x0x7fd63fb753a8) + QObject (0x0x7fd63fb1b9c0) 0 + primary-for QAbstractItemModel (0x0x7fd63fb75410) + +Class QAbstractState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractState::QPrivateSignal (0x0x7fd63fb1bae0) 0 empty + +Vtable for QAbstractState +QAbstractState::_ZTV14QAbstractState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QAbstractState) +16 (int (*)(...))QAbstractState::metaObject +24 (int (*)(...))QAbstractState::qt_metacast +32 (int (*)(...))QAbstractState::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractState + size=16 align=8 + base size=16 base align=8 +QAbstractState (0x0x7fd63fb75478) 0 + vptr=((& QAbstractState::_ZTV14QAbstractState) + 16u) + QObject (0x0x7fd63fb1ba80) 0 + primary-for QAbstractState (0x0x7fd63fb75478) + +Class QAbstractTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAbstractTransition::QPrivateSignal (0x0x7fd63fb1bba0) 0 empty + +Vtable for QAbstractTransition +QAbstractTransition::_ZTV19QAbstractTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QAbstractTransition) +16 (int (*)(...))QAbstractTransition::metaObject +24 (int (*)(...))QAbstractTransition::qt_metacast +32 (int (*)(...))QAbstractTransition::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAbstractTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QAbstractTransition + size=16 align=8 + base size=16 base align=8 +QAbstractTransition (0x0x7fd63fb754e0) 0 + vptr=((& QAbstractTransition::_ZTV19QAbstractTransition) + 16u) + QObject (0x0x7fd63fb1bb40) 0 + primary-for QAbstractTransition (0x0x7fd63fb754e0) + +Class QAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QAnimationGroup::QPrivateSignal (0x0x7fd63fb1bc60) 0 empty + +Vtable for QAnimationGroup +QAnimationGroup::_ZTV15QAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QAnimationGroup) +16 (int (*)(...))QAnimationGroup::metaObject +24 (int (*)(...))QAnimationGroup::qt_metacast +32 (int (*)(...))QAnimationGroup::qt_metacall +40 0u +48 0u +56 (int (*)(...))QAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QAnimationGroup + size=16 align=8 + base size=16 base align=8 +QAnimationGroup (0x0x7fd63fb75548) 0 + vptr=((& QAnimationGroup::_ZTV15QAnimationGroup) + 16u) + QAbstractAnimation (0x0x7fd63fb755b0) 0 + primary-for QAnimationGroup (0x0x7fd63fb75548) + QObject (0x0x7fd63fb1bc00) 0 + primary-for QAbstractAnimation (0x0x7fd63fb755b0) + +Class QBasicTimer + size=4 align=4 + base size=4 base align=4 +QBasicTimer (0x0x7fd63f8b3180) 0 + +Class QBitArray + size=8 align=8 + base size=8 base align=8 +QBitArray (0x0x7fd63f8b3420) 0 + +Class QBitRef + size=16 align=8 + base size=12 base align=8 +QBitRef (0x0x7fd63f8b3660) 0 + +Class QIODevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIODevice::QPrivateSignal (0x0x7fd63f8b39c0) 0 empty + +Vtable for QIODevice +QIODevice::_ZTV9QIODevice: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QIODevice) +16 (int (*)(...))QIODevice::metaObject +24 (int (*)(...))QIODevice::qt_metacast +32 (int (*)(...))QIODevice::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QIODevice::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QIODevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))__cxa_pure_virtual +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))__cxa_pure_virtual + +Class QIODevice + size=16 align=8 + base size=16 base align=8 +QIODevice (0x0x7fd63f8af680) 0 + vptr=((& QIODevice::_ZTV9QIODevice) + 16u) + QObject (0x0x7fd63f8b3960) 0 + primary-for QIODevice (0x0x7fd63f8af680) + +Class QBuffer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QBuffer::QPrivateSignal (0x0x7fd63f8b3c00) 0 empty + +Vtable for QBuffer +QBuffer::_ZTV7QBuffer: 30u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QBuffer) +16 (int (*)(...))QBuffer::metaObject +24 (int (*)(...))QBuffer::qt_metacast +32 (int (*)(...))QBuffer::qt_metacall +40 (int (*)(...))QBuffer::~QBuffer +48 (int (*)(...))QBuffer::~QBuffer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QBuffer::connectNotify +104 (int (*)(...))QBuffer::disconnectNotify +112 (int (*)(...))QIODevice::isSequential +120 (int (*)(...))QBuffer::open +128 (int (*)(...))QBuffer::close +136 (int (*)(...))QBuffer::pos +144 (int (*)(...))QBuffer::size +152 (int (*)(...))QBuffer::seek +160 (int (*)(...))QBuffer::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QBuffer::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QBuffer::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QBuffer::writeData + +Class QBuffer + size=16 align=8 + base size=16 base align=8 +QBuffer (0x0x7fd63f8af7b8) 0 + vptr=((& QBuffer::_ZTV7QBuffer) + 16u) + QIODevice (0x0x7fd63f8af820) 0 + primary-for QBuffer (0x0x7fd63f8af7b8) + QObject (0x0x7fd63f8b3ba0) 0 + primary-for QIODevice (0x0x7fd63f8af820) + +Class QByteArrayMatcher::Data + size=272 align=8 + base size=272 base align=8 +QByteArrayMatcher::Data (0x0x7fd63f8b3cc0) 0 + +Class QByteArrayMatcher + size=1040 align=8 + base size=1040 base align=8 +QByteArrayMatcher (0x0x7fd63f8b3c60) 0 + +Class QStaticByteArrayMatcherBase::Skiptable + size=256 align=1 + base size=256 base align=1 +QStaticByteArrayMatcherBase::Skiptable (0x0x7fd63f8b3de0) 0 + +Class QStaticByteArrayMatcherBase + size=256 align=16 + base size=256 base align=16 +QStaticByteArrayMatcherBase (0x0x7fd63f8b3d80) 0 + +Class QSharedData + size=4 align=4 + base size=4 base align=4 +QSharedData (0x0x7fd63f5d4000) 0 + +Class QLocale + size=8 align=8 + base size=8 base align=8 +QLocale (0x0x7fd63f5d41e0) 0 + +Class QCollatorSortKey + size=8 align=8 + base size=8 base align=8 +QCollatorSortKey (0x0x7fd63f5d4660) 0 + +Class QCollator + size=8 align=8 + base size=8 base align=8 +QCollator (0x0x7fd63f5d4720) 0 + +Class QCommandLineOption + size=8 align=8 + base size=8 base align=8 +QCommandLineOption (0x0x7fd63f75c720) 0 + +Vtable for QEvent +QEvent::_ZTV6QEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QEvent) +16 (int (*)(...))QEvent::~QEvent +24 (int (*)(...))QEvent::~QEvent + +Class QEvent + size=24 align=8 + base size=20 base align=8 +QEvent (0x0x7fd63f75cba0) 0 + vptr=((& QEvent::_ZTV6QEvent) + 16u) + +Vtable for QTimerEvent +QTimerEvent::_ZTV11QTimerEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTimerEvent) +16 (int (*)(...))QTimerEvent::~QTimerEvent +24 (int (*)(...))QTimerEvent::~QTimerEvent + +Class QTimerEvent + size=24 align=8 + base size=24 base align=8 +QTimerEvent (0x0x7fd63f759bc8) 0 + vptr=((& QTimerEvent::_ZTV11QTimerEvent) + 16u) + QEvent (0x0x7fd63f75cc00) 0 + primary-for QTimerEvent (0x0x7fd63f759bc8) + +Vtable for QChildEvent +QChildEvent::_ZTV11QChildEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QChildEvent) +16 (int (*)(...))QChildEvent::~QChildEvent +24 (int (*)(...))QChildEvent::~QChildEvent + +Class QChildEvent + size=32 align=8 + base size=32 base align=8 +QChildEvent (0x0x7fd63f759c30) 0 + vptr=((& QChildEvent::_ZTV11QChildEvent) + 16u) + QEvent (0x0x7fd63f75cc60) 0 + primary-for QChildEvent (0x0x7fd63f759c30) + +Vtable for QDynamicPropertyChangeEvent +QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI27QDynamicPropertyChangeEvent) +16 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent +24 (int (*)(...))QDynamicPropertyChangeEvent::~QDynamicPropertyChangeEvent + +Class QDynamicPropertyChangeEvent + size=32 align=8 + base size=32 base align=8 +QDynamicPropertyChangeEvent (0x0x7fd63f759c98) 0 + vptr=((& QDynamicPropertyChangeEvent::_ZTV27QDynamicPropertyChangeEvent) + 16u) + QEvent (0x0x7fd63f75ccc0) 0 + primary-for QDynamicPropertyChangeEvent (0x0x7fd63f759c98) + +Vtable for QDeferredDeleteEvent +QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QDeferredDeleteEvent) +16 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent +24 (int (*)(...))QDeferredDeleteEvent::~QDeferredDeleteEvent + +Class QDeferredDeleteEvent + size=24 align=8 + base size=24 base align=8 +QDeferredDeleteEvent (0x0x7fd63f759d00) 0 + vptr=((& QDeferredDeleteEvent::_ZTV20QDeferredDeleteEvent) + 16u) + QEvent (0x0x7fd63f75cd20) 0 + primary-for QDeferredDeleteEvent (0x0x7fd63f759d00) + +Class QCoreApplication::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QCoreApplication::QPrivateSignal (0x0x7fd63f75cde0) 0 empty + +Vtable for QCoreApplication +QCoreApplication::_ZTV16QCoreApplication: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QCoreApplication) +16 (int (*)(...))QCoreApplication::metaObject +24 (int (*)(...))QCoreApplication::qt_metacast +32 (int (*)(...))QCoreApplication::qt_metacall +40 (int (*)(...))QCoreApplication::~QCoreApplication +48 (int (*)(...))QCoreApplication::~QCoreApplication +56 (int (*)(...))QCoreApplication::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QCoreApplication::notify +120 (int (*)(...))QCoreApplication::compressEvent + +Class QCoreApplication + size=16 align=8 + base size=16 base align=8 +QCoreApplication (0x0x7fd63f759d68) 0 + vptr=((& QCoreApplication::_ZTV16QCoreApplication) + 16u) + QObject (0x0x7fd63f75cd80) 0 + primary-for QCoreApplication (0x0x7fd63f759d68) + +Class QCommandLineParser + size=8 align=8 + base size=8 base align=8 +QCommandLineParser (0x0x7fd63f75ce40) 0 + +Class QContiguousCacheData + size=24 align=4 + base size=24 base align=4 +QContiguousCacheData (0x0x7fd63f75cea0) 0 + +Class QCryptographicHash + size=8 align=8 + base size=8 base align=8 +QCryptographicHash (0x0x7fd63f3e0180) 0 + +Class QDataStream + size=32 align=8 + base size=32 base align=8 +QDataStream (0x0x7fd63f3e01e0) 0 + +Class QtPrivate::StreamStateSaver + size=16 align=8 + base size=12 base align=8 +QtPrivate::StreamStateSaver (0x0x7fd63f3e02a0) 0 + +Class QDate + size=8 align=8 + base size=8 base align=8 +QDate (0x0x7fd63f3e0300) 0 + +Class QTime + size=4 align=4 + base size=4 base align=4 +QTime (0x0x7fd63f3e05a0) 0 + +Class QDateTime::ShortData + size=8 align=8 + base size=8 base align=8 +QDateTime::ShortData (0x0x7fd63f3e08a0) 0 + +Class QDateTime::Data + size=8 align=8 + base size=8 base align=8 +QDateTime::Data (0x0x7fd63f3e0900) 0 + +Class QDateTime + size=8 align=8 + base size=8 base align=8 +QDateTime (0x0x7fd63f3e0840) 0 + +Class QElapsedTimer + size=16 align=8 + base size=16 base align=8 +QElapsedTimer (0x0x7fd63f56c6c0) 0 + +Class QDeadlineTimer + size=16 align=8 + base size=16 base align=8 +QDeadlineTimer (0x0x7fd63f56c720) 0 + +Vtable for QTextStream +QTextStream::_ZTV11QTextStream: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTextStream) +16 (int (*)(...))QTextStream::~QTextStream +24 (int (*)(...))QTextStream::~QTextStream + +Class QTextStream + size=16 align=8 + base size=16 base align=8 +QTextStream (0x0x7fd63f2aa360) 0 + vptr=((& QTextStream::_ZTV11QTextStream) + 16u) + +Class QTextStreamManipulator + size=40 align=8 + base size=38 base align=8 +QTextStreamManipulator (0x0x7fd63f2aa600) 0 + +Class QtSharedPointer::NormalDeleter + size=1 align=1 + base size=0 base align=1 +QtSharedPointer::NormalDeleter (0x0x7fd63f2aa840) 0 empty + +Class QtSharedPointer::ExternalRefCountData + size=16 align=8 + base size=16 base align=8 +QtSharedPointer::ExternalRefCountData (0x0x7fd63f2aa9c0) 0 + +Class QDebug::Stream + size=80 align=8 + base size=76 base align=8 +QDebug::Stream (0x0x7fd63f2aae40) 0 + +Class QDebug + size=8 align=8 + base size=8 base align=8 +QDebug (0x0x7fd63f2aade0) 0 + +Class QDebugStateSaver + size=8 align=8 + base size=8 base align=8 +QDebugStateSaver (0x0x7fd63f14f060) 0 + +Class QNoDebug + size=1 align=1 + base size=0 base align=1 +QNoDebug (0x0x7fd63f14f120) 0 empty + +Class QFileDevice::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileDevice::QPrivateSignal (0x0x7fd63f14f1e0) 0 empty + +Vtable for QFileDevice +QFileDevice::_ZTV11QFileDevice: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFileDevice) +16 (int (*)(...))QFileDevice::metaObject +24 (int (*)(...))QFileDevice::qt_metacast +32 (int (*)(...))QFileDevice::qt_metacall +40 (int (*)(...))QFileDevice::~QFileDevice +48 (int (*)(...))QFileDevice::~QFileDevice +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QIODevice::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFileDevice::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QFileDevice + size=16 align=8 + base size=16 base align=8 +QFileDevice (0x0x7fd63f10dd00) 0 + vptr=((& QFileDevice::_ZTV11QFileDevice) + 16u) + QIODevice (0x0x7fd63f10dd68) 0 + primary-for QFileDevice (0x0x7fd63f10dd00) + QObject (0x0x7fd63f14f180) 0 + primary-for QIODevice (0x0x7fd63f10dd68) + +Class QFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFile::QPrivateSignal (0x0x7fd63f14f420) 0 empty + +Vtable for QFile +QFile::_ZTV5QFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI5QFile) +16 (int (*)(...))QFile::metaObject +24 (int (*)(...))QFile::qt_metacast +32 (int (*)(...))QFile::qt_metacall +40 (int (*)(...))QFile::~QFile +48 (int (*)(...))QFile::~QFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QFile + size=16 align=8 + base size=16 base align=8 +QFile (0x0x7fd63f10dea0) 0 + vptr=((& QFile::_ZTV5QFile) + 16u) + QFileDevice (0x0x7fd63f10df08) 0 + primary-for QFile (0x0x7fd63f10dea0) + QIODevice (0x0x7fd63f10df70) 0 + primary-for QFileDevice (0x0x7fd63f10df08) + QObject (0x0x7fd63f14f3c0) 0 + primary-for QIODevice (0x0x7fd63f10df70) + +Class QFileInfo + size=8 align=8 + base size=8 base align=8 +QFileInfo (0x0x7fd63f14f600) 0 + +Class QDir + size=8 align=8 + base size=8 base align=8 +QDir (0x0x7fd63f14fa20) 0 + +Class QDirIterator + size=8 align=8 + base size=8 base align=8 +QDirIterator (0x0x7fd63ee83420) 0 + +Class QEasingCurve + size=8 align=8 + base size=8 base align=8 +QEasingCurve (0x0x7fd63ee83660) 0 + +Class QEventTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QEventTransition::QPrivateSignal (0x0x7fd63eb9c8a0) 0 empty + +Vtable for QEventTransition +QEventTransition::_ZTV16QEventTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QEventTransition) +16 (int (*)(...))QEventTransition::metaObject +24 (int (*)(...))QEventTransition::qt_metacast +32 (int (*)(...))QEventTransition::qt_metacall +40 (int (*)(...))QEventTransition::~QEventTransition +48 (int (*)(...))QEventTransition::~QEventTransition +56 (int (*)(...))QEventTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QEventTransition::eventTest +120 (int (*)(...))QEventTransition::onTransition + +Class QEventTransition + size=16 align=8 + base size=16 base align=8 +QEventTransition (0x0x7fd63eba5270) 0 + vptr=((& QEventTransition::_ZTV16QEventTransition) + 16u) + QAbstractTransition (0x0x7fd63eba52d8) 0 + primary-for QEventTransition (0x0x7fd63eba5270) + QObject (0x0x7fd63eb9c840) 0 + primary-for QAbstractTransition (0x0x7fd63eba52d8) + +Vtable for QException +QException::_ZTV10QException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QException) +16 (int (*)(...))QException::~QException +24 (int (*)(...))QException::~QException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QException::raise +48 (int (*)(...))QException::clone + +Class QException + size=8 align=8 + base size=8 base align=8 +QException (0x0x7fd63eba5340) 0 nearly-empty + vptr=((& QException::_ZTV10QException) + 16u) + std::exception (0x0x7fd63eb9c900) 0 nearly-empty + primary-for QException (0x0x7fd63eba5340) + +Vtable for QUnhandledException +QUnhandledException::_ZTV19QUnhandledException: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QUnhandledException) +16 (int (*)(...))QUnhandledException::~QUnhandledException +24 (int (*)(...))QUnhandledException::~QUnhandledException +32 (int (*)(...))std::exception::what +40 (int (*)(...))QUnhandledException::raise +48 (int (*)(...))QUnhandledException::clone + +Class QUnhandledException + size=8 align=8 + base size=8 base align=8 +QUnhandledException (0x0x7fd63eba53a8) 0 nearly-empty + vptr=((& QUnhandledException::_ZTV19QUnhandledException) + 16u) + QException (0x0x7fd63eba5410) 0 nearly-empty + primary-for QUnhandledException (0x0x7fd63eba53a8) + std::exception (0x0x7fd63eb9c960) 0 nearly-empty + primary-for QException (0x0x7fd63eba5410) + +Class QtPrivate::ExceptionHolder + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionHolder (0x0x7fd63eb9c9c0) 0 + +Class QtPrivate::ExceptionStore + size=8 align=8 + base size=8 base align=8 +QtPrivate::ExceptionStore (0x0x7fd63eb9ca80) 0 + +Vtable for QFactoryInterface +QFactoryInterface::_ZTV17QFactoryInterface: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QFactoryInterface) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual + +Class QFactoryInterface + size=8 align=8 + base size=8 base align=8 +QFactoryInterface (0x0x7fd63eb9cae0) 0 nearly-empty + vptr=((& QFactoryInterface::_ZTV17QFactoryInterface) + 16u) + +Class QFileSelector::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSelector::QPrivateSignal (0x0x7fd63eb9cc00) 0 empty + +Vtable for QFileSelector +QFileSelector::_ZTV13QFileSelector: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QFileSelector) +16 (int (*)(...))QFileSelector::metaObject +24 (int (*)(...))QFileSelector::qt_metacast +32 (int (*)(...))QFileSelector::qt_metacall +40 (int (*)(...))QFileSelector::~QFileSelector +48 (int (*)(...))QFileSelector::~QFileSelector +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSelector + size=16 align=8 + base size=16 base align=8 +QFileSelector (0x0x7fd63eba5478) 0 + vptr=((& QFileSelector::_ZTV13QFileSelector) + 16u) + QObject (0x0x7fd63eb9cba0) 0 + primary-for QFileSelector (0x0x7fd63eba5478) + +Class QFileSystemWatcher::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFileSystemWatcher::QPrivateSignal (0x0x7fd63eb9ccc0) 0 empty + +Vtable for QFileSystemWatcher +QFileSystemWatcher::_ZTV18QFileSystemWatcher: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFileSystemWatcher) +16 (int (*)(...))QFileSystemWatcher::metaObject +24 (int (*)(...))QFileSystemWatcher::qt_metacast +32 (int (*)(...))QFileSystemWatcher::qt_metacall +40 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +48 (int (*)(...))QFileSystemWatcher::~QFileSystemWatcher +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QFileSystemWatcher + size=16 align=8 + base size=16 base align=8 +QFileSystemWatcher (0x0x7fd63eba54e0) 0 + vptr=((& QFileSystemWatcher::_ZTV18QFileSystemWatcher) + 16u) + QObject (0x0x7fd63eb9cc60) 0 + primary-for QFileSystemWatcher (0x0x7fd63eba54e0) + +Class QFinalState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFinalState::QPrivateSignal (0x0x7fd63eb9cd80) 0 empty + +Vtable for QFinalState +QFinalState::_ZTV11QFinalState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QFinalState) +16 (int (*)(...))QFinalState::metaObject +24 (int (*)(...))QFinalState::qt_metacast +32 (int (*)(...))QFinalState::qt_metacall +40 (int (*)(...))QFinalState::~QFinalState +48 (int (*)(...))QFinalState::~QFinalState +56 (int (*)(...))QFinalState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFinalState::onEntry +120 (int (*)(...))QFinalState::onExit + +Class QFinalState + size=16 align=8 + base size=16 base align=8 +QFinalState (0x0x7fd63eba5548) 0 + vptr=((& QFinalState::_ZTV11QFinalState) + 16u) + QAbstractState (0x0x7fd63eba55b0) 0 + primary-for QFinalState (0x0x7fd63eba5548) + QObject (0x0x7fd63eb9cd20) 0 + primary-for QAbstractState (0x0x7fd63eba55b0) + +Vtable for QRunnable +QRunnable::_ZTV9QRunnable: 5u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QRunnable) +16 (int (*)(...))__cxa_pure_virtual +24 0u +32 0u + +Class QRunnable + size=16 align=8 + base size=12 base align=8 +QRunnable (0x0x7fd63eb9cde0) 0 + vptr=((& QRunnable::_ZTV9QRunnable) + 16u) + +Class QBasicMutex + size=8 align=8 + base size=8 base align=8 +QBasicMutex (0x0x7fd63eb9ce40) 0 + +Class QMutex + size=8 align=8 + base size=8 base align=8 +QMutex (0x0x7fd63eba56e8) 0 + QBasicMutex (0x0x7fd63ec6f0c0) 0 + +Class QMutexLocker + size=8 align=8 + base size=8 base align=8 +QMutexLocker (0x0x7fd63ec6f180) 0 + +Class QtPrivate::ResultItem + size=16 align=8 + base size=16 base align=8 +QtPrivate::ResultItem (0x0x7fd63ec6f240) 0 + +Class QtPrivate::ResultIteratorBase + size=16 align=8 + base size=12 base align=8 +QtPrivate::ResultIteratorBase (0x0x7fd63ec6f2a0) 0 + +Vtable for QtPrivate::ResultStoreBase +QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN9QtPrivate15ResultStoreBaseE) +16 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase +24 (int (*)(...))QtPrivate::ResultStoreBase::~ResultStoreBase + +Class QtPrivate::ResultStoreBase + size=48 align=8 + base size=44 base align=8 +QtPrivate::ResultStoreBase (0x0x7fd63ec6f3c0) 0 + vptr=((& QtPrivate::ResultStoreBase::_ZTVN9QtPrivate15ResultStoreBaseE) + 16u) + +Vtable for QFutureInterfaceBase +QFutureInterfaceBase::_ZTV20QFutureInterfaceBase: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI20QFutureInterfaceBase) +16 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase +24 (int (*)(...))QFutureInterfaceBase::~QFutureInterfaceBase + +Class QFutureInterfaceBase + size=16 align=8 + base size=16 base align=8 +QFutureInterfaceBase (0x0x7fd63ec6f420) 0 + vptr=((& QFutureInterfaceBase::_ZTV20QFutureInterfaceBase) + 16u) + +Class QFutureWatcherBase::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QFutureWatcherBase::QPrivateSignal (0x0x7fd63ec6f780) 0 empty + +Vtable for QFutureWatcherBase +QFutureWatcherBase::_ZTV18QFutureWatcherBase: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QFutureWatcherBase) +16 (int (*)(...))QFutureWatcherBase::metaObject +24 (int (*)(...))QFutureWatcherBase::qt_metacast +32 (int (*)(...))QFutureWatcherBase::qt_metacall +40 0u +48 0u +56 (int (*)(...))QFutureWatcherBase::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QFutureWatcherBase::connectNotify +104 (int (*)(...))QFutureWatcherBase::disconnectNotify +112 (int (*)(...))__cxa_pure_virtual +120 (int (*)(...))__cxa_pure_virtual + +Class QFutureWatcherBase + size=16 align=8 + base size=16 base align=8 +QFutureWatcherBase (0x0x7fd63eba5af8) 0 + vptr=((& QFutureWatcherBase::_ZTV18QFutureWatcherBase) + 16u) + QObject (0x0x7fd63ec6f720) 0 + primary-for QFutureWatcherBase (0x0x7fd63eba5af8) + +Class QHistoryState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QHistoryState::QPrivateSignal (0x0x7fd63ec6f900) 0 empty + +Vtable for QHistoryState +QHistoryState::_ZTV13QHistoryState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QHistoryState) +16 (int (*)(...))QHistoryState::metaObject +24 (int (*)(...))QHistoryState::qt_metacast +32 (int (*)(...))QHistoryState::qt_metacall +40 (int (*)(...))QHistoryState::~QHistoryState +48 (int (*)(...))QHistoryState::~QHistoryState +56 (int (*)(...))QHistoryState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QHistoryState::onEntry +120 (int (*)(...))QHistoryState::onExit + +Class QHistoryState + size=16 align=8 + base size=16 base align=8 +QHistoryState (0x0x7fd63eba5f08) 0 + vptr=((& QHistoryState::_ZTV13QHistoryState) + 16u) + QAbstractState (0x0x7fd63eba5f70) 0 + primary-for QHistoryState (0x0x7fd63eba5f08) + QObject (0x0x7fd63ec6f8a0) 0 + primary-for QAbstractState (0x0x7fd63eba5f70) + +Class QIdentityProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QIdentityProxyModel::QPrivateSignal (0x0x7fd63ec6f9c0) 0 empty + +Vtable for QIdentityProxyModel +QIdentityProxyModel::_ZTV19QIdentityProxyModel: 53u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QIdentityProxyModel) +16 (int (*)(...))QIdentityProxyModel::metaObject +24 (int (*)(...))QIdentityProxyModel::qt_metacast +32 (int (*)(...))QIdentityProxyModel::qt_metacall +40 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +48 (int (*)(...))QIdentityProxyModel::~QIdentityProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QIdentityProxyModel::index +120 (int (*)(...))QIdentityProxyModel::parent +128 (int (*)(...))QIdentityProxyModel::sibling +136 (int (*)(...))QIdentityProxyModel::rowCount +144 (int (*)(...))QIdentityProxyModel::columnCount +152 (int (*)(...))QAbstractProxyModel::hasChildren +160 (int (*)(...))QAbstractProxyModel::data +168 (int (*)(...))QAbstractProxyModel::setData +176 (int (*)(...))QIdentityProxyModel::headerData +184 (int (*)(...))QAbstractProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QAbstractProxyModel::mimeTypes +216 (int (*)(...))QAbstractProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QIdentityProxyModel::dropMimeData +240 (int (*)(...))QAbstractProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QIdentityProxyModel::insertRows +264 (int (*)(...))QIdentityProxyModel::insertColumns +272 (int (*)(...))QIdentityProxyModel::removeRows +280 (int (*)(...))QIdentityProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractProxyModel::fetchMore +312 (int (*)(...))QAbstractProxyModel::canFetchMore +320 (int (*)(...))QAbstractProxyModel::flags +328 (int (*)(...))QAbstractProxyModel::sort +336 (int (*)(...))QAbstractProxyModel::buddy +344 (int (*)(...))QIdentityProxyModel::match +352 (int (*)(...))QAbstractProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QIdentityProxyModel::setSourceModel +392 (int (*)(...))QIdentityProxyModel::mapToSource +400 (int (*)(...))QIdentityProxyModel::mapFromSource +408 (int (*)(...))QIdentityProxyModel::mapSelectionToSource +416 (int (*)(...))QIdentityProxyModel::mapSelectionFromSource + +Class QIdentityProxyModel + size=16 align=8 + base size=16 base align=8 +QIdentityProxyModel (0x0x7fd63eba5750) 0 + vptr=((& QIdentityProxyModel::_ZTV19QIdentityProxyModel) + 16u) + QAbstractProxyModel (0x0x7fd63e9e8000) 0 + primary-for QIdentityProxyModel (0x0x7fd63eba5750) + QAbstractItemModel (0x0x7fd63e9e8068) 0 + primary-for QAbstractProxyModel (0x0x7fd63e9e8000) + QObject (0x0x7fd63ec6f960) 0 + primary-for QAbstractItemModel (0x0x7fd63e9e8068) + +Class QItemSelectionRange + size=16 align=8 + base size=16 base align=8 +QItemSelectionRange (0x0x7fd63ec6fa20) 0 + +Class QItemSelectionModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QItemSelectionModel::QPrivateSignal (0x0x7fd63ec6fd80) 0 empty + +Vtable for QItemSelectionModel +QItemSelectionModel::_ZTV19QItemSelectionModel: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI19QItemSelectionModel) +16 (int (*)(...))QItemSelectionModel::metaObject +24 (int (*)(...))QItemSelectionModel::qt_metacast +32 (int (*)(...))QItemSelectionModel::qt_metacall +40 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +48 (int (*)(...))QItemSelectionModel::~QItemSelectionModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QItemSelectionModel::setCurrentIndex +120 (int (*)(...))QItemSelectionModel::select +128 (int (*)(...))QItemSelectionModel::select +136 (int (*)(...))QItemSelectionModel::clear +144 (int (*)(...))QItemSelectionModel::reset +152 (int (*)(...))QItemSelectionModel::clearCurrentIndex + +Class QItemSelectionModel + size=16 align=8 + base size=16 base align=8 +QItemSelectionModel (0x0x7fd63e9e8410) 0 + vptr=((& QItemSelectionModel::_ZTV19QItemSelectionModel) + 16u) + QObject (0x0x7fd63ec6fd20) 0 + primary-for QItemSelectionModel (0x0x7fd63e9e8410) + +Class QItemSelection + size=8 align=8 + base size=8 base align=8 +QItemSelection (0x0x7fd63e9e8618) 0 + QList (0x0x7fd63e9e8680) 0 + QListSpecialMethods (0x0x7fd63ea860c0) 0 empty + +Class QJsonValue + size=24 align=8 + base size=20 base align=8 +QJsonValue (0x0x7fd63ea865a0) 0 + +Class QJsonValueRef + size=16 align=8 + base size=12 base align=8 +QJsonValueRef (0x0x7fd63ea86660) 0 + +Class QJsonValuePtr + size=24 align=8 + base size=24 base align=8 +QJsonValuePtr (0x0x7fd63ea86720) 0 + +Class QJsonValueRefPtr + size=16 align=8 + base size=16 base align=8 +QJsonValueRefPtr (0x0x7fd63ea86780) 0 + +Class QJsonArray::iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::iterator (0x0x7fd63ea86840) 0 + +Class QJsonArray::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonArray::const_iterator (0x0x7fd63ea868a0) 0 + +Class QJsonArray + size=16 align=8 + base size=16 base align=8 +QJsonArray (0x0x7fd63ea867e0) 0 + +Class QJsonParseError + size=8 align=4 + base size=8 base align=4 +QJsonParseError (0x0x7fd63ea86960) 0 + +Class QJsonDocument + size=8 align=8 + base size=8 base align=8 +QJsonDocument (0x0x7fd63ea869c0) 0 + +Class QJsonObject::iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::iterator (0x0x7fd63ea86a80) 0 + +Class QJsonObject::const_iterator + size=16 align=8 + base size=12 base align=8 +QJsonObject::const_iterator (0x0x7fd63ea86ae0) 0 + +Class QJsonObject + size=16 align=8 + base size=16 base align=8 +QJsonObject (0x0x7fd63ea86a20) 0 + +Class QLibrary::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QLibrary::QPrivateSignal (0x0x7fd63ea86cc0) 0 empty + +Vtable for QLibrary +QLibrary::_ZTV8QLibrary: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QLibrary) +16 (int (*)(...))QLibrary::metaObject +24 (int (*)(...))QLibrary::qt_metacast +32 (int (*)(...))QLibrary::qt_metacall +40 (int (*)(...))QLibrary::~QLibrary +48 (int (*)(...))QLibrary::~QLibrary +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QLibrary + size=32 align=8 + base size=25 base align=8 +QLibrary (0x0x7fd63e9e8a28) 0 + vptr=((& QLibrary::_ZTV8QLibrary) + 16u) + QObject (0x0x7fd63ea86c60) 0 + primary-for QLibrary (0x0x7fd63e9e8a28) + +Class QVersionNumber::SegmentStorage + size=8 align=8 + base size=8 base align=8 +QVersionNumber::SegmentStorage (0x0x7fd63ea86f00) 0 + +Class QVersionNumber + size=8 align=8 + base size=8 base align=8 +QVersionNumber (0x0x7fd63ea86ea0) 0 + +Class QLibraryInfo + size=1 align=1 + base size=0 base align=1 +QLibraryInfo (0x0x7fd63e8dfc60) 0 empty + +Class QPoint + size=8 align=4 + base size=8 base align=4 +QPoint (0x0x7fd63e8dfcc0) 0 + +Class QPointF + size=16 align=8 + base size=16 base align=8 +QPointF (0x0x7fd63e8dff60) 0 + +Class QLine + size=16 align=4 + base size=16 base align=4 +QLine (0x0x7fd63e57d240) 0 + +Class QLineF + size=32 align=8 + base size=32 base align=8 +QLineF (0x0x7fd63e57d4e0) 0 + +Class QLinkedListData + size=32 align=8 + base size=25 base align=8 +QLinkedListData (0x0x7fd63e57d780) 0 + +Class QLockFile + size=8 align=8 + base size=8 base align=8 +QLockFile (0x0x7fd63e57df00) 0 + +Class QLoggingCategory::AtomicBools + size=4 align=1 + base size=4 base align=1 +QLoggingCategory::AtomicBools (0x0x7fd63e6ce0c0) 0 + +Class QLoggingCategory + size=24 align=8 + base size=24 base align=8 +QLoggingCategory (0x0x7fd63e6ce060) 0 + +Class QMargins + size=16 align=4 + base size=16 base align=4 +QMargins (0x0x7fd63e6ce240) 0 + +Class QMarginsF + size=32 align=8 + base size=32 base align=8 +QMarginsF (0x0x7fd63e6ce4e0) 0 + +Class QMessageAuthenticationCode + size=8 align=8 + base size=8 base align=8 +QMessageAuthenticationCode (0x0x7fd63e6ce7e0) 0 + +Class QMetaMethod + size=16 align=8 + base size=12 base align=8 +QMetaMethod (0x0x7fd63e6ce840) 0 + +Class QMetaEnum + size=16 align=8 + base size=12 base align=8 +QMetaEnum (0x0x7fd63e6ceae0) 0 + +Class QMetaProperty + size=32 align=8 + base size=32 base align=8 +QMetaProperty (0x0x7fd63e6cede0) 0 + +Class QMetaClassInfo + size=16 align=8 + base size=12 base align=8 +QMetaClassInfo (0x0x7fd63e6cee40) 0 + +Class QMimeData::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QMimeData::QPrivateSignal (0x0x7fd63e414180) 0 empty + +Vtable for QMimeData +QMimeData::_ZTV9QMimeData: 17u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QMimeData) +16 (int (*)(...))QMimeData::metaObject +24 (int (*)(...))QMimeData::qt_metacast +32 (int (*)(...))QMimeData::qt_metacall +40 (int (*)(...))QMimeData::~QMimeData +48 (int (*)(...))QMimeData::~QMimeData +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QMimeData::hasFormat +120 (int (*)(...))QMimeData::formats +128 (int (*)(...))QMimeData::retrieveData + +Class QMimeData + size=16 align=8 + base size=16 base align=8 +QMimeData (0x0x7fd63e5b4b60) 0 + vptr=((& QMimeData::_ZTV9QMimeData) + 16u) + QObject (0x0x7fd63e414120) 0 + primary-for QMimeData (0x0x7fd63e5b4b60) + +Class QMimeType + size=8 align=8 + base size=8 base align=8 +QMimeType (0x0x7fd63e4141e0) 0 + +Class QMimeDatabase + size=8 align=8 + base size=8 base align=8 +QMimeDatabase (0x0x7fd63e4144e0) 0 + +Class QObjectCleanupHandler::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QObjectCleanupHandler::QPrivateSignal (0x0x7fd63e4145a0) 0 empty + +Vtable for QObjectCleanupHandler +QObjectCleanupHandler::_ZTV21QObjectCleanupHandler: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QObjectCleanupHandler) +16 (int (*)(...))QObjectCleanupHandler::metaObject +24 (int (*)(...))QObjectCleanupHandler::qt_metacast +32 (int (*)(...))QObjectCleanupHandler::qt_metacall +40 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +48 (int (*)(...))QObjectCleanupHandler::~QObjectCleanupHandler +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QObjectCleanupHandler + size=24 align=8 + base size=24 base align=8 +QObjectCleanupHandler (0x0x7fd63e5b4d68) 0 + vptr=((& QObjectCleanupHandler::_ZTV21QObjectCleanupHandler) + 16u) + QObject (0x0x7fd63e414540) 0 + primary-for QObjectCleanupHandler (0x0x7fd63e5b4d68) + +Class QOperatingSystemVersion + size=16 align=4 + base size=16 base align=4 +QOperatingSystemVersion (0x0x7fd63e414600) 0 + +Class QParallelAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QParallelAnimationGroup::QPrivateSignal (0x0x7fd63e4146c0) 0 empty + +Vtable for QParallelAnimationGroup +QParallelAnimationGroup::_ZTV23QParallelAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QParallelAnimationGroup) +16 (int (*)(...))QParallelAnimationGroup::metaObject +24 (int (*)(...))QParallelAnimationGroup::qt_metacast +32 (int (*)(...))QParallelAnimationGroup::qt_metacall +40 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +48 (int (*)(...))QParallelAnimationGroup::~QParallelAnimationGroup +56 (int (*)(...))QParallelAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QParallelAnimationGroup::duration +120 (int (*)(...))QParallelAnimationGroup::updateCurrentTime +128 (int (*)(...))QParallelAnimationGroup::updateState +136 (int (*)(...))QParallelAnimationGroup::updateDirection + +Class QParallelAnimationGroup + size=16 align=8 + base size=16 base align=8 +QParallelAnimationGroup (0x0x7fd63e5b4e38) 0 + vptr=((& QParallelAnimationGroup::_ZTV23QParallelAnimationGroup) + 16u) + QAnimationGroup (0x0x7fd63e5b4ea0) 0 + primary-for QParallelAnimationGroup (0x0x7fd63e5b4e38) + QAbstractAnimation (0x0x7fd63e5b4f08) 0 + primary-for QAnimationGroup (0x0x7fd63e5b4ea0) + QObject (0x0x7fd63e414660) 0 + primary-for QAbstractAnimation (0x0x7fd63e5b4f08) + +Class QPauseAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPauseAnimation::QPrivateSignal (0x0x7fd63e414780) 0 empty + +Vtable for QPauseAnimation +QPauseAnimation::_ZTV15QPauseAnimation: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QPauseAnimation) +16 (int (*)(...))QPauseAnimation::metaObject +24 (int (*)(...))QPauseAnimation::qt_metacast +32 (int (*)(...))QPauseAnimation::qt_metacall +40 (int (*)(...))QPauseAnimation::~QPauseAnimation +48 (int (*)(...))QPauseAnimation::~QPauseAnimation +56 (int (*)(...))QPauseAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QPauseAnimation::duration +120 (int (*)(...))QPauseAnimation::updateCurrentTime +128 (int (*)(...))QAbstractAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection + +Class QPauseAnimation + size=16 align=8 + base size=16 base align=8 +QPauseAnimation (0x0x7fd63e5b4f70) 0 + vptr=((& QPauseAnimation::_ZTV15QPauseAnimation) + 16u) + QAbstractAnimation (0x0x7fd63e5b4dd0) 0 + primary-for QPauseAnimation (0x0x7fd63e5b4f70) + QObject (0x0x7fd63e414720) 0 + primary-for QAbstractAnimation (0x0x7fd63e5b4dd0) + +Class QStaticPlugin + size=16 align=8 + base size=16 base align=8 +QStaticPlugin (0x0x7fd63e414960) 0 + +Class QPluginLoader::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPluginLoader::QPrivateSignal (0x0x7fd63e414c60) 0 empty + +Vtable for QPluginLoader +QPluginLoader::_ZTV13QPluginLoader: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QPluginLoader) +16 (int (*)(...))QPluginLoader::metaObject +24 (int (*)(...))QPluginLoader::qt_metacast +32 (int (*)(...))QPluginLoader::qt_metacall +40 (int (*)(...))QPluginLoader::~QPluginLoader +48 (int (*)(...))QPluginLoader::~QPluginLoader +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QPluginLoader + size=32 align=8 + base size=25 base align=8 +QPluginLoader (0x0x7fd63e4b31a0) 0 + vptr=((& QPluginLoader::_ZTV13QPluginLoader) + 16u) + QObject (0x0x7fd63e414c00) 0 + primary-for QPluginLoader (0x0x7fd63e4b31a0) + +Class QProcessEnvironment + size=8 align=8 + base size=8 base align=8 +QProcessEnvironment (0x0x7fd63e414cc0) 0 + +Class QProcess::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QProcess::QPrivateSignal (0x0x7fd63e4e1060) 0 empty + +Vtable for QProcess +QProcess::_ZTV8QProcess: 31u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI8QProcess) +16 (int (*)(...))QProcess::metaObject +24 (int (*)(...))QProcess::qt_metacast +32 (int (*)(...))QProcess::qt_metacall +40 (int (*)(...))QProcess::~QProcess +48 (int (*)(...))QProcess::~QProcess +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QProcess::isSequential +120 (int (*)(...))QProcess::open +128 (int (*)(...))QProcess::close +136 (int (*)(...))QIODevice::pos +144 (int (*)(...))QIODevice::size +152 (int (*)(...))QIODevice::seek +160 (int (*)(...))QProcess::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QProcess::bytesAvailable +184 (int (*)(...))QProcess::bytesToWrite +192 (int (*)(...))QProcess::canReadLine +200 (int (*)(...))QProcess::waitForReadyRead +208 (int (*)(...))QProcess::waitForBytesWritten +216 (int (*)(...))QProcess::readData +224 (int (*)(...))QIODevice::readLineData +232 (int (*)(...))QProcess::writeData +240 (int (*)(...))QProcess::setupChildProcess + +Class QProcess + size=16 align=8 + base size=16 base align=8 +QProcess (0x0x7fd63e4b33a8) 0 + vptr=((& QProcess::_ZTV8QProcess) + 16u) + QIODevice (0x0x7fd63e4b3410) 0 + primary-for QProcess (0x0x7fd63e4b33a8) + QObject (0x0x7fd63e4e1000) 0 + primary-for QIODevice (0x0x7fd63e4b3410) + +Class QVariantAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QVariantAnimation::QPrivateSignal (0x0x7fd63e4e1120) 0 empty + +Vtable for QVariantAnimation +QVariantAnimation::_ZTV17QVariantAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QVariantAnimation) +16 (int (*)(...))QVariantAnimation::metaObject +24 (int (*)(...))QVariantAnimation::qt_metacast +32 (int (*)(...))QVariantAnimation::qt_metacall +40 (int (*)(...))QVariantAnimation::~QVariantAnimation +48 (int (*)(...))QVariantAnimation::~QVariantAnimation +56 (int (*)(...))QVariantAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QVariantAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QVariantAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QVariantAnimation + size=16 align=8 + base size=16 base align=8 +QVariantAnimation (0x0x7fd63e4b3478) 0 + vptr=((& QVariantAnimation::_ZTV17QVariantAnimation) + 16u) + QAbstractAnimation (0x0x7fd63e4b34e0) 0 + primary-for QVariantAnimation (0x0x7fd63e4b3478) + QObject (0x0x7fd63e4e10c0) 0 + primary-for QAbstractAnimation (0x0x7fd63e4b34e0) + +Class QPropertyAnimation::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QPropertyAnimation::QPrivateSignal (0x0x7fd63e4e11e0) 0 empty + +Vtable for QPropertyAnimation +QPropertyAnimation::_ZTV18QPropertyAnimation: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI18QPropertyAnimation) +16 (int (*)(...))QPropertyAnimation::metaObject +24 (int (*)(...))QPropertyAnimation::qt_metacast +32 (int (*)(...))QPropertyAnimation::qt_metacall +40 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +48 (int (*)(...))QPropertyAnimation::~QPropertyAnimation +56 (int (*)(...))QPropertyAnimation::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QVariantAnimation::duration +120 (int (*)(...))QVariantAnimation::updateCurrentTime +128 (int (*)(...))QPropertyAnimation::updateState +136 (int (*)(...))QAbstractAnimation::updateDirection +144 (int (*)(...))QPropertyAnimation::updateCurrentValue +152 (int (*)(...))QVariantAnimation::interpolated + +Class QPropertyAnimation + size=16 align=8 + base size=16 base align=8 +QPropertyAnimation (0x0x7fd63e4b35b0) 0 + vptr=((& QPropertyAnimation::_ZTV18QPropertyAnimation) + 16u) + QVariantAnimation (0x0x7fd63e4b3618) 0 + primary-for QPropertyAnimation (0x0x7fd63e4b35b0) + QAbstractAnimation (0x0x7fd63e4b3680) 0 + primary-for QVariantAnimation (0x0x7fd63e4b3618) + QObject (0x0x7fd63e4e1180) 0 + primary-for QAbstractAnimation (0x0x7fd63e4b3680) + +Class QReadWriteLock + size=8 align=8 + base size=8 base align=8 +QReadWriteLock (0x0x7fd63e4e12a0) 0 + +Class QReadLocker + size=8 align=8 + base size=8 base align=8 +QReadLocker (0x0x7fd63e4e1540) 0 + +Class QWriteLocker + size=8 align=8 + base size=8 base align=8 +QWriteLocker (0x0x7fd63e4e15a0) 0 + +Class QSize + size=8 align=4 + base size=8 base align=4 +QSize (0x0x7fd63e4e1600) 0 + +Class QSizeF + size=16 align=8 + base size=16 base align=8 +QSizeF (0x0x7fd63e4e1960) 0 + +Class QRect + size=16 align=4 + base size=16 base align=4 +QRect (0x0x7fd63e4e1cc0) 0 + +Class QRectF + size=32 align=8 + base size=32 base align=8 +QRectF (0x0x7fd63e4e1f60) 0 + +Class QRegularExpression + size=8 align=8 + base size=8 base align=8 +QRegularExpression (0x0x7fd63e303240) 0 + +Class QRegularExpressionMatch + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatch (0x0x7fd63e303840) 0 + +Class QRegularExpressionMatchIterator + size=8 align=8 + base size=8 base align=8 +QRegularExpressionMatchIterator (0x0x7fd63e303b40) 0 + +Class QResource + size=8 align=8 + base size=8 base align=8 +QResource (0x0x7fd63e303e40) 0 + +Class QSaveFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSaveFile::QPrivateSignal (0x0x7fd63e032000) 0 empty + +Vtable for QSaveFile +QSaveFile::_ZTV9QSaveFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSaveFile) +16 (int (*)(...))QSaveFile::metaObject +24 (int (*)(...))QSaveFile::qt_metacast +32 (int (*)(...))QSaveFile::qt_metacall +40 (int (*)(...))QSaveFile::~QSaveFile +48 (int (*)(...))QSaveFile::~QSaveFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QSaveFile::open +128 (int (*)(...))QSaveFile::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFileDevice::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QSaveFile::writeData +240 (int (*)(...))QSaveFile::fileName +248 (int (*)(...))QFileDevice::resize +256 (int (*)(...))QFileDevice::permissions +264 (int (*)(...))QFileDevice::setPermissions + +Class QSaveFile + size=16 align=8 + base size=16 base align=8 +QSaveFile (0x0x7fd63e26e958) 0 + vptr=((& QSaveFile::_ZTV9QSaveFile) + 16u) + QFileDevice (0x0x7fd63e26e9c0) 0 + primary-for QSaveFile (0x0x7fd63e26e958) + QIODevice (0x0x7fd63e26ea28) 0 + primary-for QFileDevice (0x0x7fd63e26e9c0) + QObject (0x0x7fd63e303f60) 0 + primary-for QIODevice (0x0x7fd63e26ea28) + +Class QSemaphore + size=8 align=8 + base size=8 base align=8 +QSemaphore (0x0x7fd63e0320c0) 0 + +Class QSequentialAnimationGroup::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSequentialAnimationGroup::QPrivateSignal (0x0x7fd63e032180) 0 empty + +Vtable for QSequentialAnimationGroup +QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup: 18u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI25QSequentialAnimationGroup) +16 (int (*)(...))QSequentialAnimationGroup::metaObject +24 (int (*)(...))QSequentialAnimationGroup::qt_metacast +32 (int (*)(...))QSequentialAnimationGroup::qt_metacall +40 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +48 (int (*)(...))QSequentialAnimationGroup::~QSequentialAnimationGroup +56 (int (*)(...))QSequentialAnimationGroup::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSequentialAnimationGroup::duration +120 (int (*)(...))QSequentialAnimationGroup::updateCurrentTime +128 (int (*)(...))QSequentialAnimationGroup::updateState +136 (int (*)(...))QSequentialAnimationGroup::updateDirection + +Class QSequentialAnimationGroup + size=16 align=8 + base size=16 base align=8 +QSequentialAnimationGroup (0x0x7fd63e26ea90) 0 + vptr=((& QSequentialAnimationGroup::_ZTV25QSequentialAnimationGroup) + 16u) + QAnimationGroup (0x0x7fd63e26eaf8) 0 + primary-for QSequentialAnimationGroup (0x0x7fd63e26ea90) + QAbstractAnimation (0x0x7fd63e26eb60) 0 + primary-for QAnimationGroup (0x0x7fd63e26eaf8) + QObject (0x0x7fd63e032120) 0 + primary-for QAbstractAnimation (0x0x7fd63e26eb60) + +Class QSettings::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSettings::QPrivateSignal (0x0x7fd63e032240) 0 empty + +Vtable for QSettings +QSettings::_ZTV9QSettings: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QSettings) +16 (int (*)(...))QSettings::metaObject +24 (int (*)(...))QSettings::qt_metacast +32 (int (*)(...))QSettings::qt_metacall +40 (int (*)(...))QSettings::~QSettings +48 (int (*)(...))QSettings::~QSettings +56 (int (*)(...))QSettings::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSettings + size=16 align=8 + base size=16 base align=8 +QSettings (0x0x7fd63e26ebc8) 0 + vptr=((& QSettings::_ZTV9QSettings) + 16u) + QObject (0x0x7fd63e0321e0) 0 + primary-for QSettings (0x0x7fd63e26ebc8) + +Class QSharedMemory::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSharedMemory::QPrivateSignal (0x0x7fd63e032300) 0 empty + +Vtable for QSharedMemory +QSharedMemory::_ZTV13QSharedMemory: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSharedMemory) +16 (int (*)(...))QSharedMemory::metaObject +24 (int (*)(...))QSharedMemory::qt_metacast +32 (int (*)(...))QSharedMemory::qt_metacall +40 (int (*)(...))QSharedMemory::~QSharedMemory +48 (int (*)(...))QSharedMemory::~QSharedMemory +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSharedMemory + size=16 align=8 + base size=16 base align=8 +QSharedMemory (0x0x7fd63e26ec30) 0 + vptr=((& QSharedMemory::_ZTV13QSharedMemory) + 16u) + QObject (0x0x7fd63e0322a0) 0 + primary-for QSharedMemory (0x0x7fd63e26ec30) + +Class QSignalMapper::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalMapper::QPrivateSignal (0x0x7fd63e0323c0) 0 empty + +Vtable for QSignalMapper +QSignalMapper::_ZTV13QSignalMapper: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QSignalMapper) +16 (int (*)(...))QSignalMapper::metaObject +24 (int (*)(...))QSignalMapper::qt_metacast +32 (int (*)(...))QSignalMapper::qt_metacall +40 (int (*)(...))QSignalMapper::~QSignalMapper +48 (int (*)(...))QSignalMapper::~QSignalMapper +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSignalMapper + size=16 align=8 + base size=16 base align=8 +QSignalMapper (0x0x7fd63e26ec98) 0 + vptr=((& QSignalMapper::_ZTV13QSignalMapper) + 16u) + QObject (0x0x7fd63e032360) 0 + primary-for QSignalMapper (0x0x7fd63e26ec98) + +Class QSignalTransition::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSignalTransition::QPrivateSignal (0x0x7fd63e032480) 0 empty + +Vtable for QSignalTransition +QSignalTransition::_ZTV17QSignalTransition: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI17QSignalTransition) +16 (int (*)(...))QSignalTransition::metaObject +24 (int (*)(...))QSignalTransition::qt_metacast +32 (int (*)(...))QSignalTransition::qt_metacall +40 (int (*)(...))QSignalTransition::~QSignalTransition +48 (int (*)(...))QSignalTransition::~QSignalTransition +56 (int (*)(...))QSignalTransition::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSignalTransition::eventTest +120 (int (*)(...))QSignalTransition::onTransition + +Class QSignalTransition + size=16 align=8 + base size=16 base align=8 +QSignalTransition (0x0x7fd63e26ed00) 0 + vptr=((& QSignalTransition::_ZTV17QSignalTransition) + 16u) + QAbstractTransition (0x0x7fd63e26ed68) 0 + primary-for QSignalTransition (0x0x7fd63e26ed00) + QObject (0x0x7fd63e032420) 0 + primary-for QAbstractTransition (0x0x7fd63e26ed68) + +Class QSocketNotifier::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSocketNotifier::QPrivateSignal (0x0x7fd63e032540) 0 empty + +Vtable for QSocketNotifier +QSocketNotifier::_ZTV15QSocketNotifier: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI15QSocketNotifier) +16 (int (*)(...))QSocketNotifier::metaObject +24 (int (*)(...))QSocketNotifier::qt_metacast +32 (int (*)(...))QSocketNotifier::qt_metacall +40 (int (*)(...))QSocketNotifier::~QSocketNotifier +48 (int (*)(...))QSocketNotifier::~QSocketNotifier +56 (int (*)(...))QSocketNotifier::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QSocketNotifier + size=16 align=8 + base size=16 base align=8 +QSocketNotifier (0x0x7fd63e26edd0) 0 + vptr=((& QSocketNotifier::_ZTV15QSocketNotifier) + 16u) + QObject (0x0x7fd63e0324e0) 0 + primary-for QSocketNotifier (0x0x7fd63e26edd0) + +Class QSortFilterProxyModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QSortFilterProxyModel::QPrivateSignal (0x0x7fd63e032600) 0 empty + +Vtable for QSortFilterProxyModel +QSortFilterProxyModel::_ZTV21QSortFilterProxyModel: 56u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QSortFilterProxyModel) +16 (int (*)(...))QSortFilterProxyModel::metaObject +24 (int (*)(...))QSortFilterProxyModel::qt_metacast +32 (int (*)(...))QSortFilterProxyModel::qt_metacall +40 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +48 (int (*)(...))QSortFilterProxyModel::~QSortFilterProxyModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QSortFilterProxyModel::index +120 (int (*)(...))QSortFilterProxyModel::parent +128 (int (*)(...))QSortFilterProxyModel::sibling +136 (int (*)(...))QSortFilterProxyModel::rowCount +144 (int (*)(...))QSortFilterProxyModel::columnCount +152 (int (*)(...))QSortFilterProxyModel::hasChildren +160 (int (*)(...))QSortFilterProxyModel::data +168 (int (*)(...))QSortFilterProxyModel::setData +176 (int (*)(...))QSortFilterProxyModel::headerData +184 (int (*)(...))QSortFilterProxyModel::setHeaderData +192 (int (*)(...))QAbstractProxyModel::itemData +200 (int (*)(...))QAbstractProxyModel::setItemData +208 (int (*)(...))QSortFilterProxyModel::mimeTypes +216 (int (*)(...))QSortFilterProxyModel::mimeData +224 (int (*)(...))QAbstractProxyModel::canDropMimeData +232 (int (*)(...))QSortFilterProxyModel::dropMimeData +240 (int (*)(...))QSortFilterProxyModel::supportedDropActions +248 (int (*)(...))QAbstractProxyModel::supportedDragActions +256 (int (*)(...))QSortFilterProxyModel::insertRows +264 (int (*)(...))QSortFilterProxyModel::insertColumns +272 (int (*)(...))QSortFilterProxyModel::removeRows +280 (int (*)(...))QSortFilterProxyModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QSortFilterProxyModel::fetchMore +312 (int (*)(...))QSortFilterProxyModel::canFetchMore +320 (int (*)(...))QSortFilterProxyModel::flags +328 (int (*)(...))QSortFilterProxyModel::sort +336 (int (*)(...))QSortFilterProxyModel::buddy +344 (int (*)(...))QSortFilterProxyModel::match +352 (int (*)(...))QSortFilterProxyModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractProxyModel::submit +376 (int (*)(...))QAbstractProxyModel::revert +384 (int (*)(...))QSortFilterProxyModel::setSourceModel +392 (int (*)(...))QSortFilterProxyModel::mapToSource +400 (int (*)(...))QSortFilterProxyModel::mapFromSource +408 (int (*)(...))QSortFilterProxyModel::mapSelectionToSource +416 (int (*)(...))QSortFilterProxyModel::mapSelectionFromSource +424 (int (*)(...))QSortFilterProxyModel::filterAcceptsRow +432 (int (*)(...))QSortFilterProxyModel::filterAcceptsColumn +440 (int (*)(...))QSortFilterProxyModel::lessThan + +Class QSortFilterProxyModel + size=16 align=8 + base size=16 base align=8 +QSortFilterProxyModel (0x0x7fd63e26ee38) 0 + vptr=((& QSortFilterProxyModel::_ZTV21QSortFilterProxyModel) + 16u) + QAbstractProxyModel (0x0x7fd63e26eea0) 0 + primary-for QSortFilterProxyModel (0x0x7fd63e26ee38) + QAbstractItemModel (0x0x7fd63e26ef08) 0 + primary-for QAbstractProxyModel (0x0x7fd63e26eea0) + QObject (0x0x7fd63e0325a0) 0 + primary-for QAbstractItemModel (0x0x7fd63e26ef08) + +Class QStandardPaths + size=1 align=1 + base size=0 base align=1 +QStandardPaths (0x0x7fd63e0327e0) 0 empty + +Class QState::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QState::QPrivateSignal (0x0x7fd63e032a20) 0 empty + +Vtable for QState +QState::_ZTV6QState: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QState) +16 (int (*)(...))QState::metaObject +24 (int (*)(...))QState::qt_metacast +32 (int (*)(...))QState::qt_metacall +40 (int (*)(...))QState::~QState +48 (int (*)(...))QState::~QState +56 (int (*)(...))QState::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QState::onEntry +120 (int (*)(...))QState::onExit + +Class QState + size=16 align=8 + base size=16 base align=8 +QState (0x0x7fd63e1030d0) 0 + vptr=((& QState::_ZTV6QState) + 16u) + QAbstractState (0x0x7fd63e103138) 0 + primary-for QState (0x0x7fd63e1030d0) + QObject (0x0x7fd63e0329c0) 0 + primary-for QAbstractState (0x0x7fd63e103138) + +Class QStateMachine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStateMachine::QPrivateSignal (0x0x7fd63e032b40) 0 empty + +Vtable for QStateMachine::SignalEvent +QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine11SignalEventE) +16 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent +24 (int (*)(...))QStateMachine::SignalEvent::~SignalEvent + +Class QStateMachine::SignalEvent + size=48 align=8 + base size=48 base align=8 +QStateMachine::SignalEvent (0x0x7fd63e1032d8) 0 + vptr=((& QStateMachine::SignalEvent::_ZTVN13QStateMachine11SignalEventE) + 16u) + QEvent (0x0x7fd63e032ba0) 0 + primary-for QStateMachine::SignalEvent (0x0x7fd63e1032d8) + +Vtable for QStateMachine::WrappedEvent +QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE: 4u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTIN13QStateMachine12WrappedEventE) +16 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent +24 (int (*)(...))QStateMachine::WrappedEvent::~WrappedEvent + +Class QStateMachine::WrappedEvent + size=40 align=8 + base size=40 base align=8 +QStateMachine::WrappedEvent (0x0x7fd63e103340) 0 + vptr=((& QStateMachine::WrappedEvent::_ZTVN13QStateMachine12WrappedEventE) + 16u) + QEvent (0x0x7fd63e032c00) 0 + primary-for QStateMachine::WrappedEvent (0x0x7fd63e103340) + +Vtable for QStateMachine +QStateMachine::_ZTV13QStateMachine: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI13QStateMachine) +16 (int (*)(...))QStateMachine::metaObject +24 (int (*)(...))QStateMachine::qt_metacast +32 (int (*)(...))QStateMachine::qt_metacall +40 (int (*)(...))QStateMachine::~QStateMachine +48 (int (*)(...))QStateMachine::~QStateMachine +56 (int (*)(...))QStateMachine::event +64 (int (*)(...))QStateMachine::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QStateMachine::onEntry +120 (int (*)(...))QStateMachine::onExit +128 (int (*)(...))QStateMachine::beginSelectTransitions +136 (int (*)(...))QStateMachine::endSelectTransitions +144 (int (*)(...))QStateMachine::beginMicrostep +152 (int (*)(...))QStateMachine::endMicrostep + +Class QStateMachine + size=16 align=8 + base size=16 base align=8 +QStateMachine (0x0x7fd63e1031a0) 0 + vptr=((& QStateMachine::_ZTV13QStateMachine) + 16u) + QState (0x0x7fd63e103208) 0 + primary-for QStateMachine (0x0x7fd63e1031a0) + QAbstractState (0x0x7fd63e103270) 0 + primary-for QState (0x0x7fd63e103208) + QObject (0x0x7fd63e032ae0) 0 + primary-for QAbstractState (0x0x7fd63e103270) + +Class QStorageInfo + size=8 align=8 + base size=8 base align=8 +QStorageInfo (0x0x7fd63e032c60) 0 + +Class QAbstractConcatenable + size=1 align=1 + base size=0 base align=1 +QAbstractConcatenable (0x0x7fd63ddaa0c0) 0 empty + +Class QStringListModel::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QStringListModel::QPrivateSignal (0x0x7fd63ddaab40) 0 empty + +Vtable for QStringListModel +QStringListModel::_ZTV16QStringListModel: 48u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI16QStringListModel) +16 (int (*)(...))QStringListModel::metaObject +24 (int (*)(...))QStringListModel::qt_metacast +32 (int (*)(...))QStringListModel::qt_metacall +40 (int (*)(...))QStringListModel::~QStringListModel +48 (int (*)(...))QStringListModel::~QStringListModel +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QAbstractListModel::index +120 (int (*)(...))QAbstractListModel::parent +128 (int (*)(...))QStringListModel::sibling +136 (int (*)(...))QStringListModel::rowCount +144 (int (*)(...))QAbstractListModel::columnCount +152 (int (*)(...))QAbstractListModel::hasChildren +160 (int (*)(...))QStringListModel::data +168 (int (*)(...))QStringListModel::setData +176 (int (*)(...))QAbstractItemModel::headerData +184 (int (*)(...))QAbstractItemModel::setHeaderData +192 (int (*)(...))QAbstractItemModel::itemData +200 (int (*)(...))QAbstractItemModel::setItemData +208 (int (*)(...))QAbstractItemModel::mimeTypes +216 (int (*)(...))QAbstractItemModel::mimeData +224 (int (*)(...))QAbstractItemModel::canDropMimeData +232 (int (*)(...))QAbstractListModel::dropMimeData +240 (int (*)(...))QStringListModel::supportedDropActions +248 (int (*)(...))QAbstractItemModel::supportedDragActions +256 (int (*)(...))QStringListModel::insertRows +264 (int (*)(...))QAbstractItemModel::insertColumns +272 (int (*)(...))QStringListModel::removeRows +280 (int (*)(...))QAbstractItemModel::removeColumns +288 (int (*)(...))QAbstractItemModel::moveRows +296 (int (*)(...))QAbstractItemModel::moveColumns +304 (int (*)(...))QAbstractItemModel::fetchMore +312 (int (*)(...))QAbstractItemModel::canFetchMore +320 (int (*)(...))QStringListModel::flags +328 (int (*)(...))QStringListModel::sort +336 (int (*)(...))QAbstractItemModel::buddy +344 (int (*)(...))QAbstractItemModel::match +352 (int (*)(...))QAbstractItemModel::span +360 (int (*)(...))QAbstractItemModel::roleNames +368 (int (*)(...))QAbstractItemModel::submit +376 (int (*)(...))QAbstractItemModel::revert + +Class QStringListModel + size=24 align=8 + base size=24 base align=8 +QStringListModel (0x0x7fd63e103dd0) 0 + vptr=((& QStringListModel::_ZTV16QStringListModel) + 16u) + QAbstractListModel (0x0x7fd63e103e38) 0 + primary-for QStringListModel (0x0x7fd63e103dd0) + QAbstractItemModel (0x0x7fd63e103ea0) 0 + primary-for QAbstractListModel (0x0x7fd63e103e38) + QObject (0x0x7fd63ddaaae0) 0 + primary-for QAbstractItemModel (0x0x7fd63e103ea0) + +Class QSystemSemaphore + size=8 align=8 + base size=8 base align=8 +QSystemSemaphore (0x0x7fd63ddaaba0) 0 + +Class QTemporaryDir + size=8 align=8 + base size=8 base align=8 +QTemporaryDir (0x0x7fd63ddaac60) 0 + +Class QTemporaryFile::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTemporaryFile::QPrivateSignal (0x0x7fd63ddaad80) 0 empty + +Vtable for QTemporaryFile +QTemporaryFile::_ZTV14QTemporaryFile: 34u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI14QTemporaryFile) +16 (int (*)(...))QTemporaryFile::metaObject +24 (int (*)(...))QTemporaryFile::qt_metacast +32 (int (*)(...))QTemporaryFile::qt_metacall +40 (int (*)(...))QTemporaryFile::~QTemporaryFile +48 (int (*)(...))QTemporaryFile::~QTemporaryFile +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QFileDevice::isSequential +120 (int (*)(...))QTemporaryFile::open +128 (int (*)(...))QFileDevice::close +136 (int (*)(...))QFileDevice::pos +144 (int (*)(...))QFile::size +152 (int (*)(...))QFileDevice::seek +160 (int (*)(...))QFileDevice::atEnd +168 (int (*)(...))QIODevice::reset +176 (int (*)(...))QIODevice::bytesAvailable +184 (int (*)(...))QIODevice::bytesToWrite +192 (int (*)(...))QIODevice::canReadLine +200 (int (*)(...))QIODevice::waitForReadyRead +208 (int (*)(...))QIODevice::waitForBytesWritten +216 (int (*)(...))QFileDevice::readData +224 (int (*)(...))QFileDevice::readLineData +232 (int (*)(...))QFileDevice::writeData +240 (int (*)(...))QTemporaryFile::fileName +248 (int (*)(...))QFile::resize +256 (int (*)(...))QFile::permissions +264 (int (*)(...))QFile::setPermissions + +Class QTemporaryFile + size=16 align=8 + base size=16 base align=8 +QTemporaryFile (0x0x7fd63e103f08) 0 + vptr=((& QTemporaryFile::_ZTV14QTemporaryFile) + 16u) + QFile (0x0x7fd63e103f70) 0 + primary-for QTemporaryFile (0x0x7fd63e103f08) + QFileDevice (0x0x7fd63de51000) 0 + primary-for QFile (0x0x7fd63e103f70) + QIODevice (0x0x7fd63de51068) 0 + primary-for QFileDevice (0x0x7fd63de51000) + QObject (0x0x7fd63ddaad20) 0 + primary-for QIODevice (0x0x7fd63de51068) + +Class QTextBoundaryFinder + size=48 align=8 + base size=48 base align=8 +QTextBoundaryFinder (0x0x7fd63ddaade0) 0 + +Class QTextCodec::ConverterState + size=32 align=8 + base size=32 base align=8 +QTextCodec::ConverterState (0x0x7fd63de9e060) 0 + +Vtable for QTextCodec +QTextCodec::_ZTV10QTextCodec: 9u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI10QTextCodec) +16 (int (*)(...))__cxa_pure_virtual +24 (int (*)(...))QTextCodec::aliases +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual +56 0u +64 0u + +Class QTextCodec + size=8 align=8 + base size=8 base align=8 +QTextCodec (0x0x7fd63de9e000) 0 nearly-empty + vptr=((& QTextCodec::_ZTV10QTextCodec) + 16u) + +Class QTextEncoder + size=40 align=8 + base size=40 base align=8 +QTextEncoder (0x0x7fd63de9e240) 0 + +Class QTextDecoder + size=40 align=8 + base size=40 base align=8 +QTextDecoder (0x0x7fd63de9e2a0) 0 + +Class QThread::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThread::QPrivateSignal (0x0x7fd63de9e360) 0 empty + +Vtable for QThread +QThread::_ZTV7QThread: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI7QThread) +16 (int (*)(...))QThread::metaObject +24 (int (*)(...))QThread::qt_metacast +32 (int (*)(...))QThread::qt_metacall +40 (int (*)(...))QThread::~QThread +48 (int (*)(...))QThread::~QThread +56 (int (*)(...))QThread::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QThread::run + +Class QThread + size=16 align=8 + base size=16 base align=8 +QThread (0x0x7fd63de51270) 0 + vptr=((& QThread::_ZTV7QThread) + 16u) + QObject (0x0x7fd63de9e300) 0 + primary-for QThread (0x0x7fd63de51270) + +Class QThreadPool::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QThreadPool::QPrivateSignal (0x0x7fd63de9e420) 0 empty + +Vtable for QThreadPool +QThreadPool::_ZTV11QThreadPool: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QThreadPool) +16 (int (*)(...))QThreadPool::metaObject +24 (int (*)(...))QThreadPool::qt_metacast +32 (int (*)(...))QThreadPool::qt_metacall +40 (int (*)(...))QThreadPool::~QThreadPool +48 (int (*)(...))QThreadPool::~QThreadPool +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QThreadPool + size=16 align=8 + base size=16 base align=8 +QThreadPool (0x0x7fd63de512d8) 0 + vptr=((& QThreadPool::_ZTV11QThreadPool) + 16u) + QObject (0x0x7fd63de9e3c0) 0 + primary-for QThreadPool (0x0x7fd63de512d8) + +Class QThreadStorageData + size=4 align=4 + base size=4 base align=4 +QThreadStorageData (0x0x7fd63de9e480) 0 + +Class QTimeLine::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimeLine::QPrivateSignal (0x0x7fd63de9e5a0) 0 empty + +Vtable for QTimeLine +QTimeLine::_ZTV9QTimeLine: 15u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI9QTimeLine) +16 (int (*)(...))QTimeLine::metaObject +24 (int (*)(...))QTimeLine::qt_metacast +32 (int (*)(...))QTimeLine::qt_metacall +40 (int (*)(...))QTimeLine::~QTimeLine +48 (int (*)(...))QTimeLine::~QTimeLine +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimeLine::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTimeLine::valueForTime + +Class QTimeLine + size=16 align=8 + base size=16 base align=8 +QTimeLine (0x0x7fd63de51340) 0 + vptr=((& QTimeLine::_ZTV9QTimeLine) + 16u) + QObject (0x0x7fd63de9e540) 0 + primary-for QTimeLine (0x0x7fd63de51340) + +Class QTimer::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTimer::QPrivateSignal (0x0x7fd63de9e660) 0 empty + +Vtable for QTimer +QTimer::_ZTV6QTimer: 14u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI6QTimer) +16 (int (*)(...))QTimer::metaObject +24 (int (*)(...))QTimer::qt_metacast +32 (int (*)(...))QTimer::qt_metacall +40 (int (*)(...))QTimer::~QTimer +48 (int (*)(...))QTimer::~QTimer +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QTimer::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify + +Class QTimer + size=32 align=8 + base size=29 base align=8 +QTimer (0x0x7fd63de513a8) 0 + vptr=((& QTimer::_ZTV6QTimer) + 16u) + QObject (0x0x7fd63de9e600) 0 + primary-for QTimer (0x0x7fd63de513a8) + +Class QTimeZone::OffsetData + size=32 align=8 + base size=28 base align=8 +QTimeZone::OffsetData (0x0x7fd63db75480) 0 + +Class QTimeZone + size=8 align=8 + base size=8 base align=8 +QTimeZone (0x0x7fd63db75420) 0 + +Class QTranslator::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QTranslator::QPrivateSignal (0x0x7fd63db75a20) 0 empty + +Vtable for QTranslator +QTranslator::_ZTV11QTranslator: 16u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI11QTranslator) +16 (int (*)(...))QTranslator::metaObject +24 (int (*)(...))QTranslator::qt_metacast +32 (int (*)(...))QTranslator::qt_metacall +40 (int (*)(...))QTranslator::~QTranslator +48 (int (*)(...))QTranslator::~QTranslator +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QTranslator::translate +120 (int (*)(...))QTranslator::isEmpty + +Class QTranslator + size=16 align=8 + base size=16 base align=8 +QTranslator (0x0x7fd63db7d618) 0 + vptr=((& QTranslator::_ZTV11QTranslator) + 16u) + QObject (0x0x7fd63db759c0) 0 + primary-for QTranslator (0x0x7fd63db7d618) + +Class QUrl + size=8 align=8 + base size=8 base align=8 +QUrl (0x0x7fd63db75b40) 0 + +Class QUrlQuery + size=8 align=8 + base size=8 base align=8 +QUrlQuery (0x0x7fd63dcdf240) 0 + +Class QUuid + size=16 align=4 + base size=16 base align=4 +QUuid (0x0x7fd63dcdf540) 0 + +Class QWaitCondition + size=8 align=8 + base size=8 base align=8 +QWaitCondition (0x0x7fd63dcdf7e0) 0 + +Class QXmlStreamStringRef + size=16 align=8 + base size=16 base align=8 +QXmlStreamStringRef (0x0x7fd63dcdf840) 0 + +Class QXmlStreamAttribute + size=80 align=8 + base size=73 base align=8 +QXmlStreamAttribute (0x0x7fd63d9fdc60) 0 + +Class QXmlStreamAttributes + size=8 align=8 + base size=8 base align=8 +QXmlStreamAttributes (0x0x7fd63da00c30) 0 + QVector (0x0x7fd63da50060) 0 + +Class QXmlStreamNamespaceDeclaration + size=40 align=8 + base size=40 base align=8 +QXmlStreamNamespaceDeclaration (0x0x7fd63da500c0) 0 + +Class QXmlStreamNotationDeclaration + size=56 align=8 + base size=56 base align=8 +QXmlStreamNotationDeclaration (0x0x7fd63da50360) 0 + +Class QXmlStreamEntityDeclaration + size=88 align=8 + base size=88 base align=8 +QXmlStreamEntityDeclaration (0x0x7fd63da50600) 0 + +Vtable for QXmlStreamEntityResolver +QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver: 6u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI24QXmlStreamEntityResolver) +16 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +24 (int (*)(...))QXmlStreamEntityResolver::~QXmlStreamEntityResolver +32 (int (*)(...))QXmlStreamEntityResolver::resolveEntity +40 (int (*)(...))QXmlStreamEntityResolver::resolveUndeclaredEntity + +Class QXmlStreamEntityResolver + size=8 align=8 + base size=8 base align=8 +QXmlStreamEntityResolver (0x0x7fd63da508a0) 0 nearly-empty + vptr=((& QXmlStreamEntityResolver::_ZTV24QXmlStreamEntityResolver) + 16u) + +Class QXmlStreamReader + size=8 align=8 + base size=8 base align=8 +QXmlStreamReader (0x0x7fd63da50900) 0 + +Class QXmlStreamWriter + size=8 align=8 + base size=8 base align=8 +QXmlStreamWriter (0x0x7fd63da50a20) 0 + +Class QGeoAddress + size=8 align=8 + base size=8 base align=8 +QGeoAddress (0x0x7fd63da50b40) 0 + +Class QGeoCoordinate + size=8 align=8 + base size=8 base align=8 +QGeoCoordinate (0x0x7fd63da50f60) 0 + +Class QGeoShape + size=8 align=8 + base size=8 base align=8 +QGeoShape (0x0x7fd63db523c0) 0 + +Class QGeoAreaMonitorInfo + size=8 align=8 + base size=8 base align=8 +QGeoAreaMonitorInfo (0x0x7fd63db527e0) 0 + +Class QGeoPositionInfo + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfo (0x0x7fd63db528a0) 0 + +Class QGeoPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoPositionInfoSource::QPrivateSignal (0x0x7fd63db52960) 0 empty + +Vtable for QGeoPositionInfoSource +QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI22QGeoPositionInfoSource) +16 (int (*)(...))QGeoPositionInfoSource::metaObject +24 (int (*)(...))QGeoPositionInfoSource::qt_metacast +32 (int (*)(...))QGeoPositionInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoPositionInfoSource (0x0x7fd63da81d00) 0 + vptr=((& QGeoPositionInfoSource::_ZTV22QGeoPositionInfoSource) + 16u) + QObject (0x0x7fd63db52900) 0 + primary-for QGeoPositionInfoSource (0x0x7fd63da81d00) + +Class QGeoAreaMonitorSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoAreaMonitorSource::QPrivateSignal (0x0x7fd63db52ba0) 0 empty + +Vtable for QGeoAreaMonitorSource +QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource: 23u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI21QGeoAreaMonitorSource) +16 (int (*)(...))QGeoAreaMonitorSource::metaObject +24 (int (*)(...))QGeoAreaMonitorSource::qt_metacast +32 (int (*)(...))QGeoAreaMonitorSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoAreaMonitorSource::setPositionInfoSource +120 (int (*)(...))QGeoAreaMonitorSource::positionInfoSource +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual +160 (int (*)(...))__cxa_pure_virtual +168 (int (*)(...))__cxa_pure_virtual +176 (int (*)(...))__cxa_pure_virtual + +Class QGeoAreaMonitorSource + size=24 align=8 + base size=24 base align=8 +QGeoAreaMonitorSource (0x0x7fd63da81e38) 0 + vptr=((& QGeoAreaMonitorSource::_ZTV21QGeoAreaMonitorSource) + 16u) + QObject (0x0x7fd63db52b40) 0 + primary-for QGeoAreaMonitorSource (0x0x7fd63da81e38) + +Class QGeoRectangle + size=8 align=8 + base size=8 base align=8 +QGeoRectangle (0x0x7fd63da81ea0) 0 + QGeoShape (0x0x7fd63db52c00) 0 + +Class QGeoCircle + size=8 align=8 + base size=8 base align=8 +QGeoCircle (0x0x7fd63d81b068) 0 + QGeoShape (0x0x7fd63d843120) 0 + +Class QGeoLocation + size=8 align=8 + base size=8 base align=8 +QGeoLocation (0x0x7fd63d8434e0) 0 + +Class QGeoPath + size=8 align=8 + base size=8 base align=8 +QGeoPath (0x0x7fd63d81b410) 0 + QGeoShape (0x0x7fd63d843900) 0 + +Class QGeoSatelliteInfo + size=8 align=8 + base size=8 base align=8 +QGeoSatelliteInfo (0x0x7fd63d843cc0) 0 + +Class QGeoSatelliteInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QGeoSatelliteInfoSource::QPrivateSignal (0x0x7fd63d843d80) 0 empty + +Vtable for QGeoSatelliteInfoSource +QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource: 20u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QGeoSatelliteInfoSource) +16 (int (*)(...))QGeoSatelliteInfoSource::metaObject +24 (int (*)(...))QGeoSatelliteInfoSource::qt_metacast +32 (int (*)(...))QGeoSatelliteInfoSource::qt_metacall +40 0u +48 0u +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QGeoSatelliteInfoSource::setUpdateInterval +120 (int (*)(...))__cxa_pure_virtual +128 (int (*)(...))__cxa_pure_virtual +136 (int (*)(...))__cxa_pure_virtual +144 (int (*)(...))__cxa_pure_virtual +152 (int (*)(...))__cxa_pure_virtual + +Class QGeoSatelliteInfoSource + size=24 align=8 + base size=24 base align=8 +QGeoSatelliteInfoSource (0x0x7fd63d81b618) 0 + vptr=((& QGeoSatelliteInfoSource::_ZTV23QGeoSatelliteInfoSource) + 16u) + QObject (0x0x7fd63d843d20) 0 + primary-for QGeoSatelliteInfoSource (0x0x7fd63d81b618) + +Vtable for QGeoPositionInfoSourceFactory +QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory: 7u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI29QGeoPositionInfoSourceFactory) +16 0u +24 0u +32 (int (*)(...))__cxa_pure_virtual +40 (int (*)(...))__cxa_pure_virtual +48 (int (*)(...))__cxa_pure_virtual + +Class QGeoPositionInfoSourceFactory + size=8 align=8 + base size=8 base align=8 +QGeoPositionInfoSourceFactory (0x0x7fd63d843e40) 0 nearly-empty + vptr=((& QGeoPositionInfoSourceFactory::_ZTV29QGeoPositionInfoSourceFactory) + 16u) + +Class QNmeaPositionInfoSource::QPrivateSignal + size=1 align=1 + base size=0 base align=1 +QNmeaPositionInfoSource::QPrivateSignal (0x0x7fd63d843f60) 0 empty + +Vtable for QNmeaPositionInfoSource +QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource: 24u entries +0 (int (*)(...))0 +8 (int (*)(...))(& _ZTI23QNmeaPositionInfoSource) +16 (int (*)(...))QNmeaPositionInfoSource::metaObject +24 (int (*)(...))QNmeaPositionInfoSource::qt_metacast +32 (int (*)(...))QNmeaPositionInfoSource::qt_metacall +40 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +48 (int (*)(...))QNmeaPositionInfoSource::~QNmeaPositionInfoSource +56 (int (*)(...))QObject::event +64 (int (*)(...))QObject::eventFilter +72 (int (*)(...))QObject::timerEvent +80 (int (*)(...))QObject::childEvent +88 (int (*)(...))QObject::customEvent +96 (int (*)(...))QObject::connectNotify +104 (int (*)(...))QObject::disconnectNotify +112 (int (*)(...))QNmeaPositionInfoSource::setUpdateInterval +120 (int (*)(...))QGeoPositionInfoSource::setPreferredPositioningMethods +128 (int (*)(...))QNmeaPositionInfoSource::lastKnownPosition +136 (int (*)(...))QNmeaPositionInfoSource::supportedPositioningMethods +144 (int (*)(...))QNmeaPositionInfoSource::minimumUpdateInterval +152 (int (*)(...))QNmeaPositionInfoSource::error +160 (int (*)(...))QNmeaPositionInfoSource::startUpdates +168 (int (*)(...))QNmeaPositionInfoSource::stopUpdates +176 (int (*)(...))QNmeaPositionInfoSource::requestUpdate +184 (int (*)(...))QNmeaPositionInfoSource::parsePosInfoFromNmeaData + +Class QNmeaPositionInfoSource + size=32 align=8 + base size=32 base align=8 +QNmeaPositionInfoSource (0x0x7fd63d81b680) 0 + vptr=((& QNmeaPositionInfoSource::_ZTV23QNmeaPositionInfoSource) + 16u) + QGeoPositionInfoSource (0x0x7fd63d81b6e8) 0 + primary-for QNmeaPositionInfoSource (0x0x7fd63d81b680) + QObject (0x0x7fd63d843f00) 0 + primary-for QGeoPositionInfoSource (0x0x7fd63d81b6e8) + diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt new file mode 100644 index 0000000..8430084 --- /dev/null +++ b/tests/auto/cmake/CMakeLists.txt @@ -0,0 +1,19 @@ + +cmake_minimum_required(VERSION 2.8) + +project(qmake_cmake_files) + +enable_testing() + +find_package(Qt5Core REQUIRED) + +include("${_Qt5CTestMacros}") + +set(qt_module_includes + Location QPlaceCategory + Positioning QGeoRectangle +) + +test_module_includes( + ${qt_module_includes} +) diff --git a/tests/auto/cmake/cmake.pro b/tests/auto/cmake/cmake.pro new file mode 100644 index 0000000..69540e9 --- /dev/null +++ b/tests/auto/cmake/cmake.pro @@ -0,0 +1,7 @@ + +# Cause make to do nothing. +TEMPLATE = subdirs + +CMAKE_QT_MODULES_UNDER_TEST = location positioning + +CONFIG += ctest_testcase diff --git a/tests/auto/declarative_core/declarative_core.pro b/tests/auto/declarative_core/declarative_core.pro new file mode 100644 index 0000000..30c2b7f --- /dev/null +++ b/tests/auto/declarative_core/declarative_core.pro @@ -0,0 +1,14 @@ +# QML tests in this directory must not depend on an OpenGL context. +# QML tests that do require an OpenGL context must go in ../../declarative_ui. + +TEMPLATE = app +TARGET = tst_declarative_core +CONFIG += qmltestcase +SOURCES += main.cpp + +CONFIG -= app_bundle + +QT += location quick + +OTHER_FILES = *.qml *.js +TESTDATA = $$OTHER_FILES diff --git a/tests/auto/declarative_core/main.cpp b/tests/auto/declarative_core/main.cpp new file mode 100644 index 0000000..b380a23 --- /dev/null +++ b/tests/auto/declarative_core/main.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +static void initializeLibraryPath() +{ +#if QT_CONFIG(library) + // Set custom path since CI doesn't install test plugins +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif +} + +Q_COREAPP_STARTUP_FUNCTION(initializeLibraryPath) + +QUICK_TEST_MAIN(declarative_core) diff --git a/tests/auto/declarative_core/tst_address.qml b/tests/auto/declarative_core/tst_address.qml new file mode 100644 index 0000000..94f986c --- /dev/null +++ b/tests/auto/declarative_core/tst_address.qml @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtTest 1.0 +import QtPositioning 5.2 + +TestCase { + id: testCase + + name: "Address" + + Address { + id: address + + street: "742 Evergreen Tce" + district: "Pressboard Estates" + city: "Springfield" + state: "Oregon" + postalCode: "8900" + country: "United States" + countryCode: "USA" + } + + function test_qmlAddressText() { + compare(address.isTextGenerated, true); + compare(address.text, "742 Evergreen Tce
    Springfield, Oregon 8900
    United States"); + var textChangedSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + textChangedSpy.target = address; + textChangedSpy.signalName = "textChanged" + + var isTextGeneratedSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + isTextGeneratedSpy.target = address + isTextGeneratedSpy.signalName = "isTextGeneratedChanged" + + address.countryCode = "FRA"; + compare(address.text, "742 Evergreen Tce
    8900 Springfield
    United States"); + compare(textChangedSpy.count, 1); + textChangedSpy.clear(); + compare(isTextGeneratedSpy.count, 0); + + address.text = "address label"; + compare(address.isTextGenerated, false); + compare(address.text, "address label"); + compare(textChangedSpy.count, 1); + textChangedSpy.clear(); + compare(isTextGeneratedSpy.count, 1); + isTextGeneratedSpy.clear(); + + address.countryCode = "USA"; + compare(address.text, "address label"); + compare(textChangedSpy.count, 0); + textChangedSpy.clear(); + compare(isTextGeneratedSpy.count, 0); + + address.text = ""; + compare(address.isTextGenerated, true); + compare(address.text, "742 Evergreen Tce
    Springfield, Oregon 8900
    United States"); + compare(textChangedSpy.count, 1); + textChangedSpy.clear(); + compare(isTextGeneratedSpy.count, 1); + isTextGeneratedSpy.clear(); + } +} diff --git a/tests/auto/declarative_core/tst_category.qml b/tests/auto/declarative_core/tst_category.qml new file mode 100644 index 0000000..51809dc --- /dev/null +++ b/tests/auto/declarative_core/tst_category.qml @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "Category" + + Category { id: emptyCategory } + + function test_empty() { + compare(emptyCategory.categoryId, ""); + compare(emptyCategory.name, ""); + compare(emptyCategory.visibility, Category.UnspecifiedVisibility); + compare(emptyCategory.status, Category.Ready); + compare(emptyCategory.plugin, null); + verify(emptyCategory.icon); + } + + Category { + id: qmlCategory + + plugin: testPlugin + + categoryId: "test-category-id" + name: "Test Category" + visibility: Category.DeviceVisibility + + icon: Icon { + Component.onCompleted: { + parameters.singleUrl = "http://example.com/icons/test-category.png" + } + } + } + + function test_qmlConstructedCategory() { + compare(qmlCategory.categoryId, "test-category-id"); + compare(qmlCategory.name, "Test Category"); + compare(qmlCategory.visibility, Category.DeviceVisibility); + compare(qmlCategory.status, Category.Ready); + compare(qmlCategory.plugin, testPlugin); + verify(qmlCategory.icon); + compare(qmlCategory.icon.url(), "http://example.com/icons/test-category.png"); + compare(qmlCategory.icon.parameters.singleUrl, "http://example.com/icons/test-category.png"); + compare(qmlCategory.icon.plugin, qmlCategory.plugin); + } + + Category { + id: testCategory + } + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + } + + Plugin { + id: invalidPlugin + } + + Icon { + id: testIcon + } + + Category { + id: saveCategory + + name: "Test Category" + visibility: Place.DeviceVisibility + } + + VisualDataModel { + id: categoryModel + + model: CategoryModel { + plugin: testPlugin + } + delegate: Item { } + } + + function test_setAndGet_data() { + return [ + { tag: "name", property: "name", signal: "nameChanged", value: "Test Category", reset: "" }, + { tag: "categoryId", property: "categoryId", signal: "categoryIdChanged", value: "test-category-id-1", reset: "" }, + { tag: "visibility", property: "visibility", signal: "visibilityChanged", value: Place.PublicVisibility, reset: Place.UnspecifiedVisibility }, + { tag: "plugin", property: "plugin", signal: "pluginChanged", value: testPlugin }, + { tag: "icon", property: "icon", signal: "iconChanged", value: testIcon } + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testCategory, data); + } + + function test_save() { + categoryModel.model.update(); + tryCompare(categoryModel.model, "status", CategoryModel.Ready); + compare(categoryModel.count, 0); + + saveCategory.plugin = testPlugin; + saveCategory.categoryId = "invalid-category-id"; + + saveCategory.save(); + + compare(saveCategory.status, Category.Saving); + verify(saveCategory.errorString().length === 0); + + tryCompare(saveCategory, "status", Category.Error); + verify(saveCategory.errorString().length > 0); + + // try again without an invalid categoryId + saveCategory.categoryId = ""; + saveCategory.save(); + + compare(saveCategory.status, Category.Saving); + + tryCompare(saveCategory, "status", Category.Ready); + verify(saveCategory.errorString().length === 0); + + verify(saveCategory.categoryId !== ""); + + + // Verify that the category was added to the model + categoryModel.model.update(); + compare(categoryModel.model.status, CategoryModel.Loading); + + tryCompare(categoryModel.model, "status", CategoryModel.Ready); + + compare(categoryModel.count, 1); + var modelCategory = categoryModel.model.data(categoryModel.modelIndex(0), + CategoryModel.CategoryRole); + compare(modelCategory.categoryId, saveCategory.categoryId); + compare(modelCategory.name, saveCategory.name); + + + // Remove a category + saveCategory.remove(); + + compare(saveCategory.status, Category.Removing); + + tryCompare(saveCategory, "status", Category.Ready); + verify(saveCategory.errorString().length === 0); + + + // Verify that the category was removed from the model + categoryModel.model.update(); + compare(categoryModel.model.status, CategoryModel.Loading); + + tryCompare(categoryModel.model, "status", CategoryModel.Ready); + + compare(categoryModel.count, 0); + + + // Try again, this time fail because category does not exist + saveCategory.remove(); + + compare(saveCategory.status, Category.Removing); + + tryCompare(saveCategory, "status", Category.Error); + + verify(saveCategory.errorString().length > 0); + } + + function test_saveWithoutPlugin() { + saveCategory.plugin = null; + saveCategory.categoryId = ""; + + saveCategory.save(); + + tryCompare(saveCategory, "status", Category.Error); + + verify(saveCategory.errorString().length > 0); + compare(saveCategory.categoryId, ""); + + saveCategory.plugin = invalidPlugin; + + saveCategory.save(); + + compare(saveCategory.status, Category.Error); + + verify(saveCategory.errorString().length > 0); + compare(saveCategory.categoryId, ""); + } + + function test_removeWithoutPlugin() { + saveCategory.plugin = null; + saveCategory.categoryId = "test-category-id"; + + saveCategory.remove(); + + compare(saveCategory.status, Category.Error); + + verify(saveCategory.errorString().length > 0); + compare(saveCategory.categoryId, "test-category-id"); + + saveCategory.plugin = invalidPlugin; + + saveCategory.remove(); + + compare(saveCategory.status, Category.Error); + + verify(saveCategory.errorString().length > 0); + compare(saveCategory.categoryId, "test-category-id"); + } +} diff --git a/tests/auto/declarative_core/tst_categorymodel.qml b/tests/auto/declarative_core/tst_categorymodel.qml new file mode 100644 index 0000000..0b6e50a --- /dev/null +++ b/tests/auto/declarative_core/tst_categorymodel.qml @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "CategoryModel" + + CategoryModel { + id: testModel + } + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + PluginParameter { + name: "initializePlaceData" + value: true + } + ] + } + + Plugin { + id: uninitializedPlugin + } + + Plugin { + id: nonExistantPlugin + name: "nonExistentName" + } + + function test_setAndGet_data() { + return [ + { tag: "plugin", property: "plugin", signal: "pluginChanged", value: testPlugin }, + { tag: "hierarchical", property: "hierarchical", signal: "hierarchicalChanged", value: false, reset: true }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testModel, data); + } + + function test_hierarchicalModel() { + var modelSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + var categoryModel = Qt.createQmlObject('import QtQuick 2.0; import QtLocation 5.3;' + + 'VisualDataModel { model: CategoryModel {} delegate: Item {} }', + testCase, "VisualDataModel"); + + modelSpy.target = categoryModel.model; + modelSpy.signalName = "statusChanged"; + + compare(categoryModel.model.status, CategoryModel.Null); + compare(categoryModel.count, 0); + + + // set the plugin + categoryModel.model.plugin = testPlugin; + categoryModel.model.update(); + tryCompare(categoryModel.model, "status", CategoryModel.Loading); + compare(modelSpy.count, 1); + + tryCompare(categoryModel.model, "status", CategoryModel.Ready); + compare(modelSpy.count, 2); + compare(categoryModel.model.errorString(), ""); + + var expectedNames = [ "Accommodation", "Park" ]; + + compare(categoryModel.count, expectedNames.length); + + for (var i = 0; i < expectedNames.length; ++i) { + var category = categoryModel.model.data(categoryModel.modelIndex(i), + CategoryModel.CategoryRole); + compare(category.name, expectedNames[i]); + } + + + // check that "Accommodation" has children + categoryModel.rootIndex = categoryModel.modelIndex(0); + + expectedNames = [ "Camping", "Hotel", "Motel" ]; + + compare(categoryModel.count, expectedNames.length); + + for (i = 0; i < expectedNames.length; ++i) { + category = categoryModel.model.data(categoryModel.modelIndex(i), + CategoryModel.CategoryRole); + compare(category.name, expectedNames[i]); + + var parentCategory = categoryModel.model.data(categoryModel.modelIndex(i), + CategoryModel.ParentCategoryRole); + compare(parentCategory.name, "Accommodation"); + } + + categoryModel.rootIndex = categoryModel.parentModelIndex(); + + compare(categoryModel.count, 2); + + + // check that "Park" has no children + categoryModel.rootIndex = categoryModel.modelIndex(1); + + compare(categoryModel.count, 0); + + categoryModel.rootIndex = categoryModel.parentModelIndex(); + + + // clean up + categoryModel.model.plugin = null; + categoryModel.model.update(); + + // check that the model is empty when an error is encountered + tryCompare(categoryModel, "count", 0); + compare(categoryModel.model.status, CategoryModel.Error); + } + + function test_flatModel() { + var modelSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + var categoryModel = Qt.createQmlObject('import QtQuick 2.0; import QtLocation 5.3;' + + 'VisualDataModel { model: CategoryModel {} delegate: Item {} }', + testCase, "VisualDataModel"); + + modelSpy.target = categoryModel.model; + modelSpy.signalName = "statusChanged"; + + compare(categoryModel.model.status, CategoryModel.Null); + compare(categoryModel.count, 0); + + + // set the plugin + categoryModel.model.hierarchical = false; + categoryModel.model.plugin = testPlugin; + + categoryModel.model.update(); + tryCompare(categoryModel.model, "status", CategoryModel.Loading); + compare(modelSpy.count, 1); + + tryCompare(categoryModel.model, "status", CategoryModel.Ready); + compare(modelSpy.count, 2); + + var expectedNames = [ "Accommodation", "Camping", "Hotel", "Motel", "Park" ]; + + compare(categoryModel.count, expectedNames.length); + + for (var i = 0; i < expectedNames.length; ++i) { + var category = categoryModel.model.data(categoryModel.modelIndex(i), + CategoryModel.CategoryRole); + var name = categoryModel.model.data(categoryModel.modelIndex(i), 0); // DisplayRole + + compare(name, expectedNames[i]); + compare(category.name, expectedNames[i]); + } + + + // check that no category has children + for (i = 0; i < categoryModel.count; ++i) { + categoryModel.rootIndex = categoryModel.modelIndex(i); + + compare(categoryModel.count, 0); + + categoryModel.rootIndex = categoryModel.parentModelIndex(); + } + + + // clean up + categoryModel.model.hierarchical = true; + categoryModel.model.plugin = null; + + + // check that the model is empty when an error is encountered + categoryModel.model.update(); + tryCompare(categoryModel, "count", 0); + compare(categoryModel.model.status, CategoryModel.Error); + } + + function test_error() { + var testModel = Qt.createQmlObject('import QtLocation 5.3; CategoryModel {}', testCase, "CategoryModel"); + + var statusChangedSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + statusChangedSpy.target = testModel; + statusChangedSpy.signalName = "statusChanged"; + + //try updating without a plugin instance + testModel.update(); + tryCompare(statusChangedSpy, "count", 2); + compare(testModel.status, CategoryModel.Error); + statusChangedSpy.clear(); + //Aside: there is some difficulty in checking the transition to the Loading state + //since the model transitions from Loading to Error before the next event loop + //iteration. + + //try updating with an uninitialized plugin instance. + testModel.plugin = uninitializedPlugin; + testModel.update(); + tryCompare(statusChangedSpy, "count", 2); + compare(testModel.status, CategoryModel.Error); + statusChangedSpy.clear(); + + //try searching with plugin a instance + //that has been provided a non-existent name + testModel.plugin = nonExistantPlugin; + testModel.update(); + tryCompare(statusChangedSpy, "count", 2); + compare(testModel.status, CategoryModel.Error); + } +} diff --git a/tests/auto/declarative_core/tst_contactdetail.qml b/tests/auto/declarative_core/tst_contactdetail.qml new file mode 100644 index 0000000..a91c19f --- /dev/null +++ b/tests/auto/declarative_core/tst_contactdetail.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "ContactDetail" + + ContactDetail { id: emptyContactDetail } + + function test_empty() { + compare(emptyContactDetail.label, ""); + compare(emptyContactDetail.value, ""); + } + + ContactDetail { + id: qmlContactDetail + + label: "Phone" + value: "12345" + } + + function test_qmlConstructedContactDetail() { + compare(qmlContactDetail.label, "Phone"); + compare(qmlContactDetail.value, "12345"); + } + + ContactDetail { + id: testContactDetail + } + + function test_setAndGet_data() { + return [ + { tag: "label", property: "label", signal: "labelChanged", value: "Phone", reset: "" }, + { tag: "value", property: "value", signal: "valueChanged", value: "12345", reset: "" }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testContactDetail, data); + } +} diff --git a/tests/auto/declarative_core/tst_coordinate.qml b/tests/auto/declarative_core/tst_coordinate.qml new file mode 100644 index 0000000..79ff8d0 --- /dev/null +++ b/tests/auto/declarative_core/tst_coordinate.qml @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtPositioning 5.5 + +Item { + id: item + + property variant empty: QtPositioning.coordinate() + property variant base: QtPositioning.coordinate(1.0, 1.0, 5.0) + property variant zero: QtPositioning.coordinate(0, 0) + property variant plusone: QtPositioning.coordinate(0, 1) + property variant minusone: QtPositioning.coordinate(0, -1) + property variant north: QtPositioning.coordinate(3, 0) + + SignalSpy { id: coordSpy; target: item; signalName: "baseChanged" } + + property variant inside: QtPositioning.coordinate(0.5, 0.5) + property variant outside: QtPositioning.coordinate(2, 2) + property variant tl: QtPositioning.coordinate(1, 0) + property variant br: QtPositioning.coordinate(0, 1) + property variant box: QtPositioning.rectangle(tl, br) + + + Address { + id: validTestAddress + street: "53 Brandl St" + city: "Eight Mile Plains" + country: "Australia" + countryCode: "AUS" + } + + Location { + id: testLocation + coordinate: inside + boundingBox: box + address: validTestAddress + } + + Location { + id: invalidLocation + } + + + Item { + id: coordinateItem + property variant coordinate + property int animationDuration: 100 + property var coordinateList: [] + property int coordinateCount: 0 + + CoordinateAnimation { + id: coordinateAnimation + target: coordinateItem + property: "coordinate" + duration: coordinateItem.animationDuration + } + onCoordinateChanged: { + if (!coordinateList) { + coordinateList = [] + } + coordinateList[coordinateCount] = QtPositioning.coordinate(coordinate.latitude,coordinate.longitude) + coordinateCount++ + } + + SignalSpy { id: coordinateAnimationStartSpy; target: coordinateAnimation; signalName: "started" } + SignalSpy { id: coordinateAnimationStopSpy; target: coordinateAnimation; signalName: "stopped" } + SignalSpy { id: coordinateAnimationDirectionSpy; target: coordinateAnimation; signalName: "directionChanged" } + } + + TestCase { + name: "GeoLocation" + + function test_Location_complete() + { + compare (testLocation.coordinate.longitude, inside.longitude) + compare (testLocation.coordinate.latitude, inside.latitude) + + compare (testLocation.boundingBox.contains(inside), true) + compare (testLocation.boundingBox.contains(outside), false) + compare (testLocation.boundingBox.bottomRight.longitude, br.longitude) + compare (testLocation.boundingBox.bottomRight.latitude, br.latitude) + compare (testLocation.boundingBox.topLeft.longitude, tl.longitude) + compare (testLocation.boundingBox.topLeft.latitude, tl.latitude) + + compare (testLocation.address.country, "Australia") + compare (testLocation.address.countryCode, "AUS") + compare (testLocation.address.city, "Eight Mile Plains") + compare (testLocation.address.street, "53 Brandl St") + } + + function test_Location_invalid() + { + compare(invalidLocation.coordinate.isValid, false) + compare(invalidLocation.boundingBox.isEmpty, true) + compare(invalidLocation.boundingBox.isValid, false) + compare(invalidLocation.address.city, "") + } + } + + TestCase { + name: "Coordinate" + + function test_validity() + { + compare(empty.isValid, false) + + empty.longitude = 0.0; + empty.latitude = 0.0; + + compare(empty.isValid, true) + } + + function test_accessors() + { + compare(base.longitude, 1.0) + compare(base.latitude, 1.0) + compare(base.altitude, 5.0) + + coordSpy.clear(); + + base.longitude = 2.0; + base.latitude = 3.0; + base.altitude = 6.0; + + compare(base.longitude, 2.0) + compare(base.latitude, 3.0) + compare(base.altitude, 6.0) + compare(coordSpy.count, 3) + } + + function test_comparison_data() + { + return [ + { tag: "empty", coord1: empty, coord2: QtPositioning.coordinate(), result: true }, + { tag: "zero", coord1: zero, coord2: QtPositioning.coordinate(0, 0), result: true }, + { tag: "plusone", coord1: plusone, coord2: QtPositioning.coordinate(0, 1), result: true }, + { tag: "minusone", coord1: minusone, coord2: QtPositioning.coordinate(0, -1), result: true }, + { tag: "north", coord1: north, coord2: QtPositioning.coordinate(3, 0), result: true }, + { tag: "lat,long.alt", coord1: QtPositioning.coordinate(1.1, 2.2, 3.3), coord2: QtPositioning.coordinate(1.1, 2.2, 3.3), result: true }, + { tag: "not equal1", coord1: plusone, coord2: minusone, result: false }, + { tag: "not equal2", coord1: plusone, coord2: north, result: false } + ] + } + + function test_comparison(data) + { + compare(data.coord1 === data.coord2, data.result) + compare(data.coord1 !== data.coord2, !data.result) + compare(data.coord1 == data.coord2, data.result) + compare(data.coord1 != data.coord2, !data.result) + } + + function test_distance() + { + compare(zero.distanceTo(plusone), zero.distanceTo(minusone)) + compare(2*plusone.distanceTo(zero), plusone.distanceTo(minusone)) + compare(zero.distanceTo(plusone) > 0, true) + } + + function test_azimuth() + { + compare(zero.azimuthTo(north), 0) + compare(zero.azimuthTo(plusone), 90) + compare(zero.azimuthTo(minusone), 270) + compare(minusone.azimuthTo(plusone), 360 - plusone.azimuthTo(minusone)) + } + + function test_atDistanceAndAzimuth() + { + // 112km is approximately one degree of arc + + var coord_0d = zero.atDistanceAndAzimuth(112000, 0) + compare(coord_0d.latitude > 0.95, true) + compare(coord_0d.latitude < 1.05, true) + compare(coord_0d.longitude < 0.05, true) + compare(coord_0d.longitude > -0.05, true) + compare(zero.distanceTo(coord_0d), 112000) + compare(zero.azimuthTo(coord_0d), 0) + + var coord_90d = zero.atDistanceAndAzimuth(112000, 90) + compare(coord_90d.longitude > 0.95, true) + compare(coord_90d.longitude < 1.05, true) + compare(coord_90d.latitude < 0.05, true) + compare(coord_90d.latitude > -0.05, true) + compare(zero.distanceTo(coord_90d), 112000) + compare(zero.azimuthTo(coord_90d), 90) + + var coord_30d = zero.atDistanceAndAzimuth(20000, 30) + compare(coord_30d.longitude > 0, true) + compare(coord_30d.latitude > 0, true) + compare(zero.distanceTo(coord_30d), 20000) + compare(zero.azimuthTo(coord_30d), 30) + + var coord_30d2 = coord_30d.atDistanceAndAzimuth(200, 30) + compare(zero.distanceTo(coord_30d2), 20200) + } + } + + TestCase { + name: "CoordinateAnimation" + + function init() + { + coordinateAnimation.stop() + coordinateAnimationStartSpy.clear() + coordinateAnimationStopSpy.clear() + coordinateAnimationDirectionSpy.clear() + coordinateAnimation.from = QtPositioning.coordinate(50,50) + coordinateAnimation.to = QtPositioning.coordinate(50,50) + coordinateAnimation.direction = CoordinateAnimation.Shortest + coordinateItem.coordinate = QtPositioning.coordinate(50,50) + coordinateItem.coordinateList = [] + coordinateItem.coordinateCount = 0 + } + + function initTestCase() + { + compare(coordinateAnimation.direction, CoordinateAnimation.Shortest) + compare(coordinateAnimationDirectionSpy.count,0) + coordinateAnimation.direction = CoordinateAnimation.Shortest + compare(coordinateAnimationDirectionSpy.count,0) + coordinateAnimation.direction = CoordinateAnimation.West + compare(coordinateAnimationDirectionSpy.count,1) + coordinateAnimation.direction = CoordinateAnimation.East + compare(coordinateAnimationDirectionSpy.count,2) + } + + function toMercator(coord) + { + var pi = Math.PI + var lon = coord.longitude / 360.0 + 0.5; + + var lat = coord.latitude; + lat = 0.5 - (Math.log(Math.tan((pi / 4.0) + (pi / 2.0) * lat / 180.0)) / pi) / 2.0; + lat = Math.max(0.0, lat); + lat = Math.min(1.0, lat); + + return {'latitude': lat, 'longitude': lon}; + } + + function coordinate_animation(from, to, movingEast) + { + var fromMerc = toMercator(from) + var toMerc = toMercator(to) + var delta = (toMerc.latitude - fromMerc.latitude) / (toMerc.longitude - fromMerc.longitude) + + compare(coordinateItem.coordinateList.length, 0); + coordinateAnimation.from = from + coordinateAnimation.to = to + coordinateAnimation.start() + tryCompare(coordinateAnimationStartSpy,"count",1) + tryCompare(coordinateAnimationStopSpy,"count",1) + + //check correct start position + compare(coordinateItem.coordinateList[0], from) + //check correct end position + compare(coordinateItem.coordinateList[coordinateItem.coordinateList.length - 1],to) + + var i + var lastLongitude + for (i in coordinateItem.coordinateList) { + var coordinate = coordinateItem.coordinateList[i] + var mercCoordinate = toMercator(coordinate) + + //check that coordinates from the animation is along a straight line between from and to + var estimatedLatitude = fromMerc.latitude + (mercCoordinate.longitude - fromMerc.longitude) * delta + verify(mercCoordinate.latitude - estimatedLatitude < 0.00000000001); + + //check that each step has moved in the right direction + + if (lastLongitude) { + if (movingEast) { + if (coordinate.longitude > 0 && lastLongitude < 0) + verify(coordinate.longitude < lastLongitude + 360) + else + verify(coordinate.longitude < lastLongitude) + } else { + if (coordinate.longitude < 0 && lastLongitude > 0) + verify(coordinate.longitude + 360 > lastLongitude) + else + verify(coordinate.longitude > lastLongitude) + } + } + lastLongitude = coordinate.longitude + } + } + + function test_default_coordinate_animation() + { + //shortest + coordinate_animation(QtPositioning.coordinate(58.0,12.0), + QtPositioning.coordinate(62.0,24.0), + false) + } + + function test_east_direction_coordinate_animation(data) + { + coordinateAnimation.direction = CoordinateAnimation.East + coordinate_animation(data.from, + data.to, + true) + } + + function test_east_direction_coordinate_animation_data() + { + return [ + { from: QtPositioning.coordinate(58.0,24.0), to: QtPositioning.coordinate(58.0,12.0) }, + { from: QtPositioning.coordinate(58.0,12.0), to: QtPositioning.coordinate(58.0,24.0) }, + ] + } + + + function test_west_direction_coordinate_animation(data) + { + coordinateAnimation.direction = CoordinateAnimation.West + coordinate_animation(data.from, + data.to, + false) + } + + function test_west_direction_coordinate_animation_data() + { + return [ + { from: QtPositioning.coordinate(58.0,24.0),to: QtPositioning.coordinate(58.0,12.0) }, + { from: QtPositioning.coordinate(58.0,12.0),to: QtPositioning.coordinate(58.0,24.0) }, + ] + } + + + } +} diff --git a/tests/auto/declarative_core/tst_editorialmodel.qml b/tests/auto/declarative_core/tst_editorialmodel.qml new file mode 100644 index 0000000..4cb38e5 --- /dev/null +++ b/tests/auto/declarative_core/tst_editorialmodel.qml @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "EditorialModel" + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + PluginParameter { + name: "initializePlaceData" + value: true + } + ] + } + + EditorialModel { + id: testModel + } + + Place { + id: testPlace + name: "Test Place" + } + + Place { + id: parkViewHotel + placeId: "4dcc74ce-fdeb-443e-827c-367438017cf1" + plugin: testPlugin + } + + Place { + id: seaViewHotel + placeId: "8f72057a-54b2-4e95-a7bb-97b4d2b5721e" + plugin: testPlugin + } + + function test_setAndGet_data() { + return [ + { tag: "place", property: "place", signal: "placeChanged", value: testPlace }, + { tag: "batchSize", property: "batchSize", signal: "batchSizeChanged", value: 10, reset: 1 }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testModel, data); + } + + function test_consecutive_fetch_data() { + return [ + { tag: "batchSize 1", batchSize: 1 }, + { tag: "batchSize 2", batchSize: 2 }, + { tag: "batchSize 5", batchSize: 5 }, + { tag: "batchSize 10", batchSize: 10 }, + ]; + } + + function test_consecutive_fetch(data) { + var expectedEditorials = [ + { + "title": "Editorial 1", + "text": "Editorial 1 Text", + "language": "en" + }, + { + "title": "Editorial 2", + "text": "Editorial 2 Text", + "language": "en" + }, + { + "title": "Editorial 3", + "text": "Editorial 3 Text", + "language": "en" + }, + { + "title": "", + "text": "", + "language": "", + }, + { + "title": "Editorial 5", + "text": "Editorial 5 Text", + "language": "en" + } + ] + + var model = createModel(); + Utils.testConsecutiveFetch(testCase, model, parkViewHotel, expectedEditorials, data); + model.destroy(); + } + + function test_reset() { + var model = createModel(); + Utils.testReset(testCase, model, parkViewHotel); + model.destroy(); + } + + function test_fetch_data() { + return [ + { + tag: "fetch all editorials in a single batch", + model: createModel(), + batchSize: 10, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + }, + { + tag: "fetch from a place with no editorials", + model: createModel(), + batchSize: 1, + place: seaViewHotel, + expectedTotalCount: 0, + expectedCount: 0 + }, + { + tag: "fetch with batch size one less than the total", + model: createModel(), + batchSize: 4, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 4 + }, + { + tag: "fetch with batch size equal to the total", + model: createModel(), + batchSize: 5, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + }, + { + tag: "fetch with batch size larger than the total", + model: createModel(), + batchSize: 6, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + } + ] + } + + function test_fetch(data) { + Utils.testFetch(testCase, data); + data.model.destroy(); + } + + function createModel() { + return Qt.createQmlObject('import QtLocation 5.3; EditorialModel {}', + testCase, "editorialModel"); + } +} diff --git a/tests/auto/declarative_core/tst_geocoding.qml b/tests/auto/declarative_core/tst_geocoding.qml new file mode 100644 index 0000000..1eadf87 --- /dev/null +++ b/tests/auto/declarative_core/tst_geocoding.qml @@ -0,0 +1,641 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import QtPositioning 5.2 + +Item { + Plugin { id: testPlugin1; name: "qmlgeo.test.plugin"; allowExperimental: true} + Plugin { id: errorPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true + parameters: [ + PluginParameter { name: "error"; value: "1"}, + PluginParameter { name: "errorString"; value: "This error was expected. No worries !"} + ] + } + + + property variant coordinate1: QtPositioning.coordinate(51, 41) + property variant coordinate2: QtPositioning.coordinate(52, 42) + property variant coordinate3: QtPositioning.coordinate(53, 43) + property variant emptyCoordinate: QtPositioning.coordinate() + + property variant boundingBox1: QtPositioning.rectangle(coordinate1, coordinate2) + property variant boundingBox2: QtPositioning.rectangle(coordinate1, coordinate3) + property variant boundingCircle1: QtPositioning.circle(coordinate1, 100) + property variant boundingCircle2: QtPositioning.circle(coordinate2, 100) + + property variant emptyBox: QtPositioning.rectangle() + + GeocodeModel {id: emptyModel} + + Address {id: emptyAddress} + SignalSpy {id: querySpy; target: emptyModel; signalName: "queryChanged"} + SignalSpy {id: autoUpdateSpy; target: emptyModel; signalName: "autoUpdateChanged"} + SignalSpy {id: pluginSpy; target: emptyModel ; signalName: "pluginChanged"} + SignalSpy {id: boundsSpy; target: emptyModel; signalName: "boundsChanged"} + SignalSpy {id: limitSpy; target: emptyModel; signalName: "limitChanged"} + SignalSpy {id: offsetSpy; target: emptyModel; signalName: "offsetChanged"} + + TestCase { + id: testCase1 + name: "GeocodeModel" + function test_model_defaults_and_setters() { + // Query: address + compare (querySpy.count, 0) + emptyModel.query = address1 + compare (querySpy.count, 1) + compare (emptyModel.query.street, address1.street) + emptyModel.query = address1 + compare (querySpy.count, 1) + compare (emptyModel.query.street, address1.street) + // Query: coordinate + emptyModel.query = coordinate1 + compare (querySpy.count, 2) + compare (emptyModel.query.latitude, coordinate1.latitude) + emptyModel.query = coordinate1 + compare (querySpy.count, 2) + compare (emptyModel.query.latitude, coordinate1.latitude) + // Query: string + emptyModel.query = "Kuortane, Finland" + compare (querySpy.count, 3) + compare (emptyModel.query, "Kuortane, Finland") + emptyModel.query = "Kuortane, Finland" + compare (querySpy.count, 3) + compare (emptyModel.query, "Kuortane, Finland") + + // limit and offset + compare (limitSpy.count, 0) + compare (offsetSpy.count, 0) + compare(emptyModel.limit, -1) + compare(emptyModel.offset, 0) + emptyModel.limit = 2 + compare (limitSpy.count, 1) + emptyModel.limit = 2 + compare (limitSpy.count, 1) + emptyModel.offset = 10 + compare (offsetSpy.count, 1) + emptyModel.offset = 10 + compare (offsetSpy.count, 1) + + // bounding box + compare(boundsSpy.count, 0) + emptyModel.bounds = boundingBox1 + compare(boundsSpy.count, 1) + compare(emptyModel.bounds.topLeft.latitude, boundingBox1.topLeft.latitude) + compare(emptyModel.bounds.bottomRight.longitude, boundingBox1.bottomRight.longitude) + emptyModel.bounds = boundingBox1 + compare(boundsSpy.count, 1) + compare(emptyModel.bounds.topLeft.latitude, boundingBox1.topLeft.latitude) + compare(emptyModel.bounds.bottomRight.longitude, boundingBox1.bottomRight.longitude) + emptyModel.bounds = boundingBox2 + compare(boundsSpy.count, 2) + compare(emptyModel.bounds.topLeft.latitude, boundingBox2.topLeft.latitude) + compare(emptyModel.bounds.bottomRight.longitude, boundingBox2.bottomRight.longitude) + emptyModel.bounds = QtPositioning.rectangle(); + compare(boundsSpy.count, 3) + + + // bounding circle + boundsSpy.clear() + emptyModel.bounds = boundingCircle1 + compare(boundsSpy.count, 1) + compare(emptyModel.bounds.center.latitude, coordinate1.latitude) + emptyModel.bounds = boundingCircle1 + compare(boundsSpy.count, 1) + compare(emptyModel.bounds.center.latitude, coordinate1.latitude) + emptyModel.bounds = boundingCircle2 + compare(boundsSpy.count, 2) + compare(emptyModel.bounds.center.latitude, coordinate2.latitude) + var dynamicCircle = QtPositioning.circle(QtPositioning.coordinate(8, 9)); + emptyModel.bounds = dynamicCircle + compare(boundsSpy.count, 3) + compare(emptyModel.bounds.center.latitude, dynamicCircle.center.latitude) + + // status + compare (emptyModel.status, GeocodeModel.Null) + + // error + compare (emptyModel.errorString, "") + compare (emptyModel.error, GeocodeModel.NoError) + + // count + compare( emptyModel.count, 0) + + // auto update + compare (autoUpdateSpy.count, 0) + compare (emptyModel.autoUpdate, false) + emptyModel.autoUpdate = true + compare (emptyModel.autoUpdate, true) + compare (autoUpdateSpy.count, 1) + emptyModel.autoUpdate = true + compare (emptyModel.autoUpdate, true) + compare (autoUpdateSpy.count, 1) + + // mustn't crash even we don't have plugin + emptyModel.update() + + // Plugin + compare(pluginSpy.count, 0) + emptyModel.plugin = testPlugin1 + compare(pluginSpy.count, 1) + compare(emptyModel.plugin, testPlugin1) + emptyModel.plugin = testPlugin1 + compare(pluginSpy.count, 1) + emptyModel.plugin = errorPlugin + compare(pluginSpy.count, 2) + } + // Test that model acts gracefully when plugin is not set or is invalid + // (does not support geocoding) + GeocodeModel {id: errorModel; plugin: errorPlugin} + GeocodeModel {id: errorModelNoPlugin} + SignalSpy {id: countInvalidSpy; target: errorModel; signalName: "countChanged"} + SignalSpy {id: errorSpy; target: errorModel; signalName: "errorChanged"} + function test_error_plugin() { + // test plugin not set + compare(errorModelNoPlugin.error,GeocodeModel.NoError) + errorModelNoPlugin.update() + compare(errorModelNoPlugin.error,GeocodeModel.EngineNotSetError) + console.log(errorModelNoPlugin.errorString) + + //plugin set but otherwise not offering anything + compare(errorModel.error,GeocodeModel.EngineNotSetError) + compare(errorModel.errorString,"This error was expected. No worries !") + errorSpy.clear() + errorModel.update() + compare(errorModel.error,GeocodeModel.EngineNotSetError) + compare(errorModel.errorString,qsTr("Cannot geocode, geocode manager not set.")) + compare(errorSpy.count, 1) + errorSpy.clear() + errorModel.cancel() + compare(errorModel.error,GeocodeModel.NoError) + compare(errorModel.errorString,"") + compare(errorSpy.count, 1) + errorSpy.clear() + errorModel.reset() + compare(errorModel.error,GeocodeModel.NoError) + compare(errorModel.errorString,"") + compare(errorSpy.count, 0) + errorSpy.clear() + errorModel.update() + compare(errorModel.error,GeocodeModel.EngineNotSetError) + compare(errorModel.errorString,qsTr("Cannot geocode, geocode manager not set.")) + compare(errorSpy.count, 1) + errorSpy.clear() + var location = errorModel.get(-1) + compare(location, null) + } + + } + Address {id: address1; street: "wellknown street"; city: "expected city"; county: "2"} + Address {id: errorAddress1; street: "error"; county: "2"} // street is the error reason + + property variant rcoordinate1: QtPositioning.coordinate(51, 2) + property variant errorCoordinate1: QtPositioning.coordinate(73, 2) // (latiude mod 70) is the error code + property variant slackCoordinate1: QtPositioning.coordinate(60, 3) + Address {id: slackAddress1; street: "Slacker st"; city: "Lazy town"; county: "4"} + + property variant automaticCoordinate1: QtPositioning.coordinate(60, 3) + Address {id: automaticAddress1; street: "Auto st"; city: "Detroit"; county: "4"} + + Plugin { + id: testPlugin2; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "supported"; value: true}, + PluginParameter { name: "finishRequestImmediately"; value: true}, + PluginParameter { name: "validateWellKnownValues"; value: true} + ] + } + + Plugin { + id: immediatePlugin; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "supported"; value: true}, + PluginParameter { name: "finishRequestImmediately"; value: true}, + PluginParameter { name: "validateWellKnownValues"; value: false} + ] + } + + Plugin { + id: slackPlugin; + allowExperimental: true + name: "qmlgeo.test.plugin" + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "supported"; value: true}, + PluginParameter { name: "finishRequestImmediately"; value: false}, + PluginParameter { name: "validateWellKnownValues"; value: false} + ] + } + + Plugin { + id: autoPlugin; + allowExperimental: true + name: "qmlgeo.test.plugin" + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "supported"; value: true}, + PluginParameter { name: "finishRequestImmediately"; value: false}, + PluginParameter { name: "validateWellKnownValues"; value: false} + ] + } + + GeocodeModel {id: testModel; plugin: testPlugin2} + SignalSpy {id: locationsSpy; target: testModel; signalName: "locationsChanged"} + SignalSpy {id: countSpy; target: testModel; signalName: "countChanged"} + SignalSpy {id: testQuerySpy; target: testModel; signalName: "queryChanged"} + SignalSpy {id: testStatusSpy; target: testModel; signalName: "statusChanged"} + + GeocodeModel {id: slackModel; plugin: slackPlugin; } + SignalSpy {id: locationsSlackSpy; target: slackModel; signalName: "locationsChanged"} + SignalSpy {id: countSlackSpy; target: slackModel; signalName: "countChanged"} + SignalSpy {id: querySlackSpy; target: slackModel; signalName: "queryChanged"} + SignalSpy {id: errorStringSlackSpy; target: slackModel; signalName: "errorChanged"} + SignalSpy {id: errorSlackSpy; target: slackModel; signalName: "errorChanged"} + SignalSpy {id: pluginSlackSpy; target: slackModel; signalName: "pluginChanged"} + + GeocodeModel {id: immediateModel; plugin: immediatePlugin} + SignalSpy {id: locationsImmediateSpy; target: immediateModel; signalName: "locationsChanged"} + SignalSpy {id: countImmediateSpy; target: immediateModel; signalName: "countChanged"} + SignalSpy {id: queryImmediateSpy; target: immediateModel; signalName: "queryChanged"} + SignalSpy {id: statusImmediateSpy; target: immediateModel; signalName: "statusChanged"} + SignalSpy {id: errorStringImmediateSpy; target: immediateModel; signalName: "errorChanged"} + SignalSpy {id: errorImmediateSpy; target: immediateModel; signalName: "errorChanged"} + + GeocodeModel {id: automaticModel; plugin: autoPlugin; query: automaticAddress1; autoUpdate: true} + SignalSpy {id: automaticLocationsSpy; target: automaticModel; signalName: "locationsChanged"} + + TestCase { + name: "GeocodeModelGeocoding" + function clear_slack_model() { + slackModel.reset() + locationsSlackSpy.clear() + countSlackSpy.clear() + querySlackSpy.clear() + errorStringSlackSpy.clear() + errorSlackSpy.clear() + slackModel.limit = -1 + slackModel.offset = 0 + } + function clear_immediate_model() { + immediateModel.reset() + locationsImmediateSpy.clear() + countImmediateSpy.clear() + queryImmediateSpy.clear() + errorStringImmediateSpy.clear() + errorImmediateSpy.clear() + statusImmediateSpy.clear() + immediateModel.limit = -1 + immediateModel.offset = 0 + } + function test_reset() { + clear_immediate_model(); + immediateModel.query = errorAddress1 + immediateModel.update() + compare (immediateModel.errorString, errorAddress1.street) + compare (immediateModel.error, GeocodeModel.CommunicationError) + compare (immediateModel.count, 0) + compare (statusImmediateSpy.count, 2) + compare (immediateModel.status, GeocodeModel.Error) + immediateModel.reset() + compare (immediateModel.errorString, "") + compare (immediateModel.error, GeocodeModel.NoError) + compare (immediateModel.status, GeocodeModel.Null) + // Check that ongoing req is aborted + clear_slack_model() + slackModel.query = slackAddress1 + slackAddress1.county = "5" + slackModel.update() + tryCompare(countSlackSpy, "count", 0) + compare (locationsSlackSpy.count, 0) + compare (slackModel.count, 0) + slackModel.reset() + tryCompare(countSlackSpy, "count", 0) + compare (locationsSlackSpy.count, 0) + compare (slackModel.count, 0) + // Check that results are cleared + slackModel.update() + tryCompare(slackModel, "count", 5) + slackModel.reset() + compare (slackModel.count, 0) + // Check that changing plugin resets any ongoing requests + clear_slack_model() + slackModel.query = slackAddress1 + slackAddress1.county = "7" + compare (pluginSlackSpy.count, 0) + slackModel.update() + tryCompare(countSlackSpy, "count", 0) + slackModel.plugin = errorPlugin + tryCompare(countSlackSpy, "count", 0) + compare (pluginSlackSpy.count, 1) + // switch back and check that works + slackModel.plugin = slackPlugin + compare (pluginSlackSpy.count, 2) + slackModel.update() + tryCompare(countSlackSpy, "count", 0) + tryCompare(countSlackSpy, "count", 1) + } + function test_error_geocode() { + // basic immediate geocode error + clear_immediate_model() + immediateModel.query = errorAddress1 + immediateModel.update() + compare (errorStringImmediateSpy.count, 1) + compare (immediateModel.errorString, errorAddress1.street) + compare (immediateModel.error, GeocodeModel.CommunicationError) // county of the address (2) + compare (immediateModel.count, 0) + compare (statusImmediateSpy.count, 2) + compare (immediateModel.status, GeocodeModel.Error) + // basic delayed geocode error + clear_slack_model() + slackModel.query = errorAddress1 + errorAddress1.street = "error code 2" + slackModel.update() + compare (errorStringSlackSpy.count, 0) + compare (errorSlackSpy.count, 0) + tryCompare (errorStringSlackSpy, "count", 1) + tryCompare (errorSlackSpy, "count", 1) + compare (slackModel.errorString, errorAddress1.street) + compare (slackModel.error, GeocodeModel.CommunicationError) + compare (slackModel.count, 0) + // Check that we recover + slackModel.query = address1 + slackModel.update() + tryCompare(countSlackSpy, "count", 1) + compare (slackModel.count, 2) + compare (errorStringSlackSpy.count, 2) + compare (errorSlackSpy.count, 2) + compare (slackModel.errorString, "") + compare (slackModel.error, GeocodeModel.NoError) + } + + function test_error_reverse_geocode() { + // basic immediate geocode error + clear_immediate_model() + immediateModel.query = errorCoordinate1 + immediateModel.update() + if (immediateModel.errorString != "") + compare (errorStringImmediateSpy.count, 1) // the previous error is cleared upon update() + else + compare (errorImmediateSpy.count, 1) + compare (immediateModel.errorString, "error") + compare (immediateModel.error, GeocodeModel.ParseError) + compare (immediateModel.count, 0) + compare (statusImmediateSpy.count, 2) + compare (immediateModel.status, GeocodeModel.Error) + // basic delayed geocode error + clear_slack_model() + slackModel.query = errorCoordinate1 + slackModel.update() + compare (errorStringSlackSpy.count, 0) + compare (errorSlackSpy.count, 0) + if (slackModel.errorString != "") + tryCompare (errorStringSlackSpy, "count", 2) + else + tryCompare (errorStringSlackSpy, "count", 1) + compare (slackModel.errorString, "error") + compare (slackModel.error, GeocodeModel.ParseError) + compare (slackModel.count, 0) + // Check that we recover + slackModel.query = rcoordinate1 + slackModel.update() + tryCompare(countSlackSpy, "count", 1) + compare (slackModel.count, 2) + compare (errorStringSlackSpy.count, 2) + compare (errorSlackSpy.count, 2) + compare (slackModel.errorString, "") + compare (slackModel.error, GeocodeModel.NoError) + } + function test_address_geocode() { + testQuerySpy.clear() + locationsSpy.clear() + testStatusSpy.clear() + testModel.reset() + countSpy.clear() + compare (locationsSpy.count, 0) + compare (testModel.errorString, "") + compare (testModel.error, GeocodeModel.NoError) + compare (testModel.count, 0) + testModel.query = address1 + compare (testQuerySpy.count, 1) + testModel.update() + tryCompare (locationsSpy, "count", 1) // 5 sec + compare (testModel.errorString, "") + compare (testModel.error, GeocodeModel.NoError) + compare (testModel.count, 2) + compare (testQuerySpy.count, 1) + compare (testStatusSpy.count, 2) + compare (testModel.status, GeocodeModel.Ready) + compare (testModel.get(0).address.street, "wellknown street") + compare (testModel.get(0).address.city, "expected city") + } + + function test_freetext_geocode() { + testQuerySpy.clear() + locationsSpy.clear() + testStatusSpy.clear() + testModel.reset() + countSpy.clear() + compare (locationsSpy.count, 0) + compare (testModel.errorString, "") + compare (testModel.error, GeocodeModel.NoError) + compare (testModel.count, 0) + testModel.limit = 5 // number of places echoed back + testModel.offset = 10 // 'county' set in the places + // Test successful case + testModel.query = "Freetext geocode" + compare(testQuerySpy.count, 1) + testModel.update(); + tryCompare (locationsSpy, "count", 1) // 5 sec + tryCompare(countSpy, "count", 1) + tryCompare(testModel, "count", 5) + compare(testModel.get(0).address.county, "10") + // Test error case + testModel.query = "2" // tells plugin to echo error '2' + compare(testQuerySpy.count, 2) + testModel.update(); + tryCompare (locationsSpy, "count", 2) // 5 sec + tryCompare(countSpy, "count", 2) + tryCompare(testModel, "count", 0) + compare(testModel.errorString, "2") + compare (testModel.error, GeocodeModel.CommunicationError) + testModel.reset() + tryCompare(countSpy, "count", 2) + compare (testModel.count, 0) + } + + function test_delayed_freetext_geocode() { + clear_slack_model() + slackModel.limit = 5 // number of places echoed back + slackModel.offset = 10 // 'county' set in the places + // Basic successful case + slackModel.query = "freetext geocode" + compare (querySlackSpy.count, 1) + slackModel.update() + tryCompare(countSlackSpy, "count", 0) + compare (locationsSlackSpy.count, 0) + compare (slackModel.count, 0) + tryCompare(countSlackSpy, "count", 1); //waits up to 5s + compare (slackModel.count, 5) + compare (locationsSlackSpy.count, 1) + // Frequent updates, previous requests are aborted + slackModel.reset() + locationsSlackSpy.clear() + countSlackSpy.clear() + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + tryCompare(countSlackSpy, "count", 1); //waits up to 5s + compare (locationsSlackSpy.count, 1) + compare(slackModel.count, 5) // limit + } + + function test_geocode_auto_updates() { + compare (automaticModel.count, 4) // should be something already + compare (automaticLocationsSpy.count, 1) + // change query and its contents and verify that autoupdate occurs + automaticAddress1.county = 6 + tryCompare(automaticLocationsSpy, "count", 2) + compare (automaticModel.count, 6) + automaticAddress1.street = "The Avenue" + tryCompare(automaticLocationsSpy, "count", 3) + compare (automaticModel.count, 6) + automaticModel.query = automaticCoordinate1 + tryCompare(automaticLocationsSpy, "count", 4) + compare (automaticModel.count, 3) + } + + function test_delayed_geocode() { + // basic delayed response + slackModel.reset() + querySlackSpy.clear() + countSlackSpy.clear() + locationsSlackSpy.clear() + slackModel.query = slackAddress1 + slackAddress1.county = "7" + compare (querySlackSpy.count, 1) + slackModel.update() + tryCompare(countSlackSpy, "count", 0) + compare (locationsSlackSpy.count, 0) + compare (slackModel.count, 0) + tryCompare(countSlackSpy, "count", 1); //waits up to 5s + compare (locationsSlackSpy.count, 1) + compare (slackModel.count, 7) // slackAddress1.county) + // Frequent updates, previous requests are aborted + slackModel.reset() + locationsSlackSpy.clear() + countSlackSpy.clear() + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + tryCompare(countSlackSpy, "count", 1); //waits up to 5s + compare (locationsSlackSpy.count, 1) + compare(slackModel.count, 7) // slackAddress1.county + } + function test_reverse_geocode() { + testModel.reset() + testQuerySpy.clear() + locationsSpy.clear() + testStatusSpy.clear() + countSpy.clear() + compare (testModel.errorString, "") + compare (testModel.error, GeocodeModel.NoError) + compare (testModel.count, 0) + compare (testQuerySpy.count, 0) + testModel.query = rcoordinate1 + compare (testQuerySpy.count, 1) + testModel.update() + tryCompare (locationsSpy, "count", 1) // 5 sec + tryCompare(countSpy, "count", 1) + compare (testModel.errorString, "") + compare (testModel.error, GeocodeModel.NoError) + compare (testModel.count, 2) + testModel.reset() + tryCompare(countSpy, "count", 2) + compare (testModel.count, 0) + } + function test_delayed_reverse_geocode() { + clear_slack_model() + slackModel.query = slackCoordinate1 + compare (querySlackSpy.count, 1) + slackModel.update() + tryCompare(countSlackSpy, "count", 0) + compare (locationsSlackSpy.count, 0) + compare (slackModel.count, 0) + + tryCompare(countSlackSpy, "count", 1); //waits up to 5s + compare (locationsSlackSpy.count, 1) + compare (slackModel.count, 3) // slackCoordinate1.longitude + // Frequent updates, previous requests are aborted + slackModel.reset() + locationsSlackSpy.clear() + countSlackSpy.clear() + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + slackModel.update() + tryCompare(locationsSlackSpy, "count", 0) + compare(countSlackSpy.count, 0) + + tryCompare(countSlackSpy, "count", 1); //waits up to 5s + compare(locationsSlackSpy.count, 1) + compare(slackModel.count, 3) // slackCoordinate1.longitude + } + } +} diff --git a/tests/auto/declarative_core/tst_imagemodel.qml b/tests/auto/declarative_core/tst_imagemodel.qml new file mode 100644 index 0000000..2fa5093 --- /dev/null +++ b/tests/auto/declarative_core/tst_imagemodel.qml @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "ImageModel" + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + PluginParameter { + name: "initializePlaceData" + value: true + } + ] + } + + ImageModel { + id: testModel + } + + Place { + id: testPlace + name: "Test Place" + } + + Place { + id: parkViewHotel + placeId: "4dcc74ce-fdeb-443e-827c-367438017cf1" + plugin: testPlugin + } + + Place { + id: seaViewHotel + placeId: "8f72057a-54b2-4e95-a7bb-97b4d2b5721e" + plugin: testPlugin + } + + function test_setAndGet_data() { + return [ + { tag: "place", property: "place", signal: "placeChanged", value: testPlace }, + { tag: "batchSize", property: "batchSize", signal: "batchSizeChanged", value: 10, reset: 1 }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testModel, data); + } + + function test_consecutive_fetch_data() { + return [ + { tag: "batchSize 1", batchSize: 1 }, + { tag: "batchSize 2", batchSize: 2 }, + { tag: "batchSize 5", batchSize: 5 }, + { tag: "batchSize 10", batchSize: 10 }, + ]; + } + + function test_consecutive_fetch(data) { + var expectedImages = [ + { + "url": "http://somewhere.com/image1.png", + "imageId": "0001", + "mimeType": "image/png" + }, + { + "url": "http://somewhere.com/image2.png", + "imageId": "0002", + "mimeType": "image/png" + }, + { + "url": "http://somewhere.com/image3.png", + "imageId": "0003", + "mimeType": "image/png" + }, + { + "url": "", + "imageId": "", + "mimeType": "" + }, + { + "url": "http://somewhere.com/image5.png", + "imageId": "0005", + "mimeType": "image/png" + } + ] + + var model = createModel(); + Utils.testConsecutiveFetch(testCase, model, parkViewHotel, expectedImages, data); + model.destroy(); + } + + function test_reset() { + var model = createModel(); + Utils.testReset(testCase, model, parkViewHotel); + model.destroy(); + } + + function test_fetch_data() { + return [ + { + tag: "fetch all images in a single batch", + model: createModel(), + batchSize: 10, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + }, + { + tag: "fetch from a place with no images", + model: createModel(), + batchSize: 1, + place: seaViewHotel, + expectedTotalCount: 0, + expectedCount: 0 + }, + { + tag: "fetch with batch size one less than the total", + model: createModel(), + batchSize: 4, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 4 + }, + { + tag: "fetch with batch size equal to the total", + model: createModel(), + batchSize: 5, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + }, + { + tag: "fetch with batch size larger than the total", + model: createModel(), + batchSize: 6, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + } + ] + } + + function test_fetch(data) { + Utils.testFetch(testCase, data); + data.model.destroy(); + } + + function createModel() { + return Qt.createQmlObject('import QtLocation 5.3; ImageModel {}', + testCase, "imageModel"); + } +} diff --git a/tests/auto/declarative_core/tst_place.qml b/tests/auto/declarative_core/tst_place.qml new file mode 100644 index 0000000..bb789b4 --- /dev/null +++ b/tests/auto/declarative_core/tst_place.qml @@ -0,0 +1,627 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import QtPositioning 5.2 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "Place" + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + } + + Place { + id: favoritePlace + name: "Favorite Place" + } + + Place { id: emptyPlace } + + Place { id: emptyPlace2 } + + Place { id: testPlace } + + Place { + id: savePlace + + name: "Test place" + + visibility: Place.DeviceVisibility + + location: Location { + address: Address { + country: "country" + countryCode: "cc" + state: "state" + county: "county" + city: "city" + district: "district" + street: "123 Fake Street" + postalCode: "1234" + } + + coordinate { + latitude: 10 + longitude: 10 + altitude: 100 + } + + boundingBox { + center: QtPositioning.coordinate(10, 10, 100) + width: 100 + height: 100 + } + } + + ratings: Ratings { + average: 3.5 + count: 10 + } + + supplier: Supplier { + name: "Supplier 1" + supplierId: "supplier-id-1" + url: "http://www.example.com/supplier-id-1/" + icon: Icon{ + plugin: testPlugin + Component.onCompleted: { + parameters.singleUrl = "http://www.example.com/supplier-id-1/icon" + } + } + } + + categories: [ + Category { + name: "Category 1" + categoryId: "category-id-1" + plugin: testPlugin + }, + Category { + name: "Category 2" + categoryId: "category-id-2" + plugin: testPlugin + } + ] + + icon: Icon { + Component.onCompleted: { + savePlace.icon.parameters.singleUrl = "http://example.com/test-place.png"; + } + } + } + + Place { + id: dummyPlace + placeId: "487" + name: "dummyPlace" + visibility: Place.PublicVisibility + } + + // compares two places property by property + function compare_place(place1, place2) { + // check simple properties + var simpleProperties = ["name", "placeId", "primaryPhone", "primaryFax", "primaryEmail", + "primaryWebsite", "visibility"]; + for (x in simpleProperties) { + if (place1[simpleProperties[x]] !== place2[simpleProperties[x]]) + return false; + } + + // check categories + if (place1.categories.length !== place2.categories.length) + return false; + for (var i = 0; i < place1.categories.length; ++i) { + // fixme, what if the order of the two lists are not the same + if (place1.categories[i].categoryId !== place2.categories[i].categoryId) + return false; + if (place1.categories[i].name !== place2.categories[i].name) + return false; + } + + // check supplier + if (place1.supplier === null && place2.supplier !== null) + return false; + if (place1.supplier !== null && place2.supplier === null) + return false; + if (place1.supplier !== null && place2.supplier !== null) { + if (place1.supplier.supplierId !== place2.supplier.supplierId) + return false; + if (place1.supplier.name !== place2.supplier.name) + return false; + if (place1.supplier.url !== place2.supplier.url) + return false; + + // check supplier icon + if (place1.supplier.icon === null && place2.supplier.icon !== null) + return false; + if (place1.supplier.icon !== null && place2.supplier.icon === null) + return false; + if (place1.supplier.icon !== null && place2.supplier.icon !== null) { + if (place1.supplier.icon.parameters.keys().length !== place2.supplier.icon.parameters.keys().length) { + return false; + } + + var keys = place1.supplier.icon.parameters.keys() + place2.supplier.icon.parameters.keys(); + for (var i = 0; i < keys.length; ++i) { + if (place1.supplier.icon.parameters[keys[i]] != place2.supplier.icon.parameters[keys[i]]) { + return false; + } + } + + if (place1.supplier.icon.plugin !== place2.supplier.icon.plugin) + return false; + } + } + + // check ratings + if (place1. ratings === null && place2.ratings !== null) + return false; + if (place1.ratings !== null && place2.ratings === null) + return false; + if (place1.ratings !== null && place2.ratings !== null) { + if (place1.ratings.average !== place2.ratings.average) + return false; + if (place1.ratings.count !== place2.ratings.count) + return false; + } + + // check location + if (place1.location === null && place2.location !== null) + return false; + if (place1.location !== null && place2.location === null) + return false; + if (place1.location !== null && place2.location !== null) { + if (place1.location.address.country !== place2.location.address.country) + return false; + if (place1.location.address.countryCode !== place2.location.address.countryCode) + return false; + if (place1.location.address.state !== place2.location.address.state) + return false; + if (place1.location.address.county !== place2.location.address.county) + return false; + if (place1.location.address.city !== place2.location.address.city) + return false; + if (place1.location.address.district !== place2.location.address.district) + return false; + if (place1.location.address.street !== place2.location.address.street) + return false; + if (place1.location.address.postalCode !== place2.location.address.postalCode) + return false; + + if (place1.location.coordinate !== place2.location.coordinate) + return false; + if (place1.location.boundingBox !== place2.location.boundingBox) + return false; + } + + // check icon + if (place1.icon === null && place2.icon !== null) { + return false; + } + if (place1.icon !== null && place2.icon === null) { + return false; + } + if (place1.icon !== null && place2.icon !== null) { + if (place1.icon.plugin !== place2.icon.plugin) { + console.log(place1.icon.plugin + " " + place2.icon.plugin); + return false; + } + + if (place1.icon.parameters.keys().length !== place2.icon.parameters.keys().length) { + return false; + } + + var keys = place1.icon.parameters.keys() + place2.icon.parameters.keys(); + for (var i = 0; i < keys.length; ++i) { + if (place1.icon.parameters[keys[i]] + != place2.icon.parameters[keys[i]]) { + return false; + } + } + } + + // check extended attributes + + return true; + } + + function test_emptyPlace() { + // basic properties + compare(emptyPlace.plugin, null); + compare(emptyPlace.categories.length, 0); + compare(emptyPlace.name, ""); + compare(emptyPlace.placeId, ""); + compare(emptyPlace.detailsFetched, false); + compare(emptyPlace.status, Place.Ready); + compare(emptyPlace.primaryPhone, ""); + compare(emptyPlace.primaryFax, ""); + compare(emptyPlace.primaryEmail, ""); + compare(emptyPlace.primaryWebsite, ""); + compare(emptyPlace.visibility, Place.UnspecifiedVisibility); + compare(emptyPlace.attribution, ""); + + // complex properties + compare(emptyPlace.ratings.average, 0); + compare(emptyPlace.location.address.street, ''); + compare(emptyPlace.location.address.district, ''); + compare(emptyPlace.location.address.city, ''); + compare(emptyPlace.location.address.county, ''); + compare(emptyPlace.location.address.state, ''); + compare(emptyPlace.location.address.country, ''); + + compare(emptyPlace.icon.plugin, null); + + compare(emptyPlace.supplier.name, ''); + compare(emptyPlace.supplier.supplierId, ''); + compare(emptyPlace.supplier.url, ''); + + compare(emptyPlace.supplier.icon.plugin, null); + + compare(emptyPlace.reviewModel.totalCount, -1); + compare(emptyPlace.imageModel.totalCount, -1); + compare(emptyPlace.editorialModel.totalCount, -1); + compare(emptyPlace.categories.length, 0); + + verify(compare_place(emptyPlace, emptyPlace)); + verify(compare_place(emptyPlace, emptyPlace2)); + } + + function test_setAndGet_data() { + return [ + { tag: "name", property: "name", signal: "nameChanged", value: "Test Place", reset: "" }, + { tag: "placeId", property: "placeId", signal: "placeIdChanged", value: "test-place-id-1", reset: "" }, + { tag: "visibility", property: "visibility", signal: "visibilityChanged", value: Place.PublicVisibility, reset: Place.UnspecifiedVisibility }, + { tag: "attribution", property: "attribution", signal: "attributionChanged", value: "Place data from...", reset: "" }, + { tag: "favorite", property: "favorite", signal: "favoriteChanged", value: favoritePlace } + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testPlace, data); + } + + function test_categories() { + var categories = new Array(2); + categories[0] = Qt.createQmlObject('import QtLocation 5.3; Category { categoryId: "cat-id-1"; name: "Category 1" }', testCase, "Category1"); + categories[1] = Qt.createQmlObject('import QtLocation 5.3; Category { categoryId: "cat-id-2"; name: "Category 2" }', testCase, "Category2"); + + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = testPlace; + signalSpy.signalName = "categoriesChanged"; + + // set categories to something new + testPlace.categories = categories; + compare(testPlace.categories.length, categories.length); + + for (var i = 0; i < categories.length; ++i) { + compare(testPlace.categories[i].categoryId, categories[i].categoryId); + compare(testPlace.categories[i].name, categories[i].name); + } + + compare(signalSpy.count, 2); + + // set categories to the same (signal spy should not increase?) + testPlace.categories = categories; + compare(testPlace.categories.length, categories.length); + + for (var i = 0; i < categories.length; ++i) { + compare(testPlace.categories[i].categoryId, categories[i].categoryId); + compare(testPlace.categories[i].name, categories[i].name); + } + + compare(signalSpy.count, 5); // clear + append + append + + // reset by assignment + testPlace.categories = new Array(0); + compare(testPlace.categories.length, 0); + compare(signalSpy.count, 6); + + signalSpy.destroy(); + } + + function test_supplier() { + var supplier = Qt.createQmlObject('import QtLocation 5.3; Supplier { supplierId: "sup-id-1"; name: "Category 1" }', testCase, "Supplier1"); + + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = testPlace; + signalSpy.signalName = "supplierChanged"; + + // set supplier to something new + testPlace.supplier = supplier; + compare(testPlace.supplier, supplier); + + compare(testPlace.supplier.supplierId, supplier.supplierId); + compare(testPlace.supplier.name, supplier.name); + + compare(signalSpy.count, 1); + + // set supplier to the same + testPlace.supplier = supplier; + compare(testPlace.supplier, supplier); + + compare(testPlace.supplier.supplierId, supplier.supplierId); + compare(testPlace.supplier.name, supplier.name); + + compare(signalSpy.count, 1); + + // reset by assignment + testPlace.supplier = null; + compare(testPlace.supplier, null); + compare(signalSpy.count, 2); + + signalSpy.destroy(); + } + + function test_location() { + var location = Qt.createQmlObject('import QtPositioning 5.2; Location { coordinate: QtPositioning.coordinate(10.0, 20.0) }', testCase, "Location1"); + + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = testPlace; + signalSpy.signalName = "locationChanged"; + + testPlace.location = location; + compare(testPlace.location.coordinate.latitude, 10.0); + compare(signalSpy.count, 1); + + testPlace.location = location; + compare(testPlace.location.coordinate.latitude, 10.0); + compare(signalSpy.count, 1); + + testPlace.location = null; + compare(testPlace.location, null); + compare(signalSpy.count, 2); + + location.destroy(); + signalSpy.destroy(); + } + + function test_ratings() { + var ratings = Qt.createQmlObject('import QtLocation 5.3; Ratings { average: 3; count: 100 }', testCase, "Rating1"); + + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = testPlace; + signalSpy.signalName = "ratingsChanged"; + + testPlace.ratings = ratings; + compare(testPlace.ratings.average, 3); + compare(testPlace.ratings.count, 100); + compare(signalSpy.count, 1); + + testPlace.ratings = ratings; + compare(testPlace.ratings.average, 3); + compare(testPlace.ratings.count, 100); + compare(signalSpy.count, 1); + + testPlace.ratings = null; + compare(testPlace.ratings, null); + compare(signalSpy.count, 2); + + ratings.destroy(); + signalSpy.destroy(); + } + + function test_extendedAttributes() { + verify(testPlace.extendedAttributes); + + testPlace.extendedAttributes["foo"] = Qt.createQmlObject('import QtLocation 5.3; PlaceAttribute { text: "Foo"; label: "Foo label" }', testCase, 'PlaceAttribute'); + + verify(testPlace.extendedAttributes.foo); + compare(testPlace.extendedAttributes.foo.text, "Foo"); + compare(testPlace.extendedAttributes.foo.label, "Foo label"); + + testPlace.extendedAttributes["foo"] = null; + verify(!testPlace.extendedAttributes.foo); + } + + function test_contactDetailsProperty() { + verify(testPlace.contactDetails); + + testPlace.contactDetails["phone"] = Qt.createQmlObject('import QtLocation 5.3; ContactDetail { label: "Test Label"; value: "Detail Value" }', testCase, 'ContactDetail'); + + verify(testPlace.contactDetails.phone); + compare(testPlace.contactDetails.phone[0].label, "Test Label"); + compare(testPlace.contactDetails.phone[0].value, "Detail Value"); + + testPlace.contactDetails["phone"] = null; + verify(!testPlace.contactDetails.phone); + } + + function test_saveload() { + // Save a place + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = savePlace; + signalSpy.signalName = "statusChanged"; + + savePlace.plugin = testPlugin; + savePlace.icon.plugin = testPlugin; + savePlace.placeId = "invalid-place-id"; + + savePlace.save(); + + compare(savePlace.status, Place.Saving); + + tryCompare(savePlace, "status", Place.Error); + + // try again without an invalid placeId + savePlace.placeId = ""; + savePlace.save(); + + compare(savePlace.status, Place.Saving); + + tryCompare(savePlace, "status", Place.Ready); + + verify(savePlace.placeId !== ""); + + signalSpy.destroy(); + + + // Read a place + var readPlace = Qt.createQmlObject('import QtLocation 5.3; Place { }', testCase, "test_saveload"); + + signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = readPlace; + signalSpy.signalName = "statusChanged"; + + readPlace.plugin = testPlugin; + + readPlace.getDetails(); + + compare(readPlace.status, Place.Fetching); + tryCompare(readPlace, "status", Place.Error); + + readPlace.placeId = "invalid-id"; + + readPlace.getDetails(); + + compare(readPlace.status, Place.Fetching); + tryCompare(readPlace, "status", Place.Error); + + readPlace.placeId = savePlace.placeId; + + // verify that read place is not currently the same as what we saved + verify(!compare_place(readPlace, savePlace)); + + readPlace.getDetails(); + + compare(readPlace.status, Place.Fetching); + tryCompare(readPlace, "status", Place.Ready); + + // verify that read place is the same as what we saved + verify(compare_place(readPlace, savePlace)); + + signalSpy.destroy(); + + + // Remove a place + var removePlace = Qt.createQmlObject('import QtLocation 5.3; Place { }', testCase, "test_saveload"); + + signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = removePlace; + signalSpy.signalName = "statusChanged"; + + removePlace.plugin = testPlugin; + + removePlace.remove(); + + compare(removePlace.status, Place.Removing); + tryCompare(removePlace, "status", Place.Error); + + removePlace.placeId = "invalid-id"; + + removePlace.remove(); + + compare(removePlace.status, Place.Removing); + tryCompare(removePlace, "status", Place.Error); + + removePlace.placeId = savePlace.placeId; + + removePlace.remove(); + + compare(removePlace.status, Place.Removing); + tryCompare(removePlace, "status", Place.Ready); + + removePlace.getDetails(); + + compare(removePlace.status, Place.Fetching); + tryCompare(removePlace, "status", Place.Error); + + signalSpy.destroy(); + } + + function test_copy() { + var place = Qt.createQmlObject('import QtLocation 5.3; Place { }', this); + place.plugin = testPlugin; + place.copyFrom(dummyPlace); + compare(place.placeId, ""); + compare(place.name, "dummyPlace"); + compare(place.visibility, Place.UnspecifiedVisibility); + } + + function test_contactDetails(data) { + var place = Qt.createQmlObject('import QtLocation 5.3; Place {}', this); + + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = place; + signalSpy.signalName = data.signalName; + + var detail1 = Qt.createQmlObject('import QtLocation 5.3; ContactDetail {}', this); + detail1.label = "Detail1"; + detail1.value = "555-detail1"; + + place.contactDetails[data.contactType] = detail1; + compare(place.contactDetails[data.contactType].length, 1); + compare(place.contactDetails[data.contactType][0].label, "Detail1"); + compare(place.contactDetails[data.contactType][0].value, "555-detail1"); + + compare(place[data.primaryValue], "555-detail1"); + compare(signalSpy.count, 1); + signalSpy.clear(); + + var listView = Qt.createQmlObject('import QtQuick 2.0; ListView { delegate:Text{text:modelData.label + ":" + modelData.value } }', this); + listView.model = place.contactDetails[data.contactType]; + compare(listView.count, 1); + + var detail2 = Qt.createQmlObject('import QtLocation 5.3; ContactDetail {}', this); + detail2.label = "Detail2"; + detail2.value = "555-detail2"; + + var details = new Array(); + details.push(detail2); + details.push(detail1); + + place.contactDetails[data.contactType] = details; + compare(place.contactDetails[data.contactType].length, 2); + compare(place.contactDetails[data.contactType][0].label, "Detail2"); + compare(place.contactDetails[data.contactType][0].value, "555-detail2"); + compare(place.contactDetails[data.contactType][1].label, "Detail1"); + compare(place.contactDetails[data.contactType][1].value, "555-detail1"); + + compare(place[data.primaryValue], "555-detail2"); + compare(signalSpy.count, 1); + signalSpy.clear(); + listView.model = place.contactDetails[data.contactType]; + compare(listView.count, 2); + } + + function test_contactDetails_data() { + return [ + { tag: "phone", contactType: "phone", signalName: "primaryPhoneChanged", primaryValue: "primaryPhone"}, + { tag: "fax", contactType: "fax", signalName: "primaryFaxChanged", primaryValue: "primaryFax"}, + { tag: "email", contactType: "email", signalName: "primaryEmailChanged", primaryValue: "primaryEmail"}, + { tag: "website", contactType: "website", signalName: "primaryWebsiteChanged", primaryValue: "primaryWebsite"} + ]; + } +} diff --git a/tests/auto/declarative_core/tst_placeattribute.qml b/tests/auto/declarative_core/tst_placeattribute.qml new file mode 100644 index 0000000..ae61aed --- /dev/null +++ b/tests/auto/declarative_core/tst_placeattribute.qml @@ -0,0 +1,53 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "PlaceAttribute" + + PlaceAttribute { + id: testAttribute + } + + function test_setAndGet_data() { + return [ + { tag: "label", property: "label", signal: "labelChanged", value: "Test Label", reset: "" }, + { tag: "text", property: "text", signal: "textChanged", value: "Test Text", reset: "" }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testAttribute, data); + } +} diff --git a/tests/auto/declarative_core/tst_placeicon.qml b/tests/auto/declarative_core/tst_placeicon.qml new file mode 100644 index 0000000..c0f099d --- /dev/null +++ b/tests/auto/declarative_core/tst_placeicon.qml @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "Icon" + + Icon { id: emptyIcon } + + function test_empty() { + compare(emptyIcon.plugin, null); + compare(emptyIcon.parameters.keys().length, 0) + } + + + Icon { + id: qmlIconSingleUrl + } + + function test_qmlSingleUrlIcon() { + qmlIconSingleUrl.parameters.singleUrl = "http://example.com/icon.png" + + var u = qmlIconSingleUrl.url(Qt.size(64, 64)); + compare(u, "http://example.com/icon.png"); + + u = qmlIconSingleUrl.url(Qt.size(20, 20)); + compare(u, "http://example.com/icon.png"); + + qmlIconSingleUrl.parameters.singleUrl = "/home/user/icon.png" + u = qmlIconSingleUrl.url(Qt.size(20, 20)); + compare(u, "file:///home/user/icon.png"); + } + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + } + + Icon { + id: qmlIconParams + plugin: testPlugin + } + + function test_qmlIconParams() { + compare(qmlIconParams.plugin, testPlugin); + qmlIconParams.parameters.s = "http://example.com/icon_small.png" + qmlIconParams.parameters.m = "http://example.com/icon_medium.png" + qmlIconParams.parameters.l = "http://example.com/icon_large.png" + + compare(qmlIconParams.url(Qt.size(10, 10)), "http://example.com/icon_small.png"); + compare(qmlIconParams.url(Qt.size(20, 20)), "http://example.com/icon_small.png"); + compare(qmlIconParams.url(Qt.size(24, 24)), "http://example.com/icon_small.png"); + compare(qmlIconParams.url(Qt.size(25, 25)), "http://example.com/icon_medium.png"); + compare(qmlIconParams.url(Qt.size(30, 30)), "http://example.com/icon_medium.png"); + compare(qmlIconParams.url(Qt.size(39, 39)), "http://example.com/icon_medium.png"); + compare(qmlIconParams.url(Qt.size(40, 40)), "http://example.com/icon_large.png"); + compare(qmlIconParams.url(Qt.size(50, 50)), "http://example.com/icon_large.png"); + compare(qmlIconParams.url(Qt.size(60, 60)), "http://example.com/icon_large.png"); + } + + Icon { + id: testIcon + } + + function test_setAndGet_data() { + return [ + { tag: "plugin", property: "plugin", signal: "pluginChanged", value: testPlugin }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testIcon, data); + } +} diff --git a/tests/auto/declarative_core/tst_placesearchmodel.qml b/tests/auto/declarative_core/tst_placesearchmodel.qml new file mode 100644 index 0000000..4c7897b --- /dev/null +++ b/tests/auto/declarative_core/tst_placesearchmodel.qml @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import QtPositioning 5.2 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "PlaceSearchModel" + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + PluginParameter { + name: "initializePlaceData" + value: true + } + ] + } + + Plugin { + id: favoritePlugin + name: "foo" + } + + Plugin { + id: uninitializedPlugin + } + + Category { + id: testCategory1 + categoryId: "da3606c1-3448-43b3-a4a3-ca24b12dd94a" + name: "Test Category 1" + } + + Category { + id: testCategory2 + categoryId: "bb8ead84-ec2a-48a9-9c8f-d4ffd3134b21" + name: "Test Category 2" + } + + function compareArray(a, b) { + if (a.length !== b.length) + return false; + + for (var i = 0; i < a.length; ++i) { + if (b.indexOf(a[i]) < 0) + return false; + } + + return true; + } + + function test_setAndGet_data() { + var testSearchArea = QtPositioning.circle(QtPositioning.coordinate(10, 20), 5000); + + return [ + { tag: "plugin", property: "plugin", signal: "pluginChanged", value: testPlugin }, + { tag: "searchArea", property: "searchArea", signal: "searchAreaChanged", value: testSearchArea, reset: QtPositioning.shape() }, + { tag: "limit", property: "limit", signal: "limitChanged", value: 10, reset: -1 }, + + { tag: "searchTerm", property: "searchTerm", signal: "searchTermChanged", value: "Test term", reset: "" }, + { tag: "recommendationId", property: "recommendationId", signal: "recommendationIdChanged", value: "Test-place-id", reset: "" }, + { tag: "relevanceHint", property: "relevanceHint", signal: "relevanceHintChanged", value: PlaceSearchModel.DistanceHint, reset: PlaceSearchModel.UnspecifiedHint }, + { tag: "visibilityScope", property: "visibilityScope", signal: "visibilityScopeChanged", value: Place.DeviceVisibility, reset: Place.UnspecifiedVisibility }, + { tag: "favoritesPlugin", property: "favoritesPlugin", signal: "favoritesPluginChanged", value: favoritePlugin }, + { tag: "category", property: "categories", signal: "categoriesChanged", value: testCategory1, expectedValue: [ testCategory1 ], reset: [], array: true }, + { tag: "categories", property: "categories", signal: "categoriesChanged", value: [ testCategory1, testCategory2 ], reset: [], array: true }, + ]; + } + + function test_setAndGet(data) { + var testModel = Qt.createQmlObject('import QtLocation 5.3; PlaceSearchModel {}', testCase, "PlaceSearchModel"); + Utils.testObjectProperties(testCase, testModel, data); + delete testModel; + } + + function test_search_data() { + var park = Qt.createQmlObject('import QtLocation 5.3; Category {name: "Park"; categoryId: "c2e1252c-b997-44fc-8165-e53dd00f66a7"}', testCase, "Category"); + return [ + { + tag: "searchTerm, multiple results", + property: "searchTerm", + value: "view", + reset: "", + places: [ + "4dcc74ce-fdeb-443e-827c-367438017cf1", + "8f72057a-54b2-4e95-a7bb-97b4d2b5721e" + ] + }, + { + tag: "searchTerm, single result", + property: "searchTerm", + value: "park", + reset: "", + places: [ + "4dcc74ce-fdeb-443e-827c-367438017cf1" + ] + }, + { + tag: "searchTerm, multiple results", + property: "searchTerm", + value: "sea", + reset: "", + alternate: true, + places: [ + "8f72057a-54b2-4e95-a7bb-97b4d2b5721e" + ] + }, + { + tag: "categories, single result", + property: "categories", + value: [ park ], + places: [ + "dacb2181-3f67-4e6a-bd4d-635e99ad5b03" + ] + }, + { + tag: "recommendations", + property: "recommendationId", + value: "4dcc74ce-fdeb-443e-827c-367438017cf1", + reset: "", + places: [ + "8f72057a-54b2-4e95-a7bb-97b4d2b5721e", + "dacb2181-3f67-4e6a-bd4d-635e99ad5b03" + ] + }, + { + tag: "no recommendations", + property: "recommendationId", + value: "8f72057a-54b2-4e95-a7bb-97b4d2b5721e", + reset: "", + places: [ ] + } + ]; + } + + function test_search(data) { + var testModel = Qt.createQmlObject('import QtLocation 5.3; PlaceSearchModel {}', testCase, "PlaceSearchModel"); + testModel.plugin = testPlugin; + + var statusChangedSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + statusChangedSpy.target = testModel; + statusChangedSpy.signalName = "statusChanged"; + + var countChangedSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + countChangedSpy.target = testModel; + countChangedSpy.signalName = "rowCountChanged"; + + compare(testModel.status, PlaceSearchModel.Null); + + testModel[data.property] = data.value; + testModel.update(); + + compare(testModel.status, PlaceSearchModel.Loading); + compare(statusChangedSpy.count, 1); + + tryCompare(testModel, "status", PlaceSearchModel.Ready); + compare(statusChangedSpy.count, 2); + + if (data.places.length > 0) + compare(countChangedSpy.count, 1); + else + compare(countChangedSpy.count, 0); + + for (var i = 0; i < testModel.count; ++i) { + compare(testModel.data(i, "type"), PlaceSearchModel.PlaceResult); + + var place = testModel.data(i, "place"); + + verify(data.places.indexOf(place.placeId) >= 0); + } + + // Test for alternate implementation + if (data.alternate !== undefined && data.alternate === true) { + for (var ii = 0; ii < testModel.count; ++ii) { + var p = testModel.data(ii, "place"); + compare(p.extendedAttributes["x_provider"].text, "QPlacePrivateDefaultAlt") + } + } + + testModel.reset(); + + compare(statusChangedSpy.count, 3); + compare(testModel.status, PlaceSearchModel.Null); + if (data.places.length > 0) + compare(countChangedSpy.count, 2); + else + compare(countChangedSpy.count, 0); + compare(testModel.count, 0); + + countChangedSpy.destroy(); + statusChangedSpy.destroy(); + + if (data.reset === undefined) { + testModel[data.property] = null; + } else { + testModel[data.property] = data.reset; + } + + delete testModel; + delete statusChangedSpy; + delete countChangedSpy; + } + + function test_cancel() { + var testModel = Qt.createQmlObject('import QtLocation 5.3; PlaceSearchModel {}', testCase, "PlaceSearchModel"); + testModel.plugin = testPlugin; + + var statusChangedSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + statusChangedSpy.target = testModel; + statusChangedSpy.signalName = "statusChanged"; + + //try cancelling from an initially null state + compare(testModel.status, PlaceSearchModel.Null); + testModel.searchTerm = "view"; + testModel.update(); + tryCompare(testModel, "status", PlaceSearchModel.Loading); + testModel.cancel(); + tryCompare(testModel, "status", PlaceSearchModel.Ready); + compare(statusChangedSpy.count, 2); + + testModel.update(); + tryCompare(testModel, "status", PlaceSearchModel.Loading); + tryCompare(testModel, "status", PlaceSearchModel.Ready); + compare(statusChangedSpy.count, 4); + + var numResults = testModel.count; + verify(numResults > 0); + + //try cancelling from an initially ready state + testModel.update(); + tryCompare(testModel, "status", PlaceSearchModel.Loading); + testModel.cancel(); + tryCompare(testModel, "status", PlaceSearchModel.Ready); + compare(testModel.count, numResults); + compare(statusChangedSpy.count, 6); + + //chack that an encountering an error will cause the model + //to clear its data + testModel.plugin = null; + testModel.update(); + tryCompare(testModel, "count", 0); + compare(testModel.status, PlaceSearchModel.Error); + + delete testModel; + delete statusChangedSpy; + } + + function test_error() { + var testModel = Qt.createQmlObject('import QtLocation 5.3; PlaceSearchModel {}', testCase, "PlaceSearchModel"); + + var statusChangedSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + statusChangedSpy.target = testModel; + statusChangedSpy.signalName = "statusChanged"; + + //try searching without a plugin instance + testModel.update(); + tryCompare(statusChangedSpy, "count", 2); + compare(testModel.status, PlaceSearchModel.Error); + statusChangedSpy.clear(); + //Aside: there is some difficulty in checking the transition to the Loading state + //since the model transitions from Loading to Error before the next event loop + //iteration. + + //try searching with an uninitialized plugin instance. + testModel.plugin = uninitializedPlugin; + testModel.update(); + tryCompare(statusChangedSpy, "count", 2); + compare(testModel.status, PlaceSearchModel.Error); + statusChangedSpy.clear(); + + //try searching with plugin a instance + //that has been provided a non-existent name + testModel.plugin = favoritePlugin; + testModel.update(); + tryCompare(statusChangedSpy, "count", 2); + compare(testModel.status, PlaceSearchModel.Error); + } +} diff --git a/tests/auto/declarative_core/tst_placesearchsuggestionmodel.qml b/tests/auto/declarative_core/tst_placesearchsuggestionmodel.qml new file mode 100644 index 0000000..1334756 --- /dev/null +++ b/tests/auto/declarative_core/tst_placesearchsuggestionmodel.qml @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import QtPositioning 5.2 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "PlaceSearchSuggestionModel" + + PlaceSearchSuggestionModel { + id: testModel + } + + PlaceSearchSuggestionModel { + id: testModelError + } + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + } + + Plugin { + id: nonExistantPlugin + name: "nonExistantName" + } + + Plugin { + id: uninitializedPlugin + } + + function test_setAndGet_data() { + var testSearchArea = QtPositioning.circle(QtPositioning.coordinate(10, 20), 5000); + return [ + { tag: "plugin", property: "plugin", signal: "pluginChanged", value: testPlugin }, + { tag: "searchArea", property: "searchArea", signal: "searchAreaChanged", value: testSearchArea, reset: QtPositioning.shape() }, + { tag: "limit", property: "limit", signal: "limitChanged", value: 10, reset: -1 }, + + { tag: "searchTerm", property: "searchTerm", signal: "searchTermChanged", value: "Test term", reset: "" }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testModel, data); + } + + SignalSpy { id: statusChangedSpy; target: testModel; signalName: "statusChanged" } + SignalSpy { id: suggestionsChangedSpy; target: testModel; signalName: "suggestionsChanged" } + + function test_suggestions() { + compare(statusChangedSpy.count, 0); + testModel.plugin = testPlugin; + + compare(testModel.status, PlaceSearchSuggestionModel.Null); + + testModel.searchTerm = "test"; + testModel.update(); + + compare(testModel.status, PlaceSearchSuggestionModel.Loading); + compare(statusChangedSpy.count, 1); + + tryCompare(testModel, "status", PlaceSearchSuggestionModel.Ready); + compare(statusChangedSpy.count, 2); + + var expectedSuggestions = [ "test1", "test2", "test3" ]; + + compare(suggestionsChangedSpy.count, 1); + compare(testModel.suggestions, expectedSuggestions); + + testModel.reset(); + + compare(statusChangedSpy.count, 3); + compare(testModel.status, PlaceSearchSuggestionModel.Null); + compare(suggestionsChangedSpy.count, 2); + compare(testModel.suggestions, []); + + testModel.update(); + + compare(statusChangedSpy.count, 4); + compare(testModel.status, PlaceSearchSuggestionModel.Loading); + + testModel.cancel(); + + compare(statusChangedSpy.count, 5); + compare(testModel.status, PlaceSearchSuggestionModel.Ready); + + //check that an encountering an error will cause the model + //to clear its data + testModel.plugin = null; + testModel.update(); + tryCompare(testModel.suggestions, "length", 0); + compare(testModel.status, PlaceSearchSuggestionModel.Error); + } + + SignalSpy { id: statusChangedSpyError; target: testModelError; signalName: "statusChanged" } + + function test_error() { + compare(statusChangedSpyError.count, 0); + //try searching without a plugin instance + testModelError.update(); + tryCompare(statusChangedSpyError, "count", 2); + compare(testModelError.status, PlaceSearchSuggestionModel.Error); + statusChangedSpyError.clear(); + //Aside: there is some difficulty in checking the transition to the Loading state + //since the model transitions from Loading to Error before the next event loop + //iteration. + + //try searching with an uninitialized plugin instance. + testModelError.plugin = uninitializedPlugin; + testModelError.update(); + tryCompare(statusChangedSpyError, "count", 2); + compare(testModelError.status, PlaceSearchSuggestionModel.Error); + statusChangedSpyError.clear(); + + //try searching with plugin a instance + //that has been provided a non-existent name + testModelError.plugin = nonExistantPlugin; + testModelError.update(); + tryCompare(statusChangedSpyError, "count", 2); + compare(testModelError.status, PlaceSearchSuggestionModel.Error); + } +} diff --git a/tests/auto/declarative_core/tst_plugin.qml b/tests/auto/declarative_core/tst_plugin.qml new file mode 100644 index 0000000..7b880f1 --- /dev/null +++ b/tests/auto/declarative_core/tst_plugin.qml @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 + +Item { + + Plugin { id: unattachedPlugin } + Plugin { id: herePlugin; name: "here"} + Plugin { id: invalidPlugin; name: "invalid"; allowExperimental: true } + Plugin { id: testPlugin; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "supported"; value: true}, + PluginParameter { name: "finishRequestImmediately"; value: true}, + PluginParameter { name: "validateWellKnownValues"; value: true} + ] + } + SignalSpy {id: invalidAttachedSpy; target: invalidPlugin; signalName: "attached"} + + Plugin { + id: requiredPlugin + allowExperimental: true + required { + mapping: Plugin.OfflineMappingFeature; + geocoding: Plugin.OfflineGeocodingFeature; + places: Plugin.AnyPlacesFeatures; + } + } + + TestCase { + name: "Plugin properties" + function test_plugin() { + verify (invalidPlugin.availableServiceProviders.length > 0) + verify (invalidPlugin.availableServiceProviders.indexOf('qmlgeo.test.plugin') > -1) // at least test plugin must be present + + // invalid plugins should have no features + verify(invalidPlugin.isAttached) + verify(!(invalidPlugin.supportsMapping())) + verify(!(invalidPlugin.supportsGeocoding())) + verify(!(invalidPlugin.supportsRouting())) + verify(!(invalidPlugin.supportsPlaces())) + + if (invalidPlugin.availableServiceProviders.indexOf('qmlgeo.test.plugin') > -1) { + verify(testPlugin.isAttached) + verify(testPlugin.supportsMapping()) + verify(testPlugin.supportsGeocoding()) + verify(testPlugin.supportsPlaces()) + verify(testPlugin.supportsRouting()) + } + + if (invalidPlugin.availableServiceProviders.indexOf('here')) { + verify(herePlugin.isAttached) + verify(herePlugin.supportsMapping(Plugin.OnlineMappingFeature)) + verify(herePlugin.supportsGeocoding(Plugin.OnlineGeocodingFeature)) + verify(herePlugin.supportsRouting(Plugin.OnlineRoutingFeature)) + } + + verify(!unattachedPlugin.isAttached) + + // test changing name of plugin + invalidAttachedSpy.clear() + compare(invalidAttachedSpy.count, 0) + invalidPlugin.name = 'qmlgeo.test.plugin' + tryCompare(invalidAttachedSpy, 'count', 1) + verify(invalidPlugin.isAttached) + + verify(invalidPlugin.supportsMapping()) + verify(invalidPlugin.supportsGeocoding()) + verify(invalidPlugin.supportsRouting()) + verify(invalidPlugin.supportsPlaces()) + + invalidPlugin.name = 'here' + compare(invalidAttachedSpy.count, 2) + verify(invalidPlugin.supportsMapping(Plugin.OnlineMappingFeature)) + verify(invalidPlugin.supportsGeocoding(Plugin.OnlineGeocodingFeature)) + verify(invalidPlugin.supportsRouting(Plugin.OnlineRoutingFeature)) + + invalidPlugin.name = '' + compare(invalidAttachedSpy.count, 2) + verify(!invalidPlugin.supportsMapping()) + verify(!invalidPlugin.supportsGeocoding()) + verify(!invalidPlugin.supportsRouting()) + verify(!invalidPlugin.supportsPlaces()) + } + + function test_required() { + // the required plugin should either get here or qmlgeo.test.plugin + // either way the name will be non-empty and it'll meet the spec + verify(requiredPlugin.name !== "") + verify(requiredPlugin.supportsMapping(requiredPlugin.required.mapping)) + verify(requiredPlugin.supportsGeocoding(requiredPlugin.required.geocoding)) + verify(requiredPlugin.supportsPlaces(requiredPlugin.required.places)) + } + + function test_placesFeatures() { + verify(testPlugin.supportsPlaces(Plugin.SavePlaceFeature)) + verify(testPlugin.supportsPlaces(Plugin.SaveCategoryFeature)) + verify(testPlugin.supportsPlaces(Plugin.SearchSuggestionsFeature)) + verify(!testPlugin.supportsPlaces(Plugin.RemovePlaceFeature)) + } + + function test_locale() { + compare(herePlugin.locales, [Qt.locale().name]); + + //try assignment of a single locale + herePlugin.locales = "fr_FR"; + compare(herePlugin.locales, ["fr_FR"]); + + //try assignment of multiple locales + herePlugin.locales = ["fr_FR","en_US"]; + compare(herePlugin.locales, ["fr_FR","en_US"]); + + //check that assignment of empty locale list defaults to system locale + herePlugin.locales = []; + compare(herePlugin.locales, [Qt.locale().name]); + } + } +} diff --git a/tests/auto/declarative_core/tst_plugin_error.qml b/tests/auto/declarative_core/tst_plugin_error.qml new file mode 100644 index 0000000..50b0359 --- /dev/null +++ b/tests/auto/declarative_core/tst_plugin_error.qml @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 + +Item { + + Plugin { id: testPlugin; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "error"; value: "1"}, + PluginParameter { name: "errorString"; value: "This error was expected. No worries !"} + ] + } + + Map { + id: map + } + + SignalSpy {id: errorSpy; target: map; signalName: "errorChanged"} + + TestCase { + name: "MappingManagerError" + function test_error() { + verify (map.error === Map.NoError); + map.plugin = testPlugin; + verify (map.error === Map.NotSupportedError); + verify (map.errorString == "This error was expected. No worries !"); + compare(errorSpy.count, 1); + } + } +} diff --git a/tests/auto/declarative_core/tst_position.qml b/tests/auto/declarative_core/tst_position.qml new file mode 100644 index 0000000..1bde087 --- /dev/null +++ b/tests/auto/declarative_core/tst_position.qml @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtPositioning 5.3 + +TestCase { + id: testCase + + name: "Position" + + Position { id: defaultPosition } + + SignalSpy { id: latitudeValidSpy; target: defaultPosition; signalName: "latitudeValidChanged" } + SignalSpy { id: longitudeValidSpy; target: defaultPosition; signalName: "longitudeValidChanged" } + SignalSpy { id: altitudeValidSpy; target: defaultPosition; signalName: "altitudeValidChanged" } + SignalSpy { id: timestampSpy; target: defaultPosition; signalName: "timestampChanged" } + SignalSpy { id: speedSpy; target: defaultPosition; signalName: "speedChanged" } + SignalSpy { id: speedValidSpy; target: defaultPosition; signalName: "speedValidChanged" } + SignalSpy { id: coordinateSpy; target: defaultPosition; signalName: "coordinateChanged" } + SignalSpy { id: horizontalAccuracySpy; target: defaultPosition; signalName: "horizontalAccuracyChanged" } + SignalSpy { id: horizontalAccuracyValidSpy; target: defaultPosition; signalName: "horizontalAccuracyValidChanged" } + SignalSpy { id: verticalAccuracySpy; target: defaultPosition; signalName: "verticalAccuracyChanged" } + SignalSpy { id: verticalAccuracyValidSpy; target: defaultPosition; signalName: "verticalAccuracyValidChanged" } + SignalSpy { id: directionSpy; target: defaultPosition; signalName: "directionChanged" } + SignalSpy { id: verticalSpeedSpy; target: defaultPosition; signalName: "verticalSpeedChanged" } + + function test_defaults() { + compare(defaultPosition.latitudeValid, false); + compare(defaultPosition.longitudeValid, false); + compare(defaultPosition.altitudeValid, false); + compare(defaultPosition.speedValid, false); + compare(defaultPosition.horizontalAccuracyValid, false); + compare(defaultPosition.verticalAccuracyValid, false); + verify(!defaultPosition.directionValid); + verify(isNaN(defaultPosition.direction)); + verify(!defaultPosition.verticalSpeedValid); + verify(isNaN(defaultPosition.verticalSpeed)); + } + + function test_modifiers() { + latitudeValidSpy.clear(); + longitudeValidSpy.clear(); + altitudeValidSpy.clear(); + timestampSpy.clear(); + speedSpy.clear(); + speedValidSpy.clear(); + coordinateSpy.clear(); + horizontalAccuracySpy.clear(); + horizontalAccuracyValidSpy.clear(); + verticalAccuracySpy.clear(); + verticalAccuracyValidSpy.clear(); + directionSpy.clear(); + verticalSpeedSpy.clear(); + + defaultPosition.horizontalAccuracy = 10; + compare(horizontalAccuracySpy.count, 1); + compare(horizontalAccuracyValidSpy.count, 1); + compare(defaultPosition.horizontalAccuracy, 10); + compare(defaultPosition.horizontalAccuracyValid, true); + + defaultPosition.verticalAccuracy = 10; + compare(verticalAccuracySpy.count, 1); + compare(verticalAccuracyValidSpy.count, 1); + compare(defaultPosition.verticalAccuracy, 10); + compare(defaultPosition.verticalAccuracyValid, true); + + // some extra precautions + compare(horizontalAccuracyValidSpy.count, 1); + compare(speedSpy.count, 0); + compare(speedValidSpy.count, 0); + } +} diff --git a/tests/auto/declarative_core/tst_positionsource.qml b/tests/auto/declarative_core/tst_positionsource.qml new file mode 100644 index 0000000..a663f3a --- /dev/null +++ b/tests/auto/declarative_core/tst_positionsource.qml @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtPositioning 5.2 + +TestCase { + id: testCase + + name: "PositionSource" + + PositionSource { id: defaultSource } + PositionSource + { + id: activeDefaultSource + active: true + } + + SignalSpy { id: defaultSourceSpy; target: defaultSource; signalName: "positionChanged" } + + function test_activeDefaultSource() { + wait(0); + verify(activeDefaultSource.name !== ""); + compare(activeDefaultSource.active, true); + } + + function test_invalidSource() { + activeDefaultSource.name = "invalid_positioning_source"; + verify(!activeDefaultSource.active); + verify(!activeDefaultSource.valid); + } + + function test_defaults() { + // at least the test.source plugin should be available + verify(defaultSource.name != ""); + compare(defaultSource.active, false); + } + + function test_inactive() { + defaultSourceSpy.clear(); + compare(defaultSourceSpy.count, 0); + wait(1000); + compare(defaultSourceSpy.count, 0); + } + + PositionSource { id: testSetSource; name: "nonexistent bogus plugin" } + SignalSpy { id: testingSourcePluginSpy; target: testSetSource; signalName: "nameChanged" } + + function test_setplugin() { + testingSourcePluginSpy.clear(); + + // On construction, if the provided source name is invalid, the default source will be + // used. Test that the source is valid as expected. + verify(testSetSource.name !== ""); + //we don't really know what the default source is named. + //It may not be "test.source" + var defaultSourceName = testSetSource.name; + verify(testSetSource.valid); + + // Test that setting name to "" will still use the default. + testSetSource.name = ""; + compare(testingSourcePluginSpy.count, 0); + compare(testSetSource.name, defaultSourceName); + verify(testSetSource.valid); + + testSetSource.name = "test.source"; + if (defaultSourceName === "test.source") + compare(testingSourcePluginSpy.count, 0); + compare(testSetSource.name, "test.source"); + verify(testSetSource.valid); + testingSourcePluginSpy.clear(); + + testSetSource.name = "bogus"; + compare(testingSourcePluginSpy.count, 1); + verify(!testSetSource.valid); + } + + PositionSource { id: testingSource; name: "test.source"; updateInterval: 1000 } + SignalSpy { id: updateSpy; target: testingSource; signalName: "positionChanged" } + SignalSpy { id: directionValidSpy; target: testingSource.position; signalName: "directionValidChanged" } + SignalSpy { id: directionSpy; target: testingSource.position; signalName: "directionChanged" } + + function test_updateInterval() { + testingSource.updateInterval = 1000; + compare(testingSource.updateInterval, 1000); + testingSource.updateInterval = 1200; + compare(testingSource.updateInterval, 1200); + testingSource.updateInterval = 800; + compare(testingSource.updateInterval, 1000); + } + + function test_preferredPositioningMethods() { + testingSource.preferredPositioningMethods = PositionSource.AllPositioningMethods; + compare(testingSource.preferredPositioningMethods, PositionSource.AllPositioningMethods); + testingSource.preferredPositioningMethods = PositionSource.SatellitePositioningMethods; + compare(testingSource.preferredPositioningMethods, PositionSource.SatellitePositioningMethods); + testingSource.preferredPositioningMethods = PositionSource.NonSatellitePositioningMethods; + compare(testingSource.preferredPositioningMethods, PositionSource.NonSatellitePositioningMethods); + } + + function test_updates() { + updateSpy.clear(); + + compare(directionValidSpy.count, 0) + compare(directionSpy.count, 0) + + testingSource.active = true; + + tryCompare(updateSpy, "count", 1, 1500); + compare(testingSource.position.coordinate.longitude, 0.1); + compare(testingSource.position.coordinate.latitude, 0.1); + compare(directionValidSpy.count, 1) + compare(directionSpy.count, 1) + fuzzyCompare(testingSource.position.direction, 45, 0.1) + verify(!testingSource.position.speedValid) + verify(isNaN(testingSource.position.speed)) + + tryCompare(updateSpy, "count", 2, 1500); + compare(testingSource.position.coordinate.longitude, 0.2); + compare(testingSource.position.coordinate.latitude, 0.2); + compare(directionValidSpy.count, 1) + compare(directionSpy.count, 2) + fuzzyCompare(testingSource.position.direction, 45, 0.1) + verify(testingSource.position.speedValid) + verify(testingSource.position.speed > 10000) + + testingSource.active = false; + wait(2500); + compare(updateSpy.count, 2); + compare(testingSource.position.coordinate.longitude, 0.2); + compare(testingSource.position.coordinate.latitude, 0.2); + compare(directionValidSpy.count, 1) + compare(directionSpy.count, 2) + fuzzyCompare(testingSource.position.direction, 45, 0.1) + verify(testingSource.position.speedValid) + verify(testingSource.position.speed > 10000) + } +} diff --git a/tests/auto/declarative_core/tst_ratings.qml b/tests/auto/declarative_core/tst_ratings.qml new file mode 100644 index 0000000..5628432 --- /dev/null +++ b/tests/auto/declarative_core/tst_ratings.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "Ratings" + + Ratings { id: emptyRatings } + + function test_empty() { + compare(emptyRatings.average, 0.0); + compare(emptyRatings.maximum, 0.0); + compare(emptyRatings.count, 0); + } + + Ratings { + id: qmlRatings + + average: 3.5 + maximum: 5.0 + count: 7 + } + + function test_qmlConstructedRatings() { + compare(qmlRatings.average, 3.5); + compare(qmlRatings.maximum, 5.0); + compare(qmlRatings.count, 7); + } + + Ratings { + id: testRatings + } + + function test_setAndGet_data() { + return [ + { tag: "average", property: "average", signal: "averageChanged", value: 4.5, reset: 0.0 }, + { tag: "maximum", property: "maximum", signal: "maximumChanged", value: 5.0, reset: 0.0 }, + { tag: "count", property: "count", signal: "countChanged", value: 10, reset: 0 }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testRatings, data); + } +} diff --git a/tests/auto/declarative_core/tst_reviewmodel.qml b/tests/auto/declarative_core/tst_reviewmodel.qml new file mode 100644 index 0000000..192026f --- /dev/null +++ b/tests/auto/declarative_core/tst_reviewmodel.qml @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + name: "ReviewModel" + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + PluginParameter { + name: "initializePlaceData" + value: true + } + ] + } + + ReviewModel { + id: testModel + } + + Place { + id: testPlace + name: "Test Place" + } + + Place { + id: parkViewHotel + placeId: "4dcc74ce-fdeb-443e-827c-367438017cf1" + plugin: testPlugin + } + + Place { + id: seaViewHotel + placeId: "8f72057a-54b2-4e95-a7bb-97b4d2b5721e" + plugin: testPlugin + } + + function test_setAndGet_data() { + return [ + { tag: "place", property: "place", signal: "placeChanged", value: testPlace }, + { tag: "batchSize", property: "batchSize", signal: "batchSizeChanged", value: 10, reset: 1 }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testModel, data); + } + + function test_consecutive_fetch_data() { + return [ + { tag: "batchSize 1", batchSize: 1 }, + { tag: "batchSize 2", batchSize: 2 }, + { tag: "batchSize 5", batchSize: 5 }, + { tag: "batchSize 10", batchSize: 10 }, + ]; + } + + function test_consecutive_fetch(data) { + //Note: in javascript the months go from 0(Jan) to 11(Dec) + var expectedReviews = [ + { + "title": "Park View Review 1", + "text": "Park View Review 1 Text", + "dateTime": new Date(2004, 8, 22, 13, 1), + "language": "en", + "rating": 3.5, + "reviewId": "0001" + }, + { + "title": "Park View Review 2", + "text": "Park View Review 2 Text", + "dateTime": new Date(2005, 8, 14, 4, 17), + "language": "en", + "rating": 1, + "reviewId": "0002" + }, + { + "title": "Park View Review 3", + "text": "Park View Review 3 Text", + "dateTime": new Date(2005, 9, 14, 4, 12), + "language": "en", + "rating": 5, + "reviewId": "0003" + }, + { + "title": "", + "text": "", + "dateTime": new Date(""), + "language": "", + "rating": 0, + "reviewId": "" + }, + { + "title": "Park View Review 5", + "text": "Park View Review 5 Text", + "dateTime": new Date(2005, 10, 20, 14, 53), + "language": "en", + "rating": 2.3, + "reviewId": "0005" + } + ] + + var model = createModel(); + Utils.testConsecutiveFetch(testCase, model, parkViewHotel, expectedReviews, data); + model.destroy(); + } + + function test_reset() { + var model = createModel(); + Utils.testReset(testCase, model, parkViewHotel); + model.destroy(); + } + + function test_fetch_data() { + return [ + { + tag: "fetch all reviews in a single batch", + model: createModel(), + batchSize: 10, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + }, + { + tag: "fetch from a place with no reviews", + model: createModel(), + batchSize: 1, + place: seaViewHotel, + expectedTotalCount: 0, + expectedCount: 0 + }, + { + tag: "fetch with batch size one less than the total", + model: createModel(), + batchSize: 4, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 4 + }, + { + tag: "fetch with batch size equal to the total", + model: createModel(), + batchSize: 5, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + }, + { + tag: "fetch with batch size larger than the total", + model: createModel(), + batchSize: 6, + place: parkViewHotel, + expectedTotalCount: 5, + expectedCount: 5 + } + ] + } + + function test_fetch(data) { + Utils.testFetch(testCase, data); + data.model.destroy(); + } + + function createModel() { + return Qt.createQmlObject('import QtLocation 5.3; ReviewModel {}', + testCase, "reviewModel"); + } +} diff --git a/tests/auto/declarative_core/tst_routing.qml b/tests/auto/declarative_core/tst_routing.qml new file mode 100644 index 0000000..5198d87 --- /dev/null +++ b/tests/auto/declarative_core/tst_routing.qml @@ -0,0 +1,950 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.11 +import QtPositioning 5.2 + +Item { + id: root + function cloneArray(a) { + var i = a.length + var arr = new Array(i) + while (i--) arr[i] = a[i]; + return arr + } + Plugin { id: testPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true } + Plugin { id: errorPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true + parameters: [ + PluginParameter { name: "error"; value: "1"}, + PluginParameter { name: "errorString"; value: "This error was expected. No worries !"} + ] + } + + property variant coordinate1: QtPositioning.coordinate(51, 0) + property variant coordinate2: QtPositioning.coordinate(52, 0) + + property variant boundingBox1: QtPositioning.rectangle() + property variant boundingBox2: QtPositioning.rectangle() + + property variant circle1: QtPositioning.circle() + property variant circle2: QtPositioning.circle(tr, 4000) + + Component.onCompleted: { + boundingBox1.topLeft = bl + boundingBox1.bottomRight = bl + boundingBox1.width = 10 + + boundingBox2.topLeft = bl + boundingBox2.bottomRight = bl + boundingBox2.width = 20 + } + + property variant bl: QtPositioning.coordinate(0, 0) + property variant tl: QtPositioning.coordinate(1, 0) + property variant tr: QtPositioning.coordinate(1, 1) + property variant br: QtPositioning.coordinate(0, 1) + property variant ntr: QtPositioning.coordinate(3, 3) + + property variant unitBox: QtPositioning.rectangle(tl, br) + + Route {id: emptyRoute} + TestCase { + name: "RouteManeuver RouteSegment and MapRoute" + RouteSegment {id: emptySegment} + RouteManeuver {id: emptyManeuver} + + // TODO enable when we have map route + //MapRoute {id: emptyMapRoute} + + property variant emptyBox: QtPositioning.rectangle() + + property variant emptyCoordinate: QtPositioning.coordinate() + + // TODO enable when we have map route + /* + SignalSpy {id: mapRouteDetailLevelSpy; target: emptyMapRoute; signalName: "detailLevelChanged"} + SignalSpy {id: mapRouteColorSpy; target: emptyMapRoute.border; signalName: "colorChanged"} + SignalSpy {id: mapRouteWidthSpy; target: emptyMapRoute.border; signalName: "widthChanged"} + SignalSpy {id: mapRouteRouteSpy; target: emptyMapRoute; signalName: "routeChanged"} + function test_maproute_defaults() { + compare(mapRouteRouteSpy.count, 0) + compare(mapRouteColorSpy.count, 0) + compare(mapRouteDetailLevelSpy.count, 0) + compare (emptyMapRoute.detailLevel, 6) + emptyMapRoute.border.color = 'green' + emptyMapRoute.detailLevel = 3 + compare(mapRouteRouteSpy.count, 0) + compare(mapRouteColorSpy.count, 1) + compare(mapRouteDetailLevelSpy.count, 1) + emptyMapRoute.border.color = 'green' + emptyMapRoute.detailLevel = 3 + compare(mapRouteColorSpy.count, 1) + compare(mapRouteDetailLevelSpy.count, 1) + emptyMapRoute.route = emptyRoute + compare(mapRouteRouteSpy.count, 1) + compare(emptyMapRoute.route, emptyRoute) + // width + compare(mapRouteWidthSpy.count, 0) + emptyMapRoute.border.width = 123 + compare(mapRouteWidthSpy.count, 1) + compare(emptyMapRoute.border.width, 123) + emptyMapRoute.border.width = 123 + compare(mapRouteWidthSpy.count, 1) + emptyMapRoute.border.width = -1 + compare(mapRouteWidthSpy.count, 1) + compare(emptyMapRoute.border.width, 123) + emptyMapRoute.border.width = 0 + compare(mapRouteWidthSpy.count, 1) + compare(emptyMapRoute.border.width, 123) + } + */ + + function test_route_defaults() { + compare(emptyRoute.travelTime, 0) + compare(emptyRoute.distance,0) + compare(emptyRoute.path.length,0) + compare(emptyRoute.segments.length,0) + compare(emptyRoute.bounds.topLeft.latitude, emptyBox.topLeft.latitude) + compare(emptyRoute.bounds.bottomRight.longitude, emptyBox.bottomRight.longitude) + } + + function test_routesegment_defaults() { + compare(emptySegment.travelTime, 0) + compare(emptySegment.distance, 0) + compare(emptySegment.path.length, 0) + compare(emptySegment.maneuver.valid, emptyManeuver.valid) + compare(emptySegment.maneuver.instructionText, emptyManeuver.instructionText) + compare(emptySegment.maneuver.waypointValid, emptyManeuver.waypointValid) + } + function test_maneuver_defaults() { + compare(emptyManeuver.valid, false) + compare(emptyManeuver.instructionText, "") + compare(emptyManeuver.direction, RouteManeuver.NoDirection) + compare(emptyManeuver.timeToNextInstruction,0) + compare(emptyManeuver.distanceToNextInstruction,0) + compare(emptyManeuver.waypoint.latitude, emptyCoordinate.latitude) + compare(emptyManeuver.waypoint.longitude, emptyCoordinate.longitude) + compare(emptyManeuver.position.latitude, emptyCoordinate.latitude) + compare(emptyManeuver.position.longitude, emptyCoordinate.longitude) + } + } + + TestCase { + name: "MapRouteModel and MapRouteQuery" + RouteModel {id: emptyModel} + RouteQuery {id: emptyQuery} + + function test_model_default_properties() { + compare (emptyModel.autoUpdate, false, "Automatic update") + compare (emptyModel.status, RouteModel.Null, "Model status") + compare (emptyModel.errorString, "", "Model error") + compare (emptyModel.error, RouteModel.NoError) + compare (emptyModel.count, 0, "Model count") + emptyModel.get(192) // don't do stupid + + compare (emptyQuery.numberAlternativeRoutes, 0, "Number of alternative routes") + compare (emptyQuery.travelModes, RouteQuery.CarTravel, "Travel mode") + compare (emptyQuery.routeOptimizations, RouteQuery.FastestRoute, "Route optimization") + compare (emptyQuery.segmentDetail, RouteQuery.BasicSegmentData) + compare (emptyQuery.maneuverDetail, RouteQuery.BasicManeuvers) + compare (emptyQuery.waypoints.length, 0, "Waypoints") + compare (emptyQuery.excludedAreas.length, 0, "excluded areas") + compare (emptyQuery.featureTypes.length, 0, "Feature types") + } + + SignalSpy {id: autoUpdateSpy; target: emptyModel; signalName: "autoUpdateChanged"} + SignalSpy {id: pluginSpy; target: emptyModel ; signalName: "pluginChanged"} + + SignalSpy {id: travelModesSpy; target: emptyQuery; signalName: "travelModesChanged"} + SignalSpy {id: waypointsSpy; target: emptyQuery; signalName: "waypointsChanged"} + SignalSpy {id: exclusionSpy; target: emptyQuery; signalName: "excludedAreasChanged"} + SignalSpy {id: featureTypesSpy; target: emptyQuery; signalName: "featureTypesChanged"} + SignalSpy {id: segmentDetailSpy; target: emptyQuery; signalName: "segmentDetailChanged"} + SignalSpy {id: maneuverDetailSpy; target: emptyQuery; signalName: "maneuverDetailChanged"} + SignalSpy {id: numberAlterNativeRoutesSpy; target: emptyQuery; signalName: "numberAlternativeRoutesChanged"} + SignalSpy {id: routeOptimizationsSpy; target: emptyQuery; signalName: "routeOptimizationsChanged"} + SignalSpy {id: queryDetailsChangedSpy; target: emptyQuery; signalName: "queryDetailsChanged"} + function test_model_setters() { + // Autoupdate + compare(autoUpdateSpy.count, 0) + emptyModel.autoUpdate = true + compare(autoUpdateSpy.count, 1) + compare(emptyModel.autoUpdate, true) + emptyModel.autoUpdate = true // mustn't retrigger 'changed' -signal + compare(autoUpdateSpy.count, 1) + emptyModel.autoUpdate = false + compare(autoUpdateSpy.count, 2) + + // Travelmodes + compare(travelModesSpy.count, 0) + emptyQuery.travelModes = RouteQuery.BicycleTravel + compare(travelModesSpy.count, 1) + compare(emptyQuery.travelModes, RouteQuery.BicycleTravel) + emptyQuery.travelModes = RouteQuery.BicycleTravel | RouteQuery.PedestrianTravel + compare(emptyQuery.travelModes, RouteQuery.BicycleTravel | RouteQuery.PedestrianTravel) + compare(travelModesSpy.count, 2) + compare(queryDetailsChangedSpy.count, 2) + + // Basic adding and removing of waypoint + queryDetailsChangedSpy.clear() + compare(waypointsSpy.count, 0) + emptyQuery.addWaypoint(coordinate1) + compare(waypointsSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + emptyQuery.addWaypoint(coordinate1) + compare(waypointsSpy.count, 2) + compare(queryDetailsChangedSpy.count, 2) + compare(emptyQuery.waypoints.length, 2) + emptyQuery.removeWaypoint(coordinate1) + compare(waypointsSpy.count, 3) + compare(queryDetailsChangedSpy.count, 3) + compare(emptyQuery.waypoints.length, 1) + emptyQuery.removeWaypoint(coordinate2) // coordinate2 isn't in the list, must not impact + compare(waypointsSpy.count, 3) + compare(queryDetailsChangedSpy.count, 3) + emptyQuery.removeWaypoint(coordinate1) + compare(waypointsSpy.count, 4) + emptyQuery.removeWaypoint(coordinate1) // doesn't exist anymore, must not impact + compare(waypointsSpy.count, 4) + compare(emptyQuery.waypoints.length, 0) + // Check correct ordering of waypoints + waypointsSpy.clear() + emptyQuery.addWaypoint(coordinate1) + emptyQuery.addWaypoint(coordinate2) + emptyQuery.addWaypoint(coordinate1) + emptyQuery.addWaypoint(coordinate2) + compare(waypointsSpy.count, 4) + compare(emptyQuery.waypoints[0], coordinate1) + compare(emptyQuery.waypoints[1], coordinate2) + compare(emptyQuery.waypoints[2], coordinate1) + compare(emptyQuery.waypoints[3], coordinate2) + emptyQuery.removeWaypoint(coordinate1) // remove one from the middle, check that one added last is removed + compare(emptyQuery.waypoints[0], coordinate1) + compare(emptyQuery.waypoints[1], coordinate2) + compare(emptyQuery.waypoints[2], coordinate2) + waypointsSpy.clear() + emptyQuery.clearWaypoints() + compare(emptyQuery.waypoints.length, 0) + compare(waypointsSpy.count, 1) + + // Altering the waypoint contents should trigger signal + emptyQuery.clearWaypoints() + queryDetailsChangedSpy.clear(); + emptyQuery.addWaypoint(coordinate1) + compare(queryDetailsChangedSpy.count, 1); + + // verify coordinate is disconnected + emptyQuery.removeWaypoint(coordinate1) + compare (queryDetailsChangedSpy.count, 2) + + // verify that the same coordinate can be added to the waypoints + emptyQuery.addWaypoint(coordinate1) + compare(queryDetailsChangedSpy.count, 3); + emptyQuery.addWaypoint(coordinate1) + compare(queryDetailsChangedSpy.count, 4); + compare (emptyQuery.waypoints.length, 2) + queryDetailsChangedSpy.clear() + + // verify that removing duplicate coordinate leaves remaining ones + emptyQuery.removeWaypoint(coordinate1) + compare (queryDetailsChangedSpy.count, 1) + compare (emptyQuery.waypoints.length, 1) + + // verify that clearing works + emptyQuery.clearWaypoints() + compare(queryDetailsChangedSpy.count, 2); + compare (emptyQuery.waypoints.length, 0) + + // Excluded areas + queryDetailsChangedSpy.clear() + compare(exclusionSpy.count, 0) + emptyQuery.addExcludedArea(boundingBox1) + compare(exclusionSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + emptyQuery.addExcludedArea(boundingBox1) + // doesn't make sense to put same area twice + compare(exclusionSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.excludedAreas.length, 1) + emptyQuery.removeExcludedArea(boundingBox1) + compare(exclusionSpy.count, 2) + compare(queryDetailsChangedSpy.count, 2) + compare(emptyQuery.excludedAreas.length, 0) + emptyQuery.removeExcludedArea(boundingBox2) // boundingBox2 isn't in the list, must not impact + compare(exclusionSpy.count, 2) + compare(queryDetailsChangedSpy.count, 2) + emptyQuery.removeExcludedArea(boundingBox1) // doesn't exist anymore, must not impact + compare(exclusionSpy.count, 2) + compare(queryDetailsChangedSpy.count, 2) + // Check correct ordering of exclusion + exclusionSpy.clear() + emptyQuery.addExcludedArea(boundingBox1) + emptyQuery.addExcludedArea(boundingBox2) + emptyQuery.addExcludedArea(boundingBox1) + emptyQuery.addExcludedArea(boundingBox2) + compare(exclusionSpy.count, 2) + compare(emptyQuery.excludedAreas[0], boundingBox1) + compare(emptyQuery.excludedAreas[1], boundingBox2) + emptyQuery.removeExcludedArea(boundingBox1) // remove first and check all geos ok + compare(emptyQuery.excludedAreas[0], boundingBox2) + exclusionSpy.clear() + emptyQuery.clearExcludedAreas() + compare(emptyQuery.excludedAreas.length, 0) + compare(exclusionSpy.count, 1) + + // verify that clearing works + emptyQuery.addExcludedArea(unitBox); + compare(emptyQuery.excludedAreas.length, 1); + queryDetailsChangedSpy.clear(); + emptyQuery.clearExcludedAreas(); + compare(queryDetailsChangedSpy.count, 1); + compare(emptyQuery.excludedAreas.length, 0) + + // Feature types and weights + queryDetailsChangedSpy.clear() + compare(emptyQuery.featureTypes.length, 0) + compare(featureTypesSpy.count, 0) + emptyQuery.setFeatureWeight(RouteQuery.TollFeature, RouteQuery.AvoidFeatureWeight); + compare(featureTypesSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + emptyQuery.setFeatureWeight(RouteQuery.HighwayFeature, RouteQuery.PreferFeatureWeight); + compare(featureTypesSpy.count, 2) + compare(queryDetailsChangedSpy.count, 2) + compare(emptyQuery.featureTypes.length, 2) + compare(emptyQuery.featureTypes[0], RouteQuery.TollFeature) + compare(emptyQuery.featureTypes[1], RouteQuery.HighwayFeature) + // Verify feature weights are as set + compare(emptyQuery.featureWeight(RouteQuery.TollFeature), RouteQuery.AvoidFeatureWeight); + compare(emptyQuery.featureWeight(RouteQuery.HighwayFeature), RouteQuery.PreferFeatureWeight); + // Neutralize a weight, feature should disappear + emptyQuery.setFeatureWeight(RouteQuery.TollFeature, RouteQuery.NeutralFeatureWeight); + compare(featureTypesSpy.count, 3) + compare(queryDetailsChangedSpy.count, 3) + compare(emptyQuery.featureTypes.length, 1) + compare(emptyQuery.featureWeight(RouteQuery.TollFeature), RouteQuery.NeutralFeatureWeight); + compare(emptyQuery.featureWeight(RouteQuery.HighwayFeature), RouteQuery.PreferFeatureWeight); + compare(emptyQuery.featureTypes[0], RouteQuery.HighwayFeature) + compare(emptyQuery.featureWeight(emptyQuery.featureTypes[0]), RouteQuery.PreferFeatureWeight) + compare(featureTypesSpy.count, 3) + compare(queryDetailsChangedSpy.count, 3) + compare(emptyQuery.featureTypes.length, 1) + + // Put some feature weights and then reset them with NoFeature + emptyQuery.setFeatureWeight(RouteQuery.FerryFeature, RouteQuery.RequireFeatureWeight); + emptyQuery.setFeatureWeight(RouteQuery.MotorPoolLaneFeature, RouteQuery.DisallowFeatureWeight); + compare(featureTypesSpy.count, 5) + compare(queryDetailsChangedSpy.count, 5) + compare(emptyQuery.featureTypes.length, 3) + emptyQuery.setFeatureWeight(RouteQuery.NoFeature, RouteQuery.NeutralFeatureWeight) + compare(featureTypesSpy.count, 6) + compare(queryDetailsChangedSpy.count, 6) + compare(emptyQuery.featureTypes.length, 0) + + // Segment details + queryDetailsChangedSpy.clear() + compare(segmentDetailSpy.count, 0) + compare(emptyQuery.segmentDetail, RouteQuery.BasicSegmentData) + emptyQuery.segmentDetail = RouteQuery.NoSegmentData + compare(segmentDetailSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.segmentDetail, RouteQuery.NoSegmentData) + emptyQuery.segmentDetail = RouteQuery.NoSegmentData + compare(segmentDetailSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.segmentDetail, RouteQuery.NoSegmentData) + + // Maneuver details + queryDetailsChangedSpy.clear() + compare(maneuverDetailSpy.count, 0) + compare(emptyQuery.maneuverDetail, RouteQuery.BasicManeuvers) + emptyQuery.maneuverDetail = RouteQuery.NoManeuvers + compare(maneuverDetailSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.maneuverDetail, RouteQuery.NoManeuvers) + emptyQuery.maneuverDetail = RouteQuery.NoManeuvers + compare(maneuverDetailSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.maneuverDetail, RouteQuery.NoManeuvers) + + // NumberAlternativeRoutes + queryDetailsChangedSpy.clear() + compare(numberAlterNativeRoutesSpy.count, 0) + compare(emptyQuery.numberAlternativeRoutes, 0) + emptyQuery.numberAlternativeRoutes = 2 + compare(numberAlterNativeRoutesSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.numberAlternativeRoutes, 2) + emptyQuery.numberAlternativeRoutes = 2 + compare(numberAlterNativeRoutesSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.numberAlternativeRoutes, 2) + + // Route optimization + queryDetailsChangedSpy.clear() + compare(routeOptimizationsSpy.count, 0) + compare(emptyQuery.routeOptimizations, RouteQuery.FastestRoute) + emptyQuery.routeOptimizations = RouteQuery.ShortestRoute + compare(routeOptimizationsSpy.count, 1) + compare(queryDetailsChangedSpy.count, 1) + compare(emptyQuery.routeOptimizations, RouteQuery.ShortestRoute) + emptyQuery.routeOptimizations = RouteQuery.ShortestRoute | RouteQuery.MostScenicRoute + compare(routeOptimizationsSpy.count, 2) + compare(queryDetailsChangedSpy.count, 2) + compare(emptyQuery.routeOptimizations, RouteQuery.ShortestRoute | RouteQuery.MostScenicRoute) + + // Must act gracefully + emptyModel.reset() + emptyModel.update() + + // Plugin + compare(pluginSpy.count, 0) + emptyModel.plugin = testPlugin + compare(pluginSpy.count, 1) + compare(emptyModel.plugin, testPlugin) + emptyModel.plugin = testPlugin + compare(pluginSpy.count, 1) + emptyModel.plugin = errorPlugin + compare(pluginSpy.count, 2) + + // Must act gracefully + emptyModel.reset() + emptyModel.update() + } + // Test that model acts gracefully when plugin is not set or is invalid + // (does not support routing) + RouteModel {id: errorModel; plugin: errorPlugin} + RouteModel {id: errorModelNoPlugin} + SignalSpy {id: countInvalidSpy; target: errorModel; signalName: "countChanged"} + SignalSpy {id: errorSpy; target: errorModel; signalName: "errorChanged"} + function test_error_plugin() { + // test plugin not set + compare(errorModelNoPlugin.error,RouteModel.NoError) + errorModelNoPlugin.update() + compare(errorModelNoPlugin.error,RouteModel.EngineNotSetError) + console.log(errorModelNoPlugin.errorString) + + //plugin set but otherwise not offering anything + compare(errorModel.error,RouteModel.EngineNotSetError) + compare(errorModel.errorString,"This error was expected. No worries !") + errorSpy.clear() + errorModel.update() + compare(errorModel.error,RouteModel.EngineNotSetError) + compare(errorModel.errorString,qsTr("Cannot route, route manager not set.")) + compare(errorSpy.count, 1) + errorSpy.clear() + errorModel.cancel() + compare(errorModel.error,RouteModel.NoError) + compare(errorModel.errorString,"") + compare(errorSpy.count, 1) + errorSpy.clear() + errorModel.reset() + compare(errorModel.error,RouteModel.NoError) + compare(errorModel.errorString,"") + compare(errorSpy.count, 0) + errorSpy.clear() + errorModel.update() + compare(errorModel.error,RouteModel.EngineNotSetError) + compare(errorModel.errorString,qsTr("Cannot route, route manager not set.")) + compare(errorSpy.count, 1) + errorSpy.clear() + var data = errorModel.get(-1) + compare(data, null) + } + } + + Plugin { + id: testPlugin_immediate; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "gc_supported"; value: true}, + PluginParameter { name: "gc_finishRequestImmediately"; value: true}, + PluginParameter { name: "gc_validateWellKnownValues"; value: true} + ] + } + + Plugin { + id: testPlugin_slacker; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "gc_finishRequestImmediately"; value: false} + ] + } + + Plugin { + id: testPlugin_slacker_alt + name: "qmlgeo.test.plugin" + allowExperimental: true + PluginParameter { name: "gc_finishRequestImmediately"; value: false} + PluginParameter { name: "gc_alternateGeoRoute"; value: true} + } + + Plugin { + id: basicRoutingPlugin_slacker; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "gc_finishRequestImmediately"; value: false} + ] + } + + property variant rcoordinate1: QtPositioning.coordinate(50, 50) + property variant rcoordinate2: QtPositioning.coordinate(51, 52) + property variant rcoordinate3: QtPositioning.coordinate(53, 54) + property variant rcoordinate4: QtPositioning.coordinate(55, 56) + property variant rcoordinate5: QtPositioning.coordinate(57, 58) + + property variant fcoordinate1: QtPositioning.coordinate(60, 60) + property variant fcoordinate2: QtPositioning.coordinate(61, 62) + property variant fcoordinate3: QtPositioning.coordinate(63, 64) + property variant fcoordinate4: QtPositioning.coordinate(65, 66) + property variant fcoordinate5: QtPositioning.coordinate(67, 68) + + property variant f2coordinate1: QtPositioning.coordinate(60, 60) + property variant f2coordinate2: QtPositioning.coordinate(61, 62) + property variant f2coordinate3: QtPositioning.coordinate(63, 64) + + Waypoint { + id: waypoint1 + coordinate: QtPositioning.coordinate(70, 70) + bearing: 42 + } + + Waypoint { + id: waypoint2 + coordinate: QtPositioning.coordinate(71, 71) + bearing: 43 + + MapParameter { + id: param1 + type: "user_distance" + property real distance: 10 + } + } + + RouteQuery {id: routeQuery} + property var routeQueryDefaultWaypoints: [ + { latitude: 60, longitude: 60 }, + { latitude: 61, longitude: 62 }, + { latitude: 63, longitude: 64 }, + { latitude: 65, longitude: 66 }, + { latitude: 67, longitude: 68 } + ] + property var routeQuery2DefaultWaypoints: [ + f2coordinate1, + f2coordinate2, + f2coordinate3 + ] + RouteQuery { + id: filledRouteQuery + numberAlternativeRoutes: 0 + waypoints: routeQueryDefaultWaypoints + } + RouteQuery { + id: filledRouteQuery2 + numberAlternativeRoutes: 0 + waypoints: routeQuery2DefaultWaypoints + } + RouteModel { + id: routeModelAutomatic; + plugin: testPlugin_slacker; + query: filledRouteQuery; + autoUpdate: true + } + RouteModel { + id: routeModelAutomaticAltImpl; + plugin: testPlugin_slacker_alt; + query: filledRouteQuery; + autoUpdate: true + } + + SignalSpy {id: automaticRoutesSpy; target: routeModelAutomatic; signalName: "routesChanged" } + SignalSpy {id: automaticRoutesSpyAlt; target: routeModelAutomaticAltImpl; signalName: "routesChanged" } + + RouteModel {id: routeModel; plugin: testPlugin_immediate; query: routeQuery } + SignalSpy {id: testRoutesSpy; target: routeModel; signalName: "routesChanged"} + SignalSpy {id: testCountSpy; target: routeModel; signalName: "countChanged" } + SignalSpy {id: testStatusSpy; target: routeModel; signalName: "statusChanged"} + SignalSpy {id: testErrorStringSpy; target: routeModel; signalName: "errorChanged"} + SignalSpy {id: testErrorSpy; target: routeModel; signalName: "errorChanged"} + SignalSpy {id: testWaypointsSpy; target: routeQuery; signalName: "waypointsChanged"} + + RouteModel {id: routeModelSlack; plugin: basicRoutingPlugin_slacker; query: routeQuery } + SignalSpy {id: testRoutesSlackSpy; target: routeModelSlack; signalName: "routesChanged"} + SignalSpy {id: testCountSlackSpy; target: routeModelSlack; signalName: "countChanged" } + SignalSpy {id: testStatusSlackSpy; target: routeModelSlack; signalName: "statusChanged"} + SignalSpy {id: testErrorStringSlackSpy; target: routeModelSlack; signalName: "errorChanged"} + SignalSpy {id: testErrorSlackSpy; target: routeModelSlack; signalName: "errorChanged"} + SignalSpy {id: testPluginSlackSpy; target: routeModelSlack; signalName: "pluginChanged"} + + TestCase { + name: "Routing" + function clear_immediate_model() { + routeModel.reset() + testRoutesSpy.clear() + testCountSpy.clear() + testStatusSpy.clear() + testErrorStringSpy.clear() + testErrorSpy.clear() + } + function clear_slacker_model() { + routeModelSlack.reset() + testRoutesSlackSpy.clear() + testCountSlackSpy.clear() + testStatusSlackSpy.clear() + testErrorStringSlackSpy.clear() + testErrorSlackSpy.clear() + } + + function test_reset() { + clear_immediate_model(); + routeQuery.numberAlternativeRoutes = 72 // 'altroutes - 70' is the echoed errorcode + routeModel.update() + verify (testErrorStringSpy.count > 0) + verify (testErrorSpy.count > 0) + compare (routeModel.errorString, "error") + compare (routeModel.error, RouteModel.CommunicationError) + compare (routeModel.count, 0) + compare (testStatusSpy.count, 2) + compare (routeModel.status, RouteModel.Error) + routeModel.reset() + compare (routeModel.status, RouteModel.Null) + compare (routeModel.errorString, "") + compare (routeModel.error, RouteModel.NoError) + // Check that ongoing req is aborted + clear_slacker_model() + routeQuery.numberAlternativeRoutes = 3 + routeModelSlack.update() + wait (100) + routeModelSlack.reset() + wait (200) + compare (routeModelSlack.count, 0) + // Check that results are cleared + routeModelSlack.update() + tryCompare(routeModelSlack, "count", 3) // numberALternativeRoutes + routeModelSlack.reset() + compare (routeModelSlack.count, 0) + // Check that changing plugin resets any ongoing requests + clear_slacker_model() + routeQuery.numberAlternativeRoutes = 3 + compare (testPluginSlackSpy.count, 0) + routeModelSlack.update() + wait (100) + routeModelSlack.plugin = testPlugin_immediate + wait (200) + compare (routeModelSlack.count, 0) // should be no updates + compare (testPluginSlackSpy.count, 1) + // test that works + routeModelSlack.update() + compare (routeModelSlack.count, 3) + // return back + routeModelSlack.plugin = testPlugin_slacker + } + + function test_error_routing() { + // Basic immediate error + clear_immediate_model(); + routeQuery.numberAlternativeRoutes = 72 // 'altroutes - 70' is the echoed errorcode + routeModel.update() + compare (testErrorStringSpy.count, 1) + compare (testErrorSpy.count, 1) + compare (routeModel.errorString, "error") + compare (routeModel.error, RouteModel.CommunicationError) + compare (routeModel.count, 0) + compare (testStatusSpy.count, 2) + compare (routeModel.status, RouteModel.Error) + // Basic delayed error + clear_slacker_model() + routeQuery.numberAlternativeRoutes = 72 + routeModelSlack.update() + compare (testErrorStringSlackSpy.count, 0) + compare (testErrorSlackSpy.count, 0) + if (routeModelSlack.errorString == "") + tryCompare(testErrorStringSlackSpy, "count", 1) + else + tryCompare(testErrorStringSlackSpy, "count", 2) + compare (routeModelSlack.errorString, "error") + compare (routeModelSlack.error, RouteModel.CommunicationError) + compare (routeModelSlack.count, 0) + // check that we recover + routeQuery.numberAlternativeRoutes = 1 + routeModelSlack.update() + tryCompare(routeModelSlack, "count", 1) + compare (testCountSlackSpy.count, 1) + compare (routeModelSlack.errorString, "") + compare (routeModelSlack.error, RouteModel.NoError) + } + function test_basic_routing() { + compare (testRoutesSpy.count, 0) + compare (routeModel.errorString, "") + compare (routeModel.error, RouteModel.NoError) + compare (testCountSpy.count, 0) + compare (routeModel.count, 0) + compare (routeQuery.waypoints.length, 0) + compare (testWaypointsSpy.count, 0) + routeQuery.addWaypoint(rcoordinate1) + routeQuery.addWaypoint(rcoordinate2) + routeQuery.addWaypoint(rcoordinate3) + routeQuery.addWaypoint(rcoordinate4) + routeQuery.addWaypoint(rcoordinate5) + compare (testWaypointsSpy.count, 5) + compare (routeQuery.waypoints.length, 5) + routeQuery.numberAlternativeRoutes = 1 // how many routes to get back, > 70 indicates error + routeModel.update() + tryCompare (testRoutesSpy, "count", 1) // 5 sec + tryCompare (testCountSpy, "count", 1) + compare (routeModel.count, 1) + // the test plugin echoes waypoints back as the path of the route: + compare (routeQuery.waypoints.length, 5) + compare (routeModel.get(0).path.length, 5) + compare (routeModel.get(0).path[0].latitude, routeQuery.waypoints[0].latitude) + // check reset() functionality + routeModel.reset() + tryCompare (testRoutesSpy, "count", 2) // 5 sec + tryCompare (testCountSpy, "count", 2) + compare (routeModel.count, 0) + + // delayed responses + compare (testRoutesSlackSpy.count, 0) + compare (routeModelSlack.errorString, "") + compare (routeModel.error, RouteModel.NoError) + compare (testCountSlackSpy.count, 0) + compare (routeModelSlack.count, 0) + routeModelSlack.update() + wait (100) + compare (testRoutesSlackSpy.count, 0) + compare (testCountSlackSpy.count, 0) + tryCompare(testRoutesSlackSpy, "count", 1) + compare (testCountSlackSpy.count, 1) + compare(routeModelSlack.count, 1) + compare (routeModelSlack.get(0).path.length, 5) + compare (routeModelSlack.get(0).path[0].latitude, routeQuery.waypoints[0].latitude) + + // Frequent updates, previous requests are aborted + routeModelSlack.reset() + testRoutesSlackSpy.clear() + testCountSlackSpy.clear() + routeModelSlack.update() + wait (100) + compare(testRoutesSlackSpy.count, 0) + compare(testCountSlackSpy.count, 0) + routeModelSlack.update() + wait (100) + compare(testRoutesSlackSpy.count, 0) + compare(testCountSlackSpy.count, 0) + routeModelSlack.update() + wait (100) + compare(testRoutesSlackSpy.count, 0) + compare(testCountSlackSpy.count, 0) + routeModelSlack.update() + wait (100) + compare(testRoutesSlackSpy.count, 0) + compare(testCountSlackSpy.count, 0) + tryCompare(testRoutesSlackSpy, "count", 1) + compare(testCountSlackSpy.count, 1) + compare(routeModelSlack.count, 1) + + test_basic_routing_automatic(routeModelAutomatic, automaticRoutesSpy, "routeModelAutomatic") + test_basic_routing_automatic(routeModelAutomaticAltImpl, automaticRoutesSpyAlt, "routeModelAutomaticAltImpl") + } + + function test_basic_routing_automatic(model, spy, label) { + if (label === undefined) + return + console.log("testing",label) + // Autoupdate + spy.clear(); + filledRouteQuery.numberAlternativeRoutes = 1 // 'altroutes - 70' is the echoed errorcode + tryCompare (spy, "count", 1) // 5 sec + compare(model.count, 1) // There should be a route already + compare (model.get(0).path.length, 5) + compare (model.get(0).path[0].latitude, filledRouteQuery.waypoints[0].latitude) + + if (label === "routeModelAutomaticAltImpl") // Test that it is an altImpl + compare(model.get(0).travelTime, 123456) + + // Remove a waypoint and check that autoupdate works + filledRouteQuery.removeWaypoint(fcoordinate2) + tryCompare (spy, "count", 2) + compare (model.get(0).path.length, 4) + compare (model.get(0).path[0].latitude, fcoordinate1.latitude) + + // Add a waypoint and check that autoupdate works + filledRouteQuery.addWaypoint(fcoordinate2); + tryCompare (spy, "count", 3) + compare(model.count, 1); + compare(model.get(0).path.length, 5); + compare(model.get(0).path[0].latitude, filledRouteQuery.waypoints[0].latitude); + + // Change contents of a coordinate and check that autoupdate works + filledRouteQuery.waypoints = [ + { latitude: fcoordinate1.latitude + 1, longitude: fcoordinate1.longitude }, + { latitude: 61, longitude: 62 }, + { latitude: 63, longitude: 64 }, + { latitude: 65, longitude: 66 }, + { latitude: 67, longitude: 68 } + ]; + tryCompare (spy, "count", 4) + compare(model.get(0).path[0].latitude, fcoordinate1.latitude + 1) // new value should be echoed + + // Extra parameter + var param = Qt.createQmlObject ('import QtLocation 5.9; MapParameter { type : "test-traveltime"; property var requestedTime : 42}', root) + var initialParams = cloneArray(filledRouteQuery.quickChildren) + var modifiedParams = cloneArray(initialParams) + modifiedParams.push(param) + + filledRouteQuery.quickChildren = modifiedParams + tryCompare (spy, "count", 5) + if (label === "routeModelAutomaticAltImpl") + compare(model.get(0).travelTime, 123456) + else + compare(model.get(0).travelTime, 42) + param.requestedTime = 43 + tryCompare (spy, "count", 6) + if (label === "routeModelAutomaticAltImpl") + compare(model.get(0).travelTime, 123456) + else + compare(model.get(0).travelTime, 43) + filledRouteQuery.quickChildren = initialParams + tryCompare (spy, "count", 7) + if (label === "routeModelAutomaticAltImpl") + compare(model.get(0).travelTime, 123456) + else + compare(model.get(0).travelTime, 0) + var secondParam = Qt.createQmlObject ('import QtLocation 5.9; MapParameter { type : "foo"; property var bar : 42}', root) + modifiedParams.push(secondParam) + param.requestedTime = 44 + filledRouteQuery.quickChildren = modifiedParams + tryCompare (spy, "count", 8) + if (label === "routeModelAutomaticAltImpl") + compare(model.get(0).travelTime, 123456) + else + compare(model.get(0).travelTime, 44) + filledRouteQuery.quickChildren = initialParams + tryCompare (spy, "count", 9) + if (label === "routeModelAutomaticAltImpl") + compare(model.get(0).travelTime, 123456) + else + compare(model.get(0).travelTime, 0) + + /* Test waypoints */ + // Verify that bearing is NaN for coordinates + verify(isNaN(filledRouteQuery.waypointObjects()[0].bearing)) + var numWaypoints = filledRouteQuery.waypoints.length + // Add a waypoint with bearing + filledRouteQuery.addWaypoint(waypoint1) + tryCompare (spy, "count", 10) + compare(filledRouteQuery.waypointObjects()[numWaypoints].bearing, 42) + // testing Waypoint to coordinate conversion + compare(filledRouteQuery.waypoints[numWaypoints], filledRouteQuery.waypointObjects()[numWaypoints].coordinate) + waypoint1.latitude += 0.1 + compare(model.get(0).distance, 0) + tryCompare (spy, "count", 11) + numWaypoints++; + filledRouteQuery.addWaypoint(waypoint2) // waypoint2 contains a MapParameter with user_distance + numWaypoints++; + tryCompare (spy, "count", 12) + compare(filledRouteQuery.waypointObjects()[numWaypoints-1].bearing, 43) + compare(model.get(0).distance, 10) + waypoint1.latitude += 0.1 + tryCompare (spy, "count", 13) + waypoint2.latitude += 0.1 + tryCompare (spy, "count", 14) + filledRouteQuery.removeWaypoint(waypoint1) + tryCompare (spy, "count", 15) + waypoint2.latitude += 0.1 + tryCompare (spy, "count", 16) + waypoint1.latitude += 0.1 + tryCompare (spy, "count", 16) // No effect, now disconnected + // test with other props + waypoint2.longitude += 0.1 + tryCompare (spy, "count", 17) + waypoint2.altitude = 42 + tryCompare (spy, "count", 18) + waypoint2.bearing += 1 + tryCompare (spy, "count", 19) + compare(waypoint2.longitude, 71.1) + compare(waypoint2.altitude, 42) + compare(waypoint2.bearing, 44) + // test with map parameters + param1.distance = 42 + tryCompare (spy, "count", 20) + compare(model.get(0).distance, 42) + + + // Change query + model.query = filledRouteQuery2 + filledRouteQuery2.numberAlternativeRoutes = 3 + tryCompare (spy, "count", 21) + compare (model.get(0).path.length, 3) + + // Verify that the old query is disconnected internally ie. does not trigger update + filledRouteQuery.waypoints = [ + { latitude: fcoordinate1.latitude + 2, longitude: fcoordinate1.longitude }, + { latitude: 61, longitude: 62 }, + { latitude: 63, longitude: 64 }, + { latitude: 65, longitude: 66 }, + { latitude: 67, longitude: 68 } + ]; + wait(800) // wait to hope no further updates comes through + compare (spy.count, 21) + compare(model.get(0).path.length, 3); + + // ReSetting + filledRouteQuery.numberAlternativeRoutes = 0 + filledRouteQuery2.numberAlternativeRoutes = 0 + filledRouteQuery.waypoints = routeQueryDefaultWaypoints + filledRouteQuery2.waypoints = routeQuery2DefaultWaypoints + + waypoint1.coordinate = QtPositioning.coordinate(70, 70) + waypoint2.bearing = 42 + waypoint2.coordinate = QtPositioning.coordinate(71, 71) + waypoint2.bearing = 43 + param1.distance = 10 + } + + + + function test_route_query_handles_destroyed_qml_objects() { + var coordinate = QtPositioning.coordinate(11, 52); + routeQuery.addWaypoint(coordinate); + wait(300); + routeQuery.clearWaypoints(); + } + } +} + + + + diff --git a/tests/auto/declarative_core/tst_supplier.qml b/tests/auto/declarative_core/tst_supplier.qml new file mode 100644 index 0000000..3fcb0d1 --- /dev/null +++ b/tests/auto/declarative_core/tst_supplier.qml @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "Supplier" + + Supplier { id: emptySupplier } + + function test_empty() { + compare(emptySupplier.supplierId, ""); + compare(emptySupplier.name, ""); + compare(emptySupplier.url, ""); + verify(emptySupplier.icon); + } + + Supplier { + id: qmlSupplier + + supplierId: "test-supplier-id" + name: "Test Supplier" + url: "http://example.com/test-supplier-id" + + icon: Icon { + Component.onCompleted: { + parameters.singleUrl = "http://example.com/icons/test-supplier.png" + } + } + } + + function test_qmlConstructedSupplier() { + compare(qmlSupplier.supplierId, "test-supplier-id"); + compare(qmlSupplier.name, "Test Supplier"); + compare(qmlSupplier.url, "http://example.com/test-supplier-id"); + verify(qmlSupplier.icon); + compare(qmlSupplier.icon.parameters.singleUrl, "http://example.com/icons/test-supplier.png"); + } + + Supplier { + id: testSupplier + } + + Plugin { + id: testPlugin + name: "qmlgeo.test.plugin" + allowExperimental: true + } + + Plugin { + id: invalidPlugin + } + + Icon { + id: testIcon + } + + function test_setAndGet_data() { + return [ + { tag: "name", property: "name", signal: "nameChanged", value: "Test Supplier", reset: "" }, + { tag: "supplierId", property: "supplierId", signal: "supplierIdChanged", value: "test-supplier-id-1", reset: "" }, + { tag: "url", property: "url", signal: "urlChanged", value: "http://example.com/test-supplier-id-1", reset: "" }, + { tag: "icon", property: "icon", signal: "iconChanged", value: testIcon } + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testSupplier, data); + } +} diff --git a/tests/auto/declarative_core/tst_user.qml b/tests/auto/declarative_core/tst_user.qml new file mode 100644 index 0000000..1453c41 --- /dev/null +++ b/tests/auto/declarative_core/tst_user.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtTest 1.0 +import QtLocation 5.3 +import "utils.js" as Utils + +TestCase { + id: testCase + + name: "User" + + User { id: emptyUser } + + function test_empty() { + compare(emptyUser.userId, ""); + compare(emptyUser.name, ""); + } + + User { + id: qmlUser + + userId: "testuser" + name: "Test User" + } + + function test_qmlConstructedUser() { + compare(qmlUser.userId, "testuser"); + compare(qmlUser.name, "Test User"); + } + + User { + id: testUser + } + + function test_setAndGet_data() { + return [ + { tag: "userId", property: "userId", signal: "userIdChanged", value: "testuser", reset: "" }, + { tag: "name", property: "name", signal: "nameChanged", value: "Test User", reset: "" }, + ]; + } + + function test_setAndGet(data) { + Utils.testObjectProperties(testCase, testUser, data); + } +} diff --git a/tests/auto/declarative_core/utils.js b/tests/auto/declarative_core/utils.js new file mode 100644 index 0000000..5370bab --- /dev/null +++ b/tests/auto/declarative_core/utils.js @@ -0,0 +1,182 @@ +.pragma library + +function compareArray(a, b) { + if (a.length !== b.length) + return false; + + for (var i = 0; i < a.length; ++i) { + var aMatched = false; + var bMatched = false; + + for (var j = 0; j < b.length; ++j) { + if (a[i] === b[j]) + aMatched = true; + if (b[i] === a[j]) + bMatched = true; + if (aMatched && bMatched) + break; + } + + if (!aMatched || !bMatched) + return false; + } + + return true; +} + +function testObjectProperties(testCase, testObject, data) { + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = testObject; + signalSpy.signalName = data.signal; + + // set property to something new + testObject[data.property] = data.value; + if (data.array) { + if (data.expectedValue) { + testCase.verify(compareArray(testObject[data.property], data.expectedValue)); + testCase.compare(signalSpy.count, 1 + data.expectedValue.length); + } else { + testCase.verify(compareArray(testObject[data.property], data.value)); + testCase.compare(signalSpy.count, 1 + data.value.length); + } + + } else { + testCase.compare(testObject[data.property], data.value); + testCase.compare(signalSpy.count, 1); + } + + signalSpy.clear(); + + // set property to same value + testObject[data.property] = data.value; + if (data.array) { + if (data.expectedValue) { + testCase.verify(compareArray(testObject[data.property], data.expectedValue)); + testCase.compare(signalSpy.count, 1 + data.expectedValue.length); + } else { + testCase.verify(compareArray(testObject[data.property], data.value)); + testCase.compare(signalSpy.count, 1 + data.value.length); + } + + } else { + testCase.compare(testObject[data.property], data.value); + testCase.compare(signalSpy.count, 0); + } + + signalSpy.clear(); + + // reset property + if (data.reset === undefined) { + testObject[data.property] = null; + testCase.compare(testObject[data.property], null); + } else { + testObject[data.property] = data.reset; + if (data.array) + testCase.verify(compareArray(testObject[data.property], data.reset)); + else + testCase.compare(testObject[data.property], data.reset); + } + testCase.compare(signalSpy.count, 1); + signalSpy.destroy(); +} + +function compareObj(testCase, obj1, obj2) { + for (var propertyName in obj2) { + if (obj1[propertyName] !== undefined) { + if (propertyName === "dateTime" && isNaN(obj2["dateTime"].getTime())) + testCase.verify(isNaN(obj1["dateTime"].getTime())); + else + testCase.compare(obj1[propertyName], obj2[propertyName]) + } + } +} + +function testConsecutiveFetch(testCase, model, place, expectedValues, data) +{ + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', testCase, "SignalSpy"); + signalSpy.target = model; + signalSpy.signalName ="totalCountChanged"; + + var visDataModel = Qt.createQmlObject('import QtQuick 2.0; ' + + 'VisualDataModel{ delegate: Text{} }', + testCase, "dataModel"); + visDataModel.model = model; + + //check that initial values are as expected + testCase.compare(model.totalCount, -1); + testCase.compare(model.place, null); + testCase.compare(visDataModel.items.count, 0); + + //perform an initial fetch with the default batch size + model.batchSize = data.batchSize + model.place = place; + testCase.tryCompare(signalSpy, "count", 1); + signalSpy.clear(); + + var totalCount = model.totalCount; + testCase.compare(totalCount, 5); + testCase.compare(visDataModel.items.count, Math.min(data.batchSize, totalCount)); + + compareObj(testCase, visDataModel.items.get(0).model, expectedValues[0]); + + //fetch remaining items, in batchSize batches + while (visDataModel.items.count < totalCount) { + var startIndex = visDataModel.items.count + + //'creating' the last item will trigger a fetch + visDataModel.items.create(visDataModel.items.count - 1); + + testCase.tryCompare(visDataModel.items, "count", Math.min(totalCount, startIndex + data.batchSize)); + testCase.compare(signalSpy.count, 0); + testCase.compare(model.totalCount, totalCount); + + for (var i = startIndex; i < Math.min(totalCount, startIndex + data.batchSize); ++i) + compareObj(testCase, visDataModel.items.get(i).model, expectedValues[i]); + } + + visDataModel.destroy(); + signalSpy.destroy(); +} + +function testReset(testCase, model, place) +{ + var dataModel = Qt.createQmlObject('import QtQuick 2.0; ' + + 'VisualDataModel{ delegate: Text{} }', + testCase, "dataModel"); + + dataModel.model = model; + model.place = place; + testCase.wait(1); + testCase.verify(model.totalCount > 0); + testCase.verify(dataModel.items.count > 0); + + model.place = null; + testCase.tryCompare(model, "totalCount", -1); + testCase.compare(dataModel.items.count, 0); + + dataModel.destroy(); +} + +function testFetch(testCase, data) +{ + var model = data.model; + var visDataModel = Qt.createQmlObject('import QtQuick 2.0; ' + + 'VisualDataModel{ delegate: Text{} }', + testCase, "dataModel"); + visDataModel.model = model + + var signalSpy = Qt.createQmlObject('import QtTest 1.0; SignalSpy {}', + testCase, "SignalSpy"); + signalSpy.target = model; + signalSpy.signalName ="totalCountChanged"; + + model.batchSize = data.batchSize; + model.place = data.place; + testCase.tryCompare(signalSpy, "count", 1); + signalSpy.clear(); + testCase.compare(model.totalCount, data.expectedTotalCount); + testCase.compare(visDataModel.items.count, data.expectedCount); + + visDataModel.destroy(); + signalSpy.destroy(); +} diff --git a/tests/auto/declarative_geoshape/declarative_geoshape.pro b/tests/auto/declarative_geoshape/declarative_geoshape.pro new file mode 100644 index 0000000..aa6647f --- /dev/null +++ b/tests/auto/declarative_geoshape/declarative_geoshape.pro @@ -0,0 +1,11 @@ +# QML tests in this directory must not depend on an OpenGL context. + +TEMPLATE = app +TARGET = tst_declarative_geoshape +CONFIG += qmltestcase +SOURCES += main.cpp + +QT += positioning quick + +OTHER_FILES = *.qml +TESTDATA = $$OTHER_FILES diff --git a/tests/auto/declarative_geoshape/main.cpp b/tests/auto/declarative_geoshape/main.cpp new file mode 100644 index 0000000..ffc9445 --- /dev/null +++ b/tests/auto/declarative_geoshape/main.cpp @@ -0,0 +1,30 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +QUICK_TEST_MAIN(declarative_geoshape) diff --git a/tests/auto/declarative_geoshape/tst_locationsingleton.qml b/tests/auto/declarative_geoshape/tst_locationsingleton.qml new file mode 100644 index 0000000..3d6a9f9 --- /dev/null +++ b/tests/auto/declarative_geoshape/tst_locationsingleton.qml @@ -0,0 +1,317 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtPositioning 5.2 +import QtLocation 5.5 + +Item { + id: testCase + + property variant coordinate1: QtPositioning.coordinate(1, 1) + property variant coordinate2: QtPositioning.coordinate(2, 2) + property variant coordinate3: QtPositioning.coordinate(80, 80) + + property variant emptyCircle: QtPositioning.circle() + property variant circle1: QtPositioning.circle(coordinate1, 200000) + + SignalSpy { id: circleChangedSpy; target: testCase; signalName: "emptyCircleChanged" } + + TestCase { + name: "Bounding circle" + function test_circle_defaults_and_setters() { + circleChangedSpy.clear(); + compare (emptyCircle.radius, -1) + compare (circle1.radius, 200000) + + emptyCircle.radius = 200 + compare(circleChangedSpy.count, 1); + emptyCircle.radius = 200; + compare(circleChangedSpy.count, 1); + + emptyCircle.center = coordinate1; + compare(circleChangedSpy.count, 2); + emptyCircle.center = coordinate1 + compare(circleChangedSpy.count, 2); + emptyCircle.center = coordinate2 + compare(circleChangedSpy.count, 3); + + emptyCircle.center = coordinate1 + emptyCircle.radius = 200000 + + compare(emptyCircle.contains(coordinate1), true); + compare(emptyCircle.contains(coordinate2), true); + compare(emptyCircle.contains(coordinate3), false); + } + } + + property variant trace1 : [ QtPositioning.coordinate(43.773175, 11.255386), + QtPositioning.coordinate(43.773546 , 11.255372) ] + property variant trace2 : [ QtPositioning.coordinate(43.773175, 11.255386), + QtPositioning.coordinate(43.773546 , 11.255372), + QtPositioning.coordinate(43.77453 , 11.255734)] + + + // coordinate unit square + property variant bl: QtPositioning.coordinate(0, 0) + property variant tl: QtPositioning.coordinate(1, 0) + property variant tr: QtPositioning.coordinate(1, 1) + property variant br: QtPositioning.coordinate(0, 1) + property variant ntr: QtPositioning.coordinate(3, 3) + + property variant invalid: QtPositioning.coordinate(100, 190) + property variant inside: QtPositioning.coordinate(0.5, 0.5) + property variant outside: QtPositioning.coordinate(2, 2) + + property variant box: QtPositioning.rectangle(tl, br) + + property variant coordinates: [bl, tl, tr, br] + property variant coordinates2: [bl, tl, tr, br, ntr] + property variant coordinates3: [tr] + property variant coordinates4: [invalid] + property variant coordinates5: [] + + property variant listBox: QtPositioning.rectangle(coordinates) + property variant listBox2: QtPositioning.rectangle(coordinates2) + property variant listBox3: QtPositioning.rectangle(coordinates3) + property variant listBox4: QtPositioning.rectangle(coordinates4) + property variant listBox5: QtPositioning.rectangle(coordinates5) + + property variant widthBox: QtPositioning.rectangle(inside, 1, 1); + + // C++ auto test exists for basics of bounding box, testing here + // only added functionality + TestCase { + name: "Bounding box" + function test_box_defaults_and_setters() { + compare (box.bottomRight.longitude, br.longitude) // sanity + compare (box.contains(bl), true) + compare (box.contains(inside), true) + compare (box.contains(outside), false) + box.topRight = ntr + compare (box.contains(outside), true) + + compare (listBox.isValid, true) + compare (listBox.contains(outside), false) + compare (listBox2.contains(outside), true) + compare (listBox3.isValid, true) + compare (listBox3.isEmpty, true) + compare (listBox4.isValid, false) + compare (listBox5.isValid, false) + + compare (widthBox.contains(inside), true) + compare (widthBox.contains(outside), false) + } + } + + TestCase { + name: "Shape" + + function test_shape_comparison_data() { + return [ + { tag: "invalid shape", shape1: QtPositioning.shape(), shape2: QtPositioning.shape(), result: true }, + { tag: "box equal", shape1: box, shape2: QtPositioning.rectangle(tl, br), result: true }, + { tag: "box not equal", shape1: box, shape2: QtPositioning.rectangle([inside, outside]), result: false }, + { tag: "box invalid shape", rect1: box, shape2: QtPositioning.shape(), result: false }, + { tag: "invalid rectangle", shape1: QtPositioning.rectangle(), shape2: QtPositioning.rectangle(), result: true }, + { tag: "invalid rectangle2", shape1: QtPositioning.rectangle(), shape2: QtPositioning.shape(), result: false }, + { tag: "circle1 equal", shape1: circle1, shape2: QtPositioning.circle(coordinate1, 200000), result: true }, + { tag: "circle1 not equal", shape1: circle1, shape2: QtPositioning.circle(coordinate2, 2000), result: false }, + { tag: "circle1 invalid shape", shape1: circle1, shape2: QtPositioning.shape(), result: false }, + { tag: "invalid circle", shape1: QtPositioning.circle(), shape2: QtPositioning.circle(), result: true }, + { tag: "invalid circle2", shape1: QtPositioning.circle(), shape2: QtPositioning.shape(), result: false } + ] + } + + function test_shape_comparison(data) { + compare(data.shape1 === data.shape2, data.result) + compare(data.shape1 !== data.shape2, !data.result) + compare(data.shape1 == data.shape2, data.result) + compare(data.shape1 != data.shape2, !data.result) + } + } + + TestCase { + name: "Conversions" + + function test_shape_circle_conversions() { + var circle = QtPositioning.shapeToCircle(QtPositioning.shape()) + verify(!circle.isValid) + circle = QtPositioning.shapeToCircle(QtPositioning.circle()) + verify(!circle.isValid) + circle = QtPositioning.shapeToCircle(QtPositioning.circle(tl, 10000)) + verify(circle.isValid) + compare(circle.center, tl) + compare(circle.radius, 10000) + circle = QtPositioning.shapeToCircle(QtPositioning.rectangle()) + verify(!circle.isValid) + circle = QtPositioning.shapeToCircle(QtPositioning.rectangle(tl, br)) + verify(!circle.isValid) + circle = QtPositioning.shapeToCircle(listBox) + verify(!circle.isValid) + } + + function test_shape_rectangle_conversions() { + var rectangle = QtPositioning.shapeToRectangle(QtPositioning.shape()) + verify(!rectangle.isValid) + rectangle = QtPositioning.shapeToRectangle(QtPositioning.circle()) + verify(!rectangle.isValid) + rectangle = QtPositioning.shapeToRectangle(QtPositioning.circle(tl, 10000)) + verify(!rectangle.isValid) + rectangle = QtPositioning.shapeToRectangle(QtPositioning.rectangle()) + verify(!rectangle.isValid) + rectangle = QtPositioning.shapeToRectangle(QtPositioning.rectangle(tl, br)) + verify(rectangle.isValid) + compare(rectangle.topLeft, tl) + compare(rectangle.bottomRight, br) + rectangle = QtPositioning.shapeToRectangle(listBox) + verify(rectangle.isValid) + } + + function test_shape_path_conversions() { + var path = QtPositioning.shapeToPath(QtPositioning.shape()) + verify(!path.isValid) + path = QtPositioning.shapeToPath(QtPositioning.circle()) + verify(!path.isValid) + path = QtPositioning.shapeToPath(QtPositioning.circle(tl, 10000)) + verify(!path.isValid) + path = QtPositioning.shapeToPath(QtPositioning.rectangle()) + verify(!path.isValid) + path = QtPositioning.shapeToPath(QtPositioning.rectangle(tl, br)) + verify(!path.isValid) + + path = QtPositioning.shapeToPath(QtPositioning.path()) + verify(!path.isValid) + path = QtPositioning.shapeToPath(QtPositioning.path(trace1, 1)) + verify(path.isValid) + path = QtPositioning.shapeToPath(QtPositioning.path(trace2, 2)) + verify(path.isValid) + verify(path !== QtPositioning.shapeToPath(QtPositioning.path(trace1, 1))) + compare(path, QtPositioning.shapeToPath(QtPositioning.path(trace2, 2))) + } + } + + + MapPolyline { + id: mapPolyline + path: [ + { latitude: -27, longitude: 153.0 }, + { latitude: -27, longitude: 154.1 }, + { latitude: -28, longitude: 153.5 }, + { latitude: -29, longitude: 153.5 } + ] + } + + MapPolyline { + id: mapPolylineGeopath + } + + TestCase { + name: "MapPolyline path" + function test_path_operations() { + compare(mapPolyline.path[1].latitude, -27) + compare(mapPolyline.path[1].longitude, 154.1) + compare(mapPolyline.coordinateAt(1), QtPositioning.coordinate(-27, 154.1)) + compare(mapPolyline.path.length, mapPolyline.pathLength()) + + mapPolyline.removeCoordinate(1); + compare(mapPolyline.path[1].latitude, -28) + compare(mapPolyline.path[1].longitude, 153.5) + compare(mapPolyline.coordinateAt(1), QtPositioning.coordinate(-28, 153.5)) + compare(mapPolyline.path.length, mapPolyline.pathLength()) + + mapPolyline.addCoordinate(QtPositioning.coordinate(30, 153.1)) + compare(mapPolyline.path[mapPolyline.path.length-1].latitude, 30) + compare(mapPolyline.path[mapPolyline.path.length-1].longitude, 153.1) + compare(mapPolyline.containsCoordinate(QtPositioning.coordinate(30, 153.1)), true) + compare(mapPolyline.path.length, mapPolyline.pathLength()) + + mapPolyline.removeCoordinate(QtPositioning.coordinate(30, 153.1)) + compare(mapPolyline.path[mapPolyline.path.length-1].latitude, -29) + compare(mapPolyline.path[mapPolyline.path.length-1].longitude, 153.5) + compare(mapPolyline.containsCoordinate(QtPositioning.coordinate(30, 153.1)), false) + compare(mapPolyline.path.length, mapPolyline.pathLength()) + + mapPolyline.insertCoordinate(2, QtPositioning.coordinate(35, 153.1)) + compare(mapPolyline.path[2].latitude, 35) + compare(mapPolyline.path[2].longitude, 153.1) + compare(mapPolyline.containsCoordinate(QtPositioning.coordinate(35, 153.1)), true) + compare(mapPolyline.path.length, mapPolyline.pathLength()) + + mapPolyline.replaceCoordinate(2, QtPositioning.coordinate(45, 150.1)) + compare(mapPolyline.path[2].latitude, 45) + compare(mapPolyline.path[2].longitude, 150.1) + compare(mapPolyline.containsCoordinate(QtPositioning.coordinate(35, 153.1)), false) + compare(mapPolyline.containsCoordinate(QtPositioning.coordinate(45, 150.1)), true) + compare(mapPolyline.path.length, mapPolyline.pathLength()) + + mapPolyline.insertCoordinate(2, QtPositioning.coordinate(35, 153.1)) + compare(mapPolyline.coordinateAt(2).latitude, 35) + compare(mapPolyline.coordinateAt(2).longitude, 153.1) + compare(mapPolyline.containsCoordinate(QtPositioning.coordinate(35, 153.1)), true) + compare(mapPolyline.path.length, mapPolyline.pathLength()) + } + } + + TestCase { + name: "GeoPath path" + function test_qgeopath_path_operations() { + var geopath = QtPositioning.path() + + geopath.path = trace2 + compare(geopath.path.length, trace2.length) + + geopath.path = mapPolyline.path + compare(geopath.path.length, mapPolyline.pathLength()) + compare(geopath.boundingGeoRectangle(), mapPolyline.geoShape.boundingGeoRectangle()) + + mapPolylineGeopath.path = mapPolyline.path + compare(mapPolylineGeopath.pathLength(), mapPolyline.pathLength()) + compare(mapPolylineGeopath.geoShape.boundingGeoRectangle(), mapPolyline.geoShape.boundingGeoRectangle()) + + try { + var err = false; + mapPolylineGeopath.geoShape = geopath + } catch (e) { + if (e.message != 'Cannot assign to read-only property "geoShape"') + fail('Expected Cannot assign to read-only property "geoShape", got: ' + e.message); + err = true; + } finally { + verify(err, 'should throw Cannot assign to read-only property "geoShape"'); + } + + geopath.path = trace2 + geopath.path[0].longitude = 11.0 + compare(geopath.path.length, trace2.length) + compare(geopath.coordinateAt(0).latitude, trace2[0].latitude) + expectFail("", "Longitude comparison fails") + compare(geopath.coordinateAt(0).longitude, 11) + } + } +} diff --git a/tests/auto/declarative_ui/BLACKLIST b/tests/auto/declarative_ui/BLACKLIST new file mode 100644 index 0000000..4abe024 --- /dev/null +++ b/tests/auto/declarative_ui/BLACKLIST @@ -0,0 +1,20 @@ +[MapFlick::test_disable_onFlickStarted_with_disabled] +windows + +[MapFlick::test_flick_down] +windows + +[MapFlick::test_flick_up] +windows + +[MapFlick::test_flick_down_with_filtering] +windows + +[MapFlick::test_disable_onFlickStarted_with_nogesture] +windows + +[MapFlick::test_flick_diagonal] +windows + +[MapFlick::test_flick_up_with_filtering] +windows diff --git a/tests/auto/declarative_ui/ItemGroup.qml b/tests/auto/declarative_ui/ItemGroup.qml new file mode 100644 index 0000000..57108ec --- /dev/null +++ b/tests/auto/declarative_ui/ItemGroup.qml @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.4 +import QtPositioning 5.6 +import QtLocation 5.9 +import QtLocation.Test 5.6 + +MapItemGroup { + id: itemGroup + property double latitude : (mainRectangle.topLeft.latitude + mainRectangle.bottomRight.latitude) / 2.0 + property double longitude: (mainRectangle.topLeft.longitude + mainRectangle.bottomRight.longitude) / 2.0 + property double radius: 100 * 1000 + + MapRectangle { + id: mainRectangle + topLeft: QtPositioning.coordinate(43, -3) + bottomRight: QtPositioning.coordinate(37, 3) + opacity: 0.05 + visible: true + color: 'blue' + } + + MapCircle { + id: groupCircle + center: QtPositioning.coordinate(parent.latitude, parent.longitude) + radius: parent.radius + color: 'crimson' + } + + MapRectangle { + id: groupRectangle + topLeft: QtPositioning.coordinate(parent.latitude + 5, parent.longitude - 5) + bottomRight: QtPositioning.coordinate(parent.latitude, parent.longitude ) + color: 'yellow' + } +} diff --git a/tests/auto/declarative_ui/declarative_ui.pro b/tests/auto/declarative_ui/declarative_ui.pro new file mode 100644 index 0000000..6734b1f --- /dev/null +++ b/tests/auto/declarative_ui/declarative_ui.pro @@ -0,0 +1,21 @@ +# QML tests in this directory depend on a Qt platform plugin that supports OpenGL. +# QML tests that do not require an OpenGL context should go in ../declarative_core. + +TEMPLATE = app +TARGET = tst_declarative_ui +!no_ui_tests:CONFIG += qmltestcase +SOURCES += main.cpp + +CONFIG -= app_bundle + +QT += location quick + +OTHER_FILES = *.qml +TESTDATA = $$OTHER_FILES + + +# Import path used by 'make check' since CI doesn't install test imports +IMPORTPATH = $$OUT_PWD/../../../qml + +DISTFILES += \ + ItemGroup.qml diff --git a/tests/auto/declarative_ui/main.cpp b/tests/auto/declarative_ui/main.cpp new file mode 100644 index 0000000..49e64bc --- /dev/null +++ b/tests/auto/declarative_ui/main.cpp @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +static void initializeLibraryPath() +{ +#if QT_CONFIG(library) + // Set custom path since CI doesn't install test plugins +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif +} + +Q_COREAPP_STARTUP_FUNCTION(initializeLibraryPath) + +QUICK_TEST_MAIN(declarative_ui) diff --git a/tests/auto/declarative_ui/tst_map.qml b/tests/auto/declarative_ui/tst_map.qml new file mode 100644 index 0000000..f05b2c7 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map.qml @@ -0,0 +1,688 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtPositioning 5.5 +import QtLocation 5.10 +import QtLocation.Test 5.6 + +Item { + width:100 + height:100 + // General-purpose elements for the test: + Plugin { id: testPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true } + Plugin { id: testPlugin2; name: "gmlgeo.test.plugin"; allowExperimental: true } + Plugin { + id: testPluginLazyParameter; + name: "qmlgeo.test.plugin" + allowExperimental: true + property string extraTypeName : undefined + PluginParameter { name: "supported"; value: true} + PluginParameter { name: "finishRequestImmediately"; value: true} + PluginParameter { name: "validateWellKnownValues"; value: true} + PluginParameter { name: "extraMapTypeName"; value: testPluginLazyParameter.extraTypeName} + + Component.onCompleted: { + extraTypeName = "SomeString" + } + } + + property variant coordinate1: QtPositioning.coordinate(10, 11) + property variant coordinate2: QtPositioning.coordinate(12, 13) + property variant coordinate3: QtPositioning.coordinate(50, 50, 0) + property variant coordinate4: QtPositioning.coordinate(80, 80, 0) + property variant coordinate5: QtPositioning.coordinate(20, 180) + property variant coordinateCenterVisibleRegion: QtPositioning.coordinate(27, 77) + property variant coordinateVisible1: QtPositioning.coordinate(28, 77) + property variant coordinateVisible2: QtPositioning.coordinate(33, 79.1) + property variant coordinateVisible3: QtPositioning.coordinate(27, 80.5) + property variant invalidCoordinate: QtPositioning.coordinate() + property variant altitudelessCoordinate: QtPositioning.coordinate(50, 50) + property bool allMapsReady: mapZoomOnCompleted.mapReady + && mapZoomDefault.mapReady + && mapZoomUserInit.mapReady + && map.mapReady + && mapPar.mapReady + && coordinateMap.mapReady + && mapTiltBearing.mapReady + && mapTiltBearingHere.mapReady + && mapTestProjection.mapReady + + Map { id: mapZoomOnCompleted; width: 200; height: 200; + zoomLevel: 3; center: coordinate1; plugin: testPlugin; + Component.onCompleted: { + zoomLevel = 7 + } + } + SignalSpy {id: mapZoomSpy; target: mapZoomOnCompleted; signalName: 'zoomLevelChanged'} + + Map { id: mapZoomDefault; width: 200; height: 200; + center: coordinate1; plugin: testPlugin; } + + Map { id: mapZoomUserInit; width: 210; height: 210; + zoomLevel: 4; center: coordinate1; plugin: testPlugin; + Component.onCompleted: { + console.log("mapZoomUserInit completed") + } + } + + Map { id: mapVisibleRegion; width: 800; height: 600; + center: coordinateCenterVisibleRegion; plugin: testPlugin; zoomLevel: 1.0 } + + Map {id: map; plugin: testPlugin; center: coordinate1; width: 100; height: 100} + SignalSpy {id: mapCenterSpy; target: map; signalName: 'centerChanged'} + + Map {id: mapPar; plugin: testPlugin; center: coordinate1; width: 512; height: 512} + + Map {id: coordinateMap; plugin: testPlugin; center: coordinate3; + width: 1000; height: 1000; zoomLevel: 15 } + + Map {id: mapTiltBearing; plugin: testPlugin; center: coordinate1; + width: 1000; height: 1000; zoomLevel: 4; bearing: 45.0; tilt: 25.0 } + + Map {id: mapTiltBearingHere; plugin: testPlugin; center: coordinate1; + width: 1000; height: 1000; zoomLevel: 4; bearing: 45.0; tilt: 25.0 } + + Map { + id: mapWithLazyPlugin + plugin: testPluginLazyParameter + } + + Map { + id: mapTestProjection + plugin: testPlugin + width: 200 + height: 200 + } + + MapParameter { + id: testParameter + type: "cameraCenter_test" + property var center: QtPositioning.coordinate(-33.0, -47.0) + } + + + TestCase { + when: windowShown && allMapsReady + name: "MapProperties" + + function fuzzy_compare(val, ref) { + var tolerance = 0.01; + if ((val > ref - tolerance) && (val < ref + tolerance)) + return true; + console.log('map fuzzy cmp returns false for value, ref: ' + val + ', ' + ref) + return false; + } + + function init() { + mapCenterSpy.clear(); + } + + function test_lazy_parameter() { + compare(mapWithLazyPlugin.supportedMapTypes.length, 5) + compare(mapWithLazyPlugin.supportedMapTypes[4].name, "SomeString") + } + + function test_map_center() { + // coordinate is set at map element declaration + compare(map.center.latitude, 10) + compare(map.center.longitude, 11) + + // change center and its values + mapCenterSpy.clear(); + compare(mapCenterSpy.count, 0) + map.center = coordinate2 + compare(mapCenterSpy.count, 1) + map.center = coordinate2 + compare(mapCenterSpy.count, 1) + + // change center to dateline + mapCenterSpy.clear() + compare(mapCenterSpy.count, 0) + map.center = coordinate5 + compare(mapCenterSpy.count, 1) + compare(map.center, coordinate5) + + map.center = coordinate2 + + verify(isNaN(map.center.altitude)); + compare(map.center.longitude, 13) + compare(map.center.latitude, 12) + } + + function test_map_visible_region() + { + mapVisibleRegion.zoomLevel = 1.0 + wait(50) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3)) + + mapVisibleRegion.zoomLevel = 1.88 + verify(LocationTestHelper.waitForPolished(mapVisibleRegion)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3)) + + mapVisibleRegion.zoomLevel = 2.12 + verify(LocationTestHelper.waitForPolished(mapVisibleRegion)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3)) + + mapVisibleRegion.zoomLevel = 2.5 + verify(LocationTestHelper.waitForPolished(mapVisibleRegion)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3)) + + mapVisibleRegion.zoomLevel = 2.7 + verify(LocationTestHelper.waitForPolished(mapVisibleRegion)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible1)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible2)) + verify(mapVisibleRegion.visibleRegion.contains(coordinateVisible3)) + } + + function test_map_parameters() + { + // coordinate is set at map element declaration + var center = mapPar.toCoordinate(Qt.point((mapPar.width - 1) / 2.0, (mapPar.height - 1) / 2.0)) + fuzzyCompare(center.latitude, 10, 0.1) + fuzzyCompare(center.longitude, 11, 0.1) + + compare(mapPar.mapParameters.length, 0) + + mapPar.addMapParameter(testParameter) + + compare(mapPar.mapParameters.length, 1) + + center = mapPar.toCoordinate(Qt.point((mapPar.width - 1) / 2.0, (mapPar.height - 1) / 2.0)) + fuzzyCompare(center.latitude, -33, 0.1) + fuzzyCompare(center.longitude, -47, 0.1) + + mapPar.addMapParameter(testParameter) + compare(mapPar.mapParameters.length, 1) + + mapPar.removeMapParameter(testParameter) + compare(mapPar.mapParameters.length, 0) + + center = mapPar.toCoordinate(Qt.point((mapPar.width - 1) / 2.0, (mapPar.height - 1) / 2.0)) + fuzzyCompare(center.latitude, -33, 0.1) + fuzzyCompare(center.longitude, -47, 0.1) + + testParameter.center = mapPar.center // map.center has not been affected as it lives in the Declarative Map + mapPar.addMapParameter(testParameter) + compare(mapPar.mapParameters.length, 1) + + center = mapPar.toCoordinate(Qt.point((mapPar.width - 1) / 2.0, (mapPar.height - 1) / 2.0)) + fuzzyCompare(center.latitude, 10, 0.1) + fuzzyCompare(center.longitude, 11, 0.1) + + testParameter.center = QtPositioning.coordinate(-33.0, -47.0) + + center = mapPar.toCoordinate(Qt.point((mapPar.width - 1) / 2.0, (mapPar.height - 1) / 2.0)) + fuzzyCompare(center.latitude, -33, 0.1) + fuzzyCompare(center.longitude, -47, 0.1) + + mapPar.removeMapParameter(testParameter) + compare(mapPar.mapParameters.length, 0) + } + + function test_map_clamp() + { + //valid + map.center = QtPositioning.coordinate(10.0, 20.5, 30.8) + map.zoomLevel = 2.0 + + compare(map.center.latitude, 10) + compare(map.center.longitude, 20.5) + compare(map.center.altitude, 30.8) + + //negative values + map.center = QtPositioning.coordinate(-50, -20, 100) + map.zoomLevel = 1.0 + + compare(map.center.latitude, -50) + compare(map.center.longitude, -20) + compare(map.center.altitude, 100) + + //clamped center negative + map.center = QtPositioning.coordinate(-89, -45, 0) + map.zoomLevel = 1.0 + + fuzzyCompare(map.center.latitude, -80.8728, 0.001) + compare(map.center.longitude, -45) + compare(map.center.altitude, 0) + + //clamped center positive + map.center = QtPositioning.coordinate(86, 38, 0) + map.zoomLevel = 1.0 + + fuzzyCompare(map.center.latitude, 80.8728, 0.001) + compare(map.center.longitude, 38) + compare(map.center.altitude, 0) + } + + function test_zoom_limits() + { + map.center.latitude = 30 + map.center.longitude = 60 + map.zoomLevel = 4 + + //initial plugin values + compare(map.minimumZoomLevel, 0) + compare(map.maximumZoomLevel, 20) + compare(map.activeMapType.cameraCapabilities.minimumZoomLevel, 0) + compare(map.activeMapType.cameraCapabilities.maximumZoomLevel, 20) + + //Higher min level than curr zoom, should change curr zoom + map.minimumZoomLevel = 5 + map.maximumZoomLevel = 18 + compare(map.zoomLevel, 5) + compare(map.minimumZoomLevel, 5) + compare(map.maximumZoomLevel, 18) + + //Trying to set higher than max, max should be set. + map.maximumZoomLevel = 21 + compare(map.minimumZoomLevel, 5) + compare(map.maximumZoomLevel, 20) + + //Negative values should be ignored + map.minimumZoomLevel = -1 + map.maximumZoomLevel = -2 + compare(map.minimumZoomLevel, 5) + compare(map.maximumZoomLevel, 20) + + //Max limit lower than curr zoom, should change curr zoom + map.zoomLevel = 18 + map.maximumZoomLevel = 16 + compare(map.zoomLevel, 16) + compare(map.activeMapType.cameraCapabilities.minimumZoomLevel, 0) + compare(map.activeMapType.cameraCapabilities.maximumZoomLevel, 20) + + //reseting default + map.minimumZoomLevel = 0 + map.maximumZoomLevel = 20 + compare(map.minimumZoomLevel, 0) + compare(map.maximumZoomLevel, 20) + } + + function test_tilt_limits() + { + map.tilt = 0 + + //initial plugin values + compare(map.minimumTilt, 0) + compare(map.maximumTilt, 60) + compare(map.activeMapType.cameraCapabilities.minimumTilt, 0) + compare(map.activeMapType.cameraCapabilities.maximumTilt, 60) + + //Higher min level than curr tilt, should change curr tilt + map.minimumTilt = 5 + map.maximumTilt = 18 + compare(map.tilt, 5) + compare(map.minimumTilt, 5) + compare(map.maximumTilt, 18) + // Capabilities remain the same + compare(map.activeMapType.cameraCapabilities.minimumTilt, 0) + compare(map.activeMapType.cameraCapabilities.maximumTilt, 60) + + //Trying to set higher than max, max should be set. + map.maximumTilt = 61 + compare(map.minimumTilt, 5) + compare(map.maximumTilt, 60) + + //Negative values should be ignored + map.minimumTilt = -1 + map.maximumTilt = -2 + compare(map.minimumTilt, 5) + compare(map.maximumTilt, 60) + + //Max limit lower than curr zoom, should change curr zoom + map.tilt = 18 + map.maximumTilt = 16 + compare(map.tilt, 16) + + //resetting default + map.minimumTilt = 0 + map.maximumTilt = 60 + map.tilt = 0 + compare(map.minimumTilt, 0) + compare(map.maximumTilt, 60) + compare(map.tilt, 0) + } + + function test_fov_limits() + { + map.fieldOfView = 45 + + //initial plugin values + compare(map.minimumFieldOfView, 45) + compare(map.maximumFieldOfView, 45) + compare(map.activeMapType.cameraCapabilities.minimumFieldOfView, 45) + compare(map.activeMapType.cameraCapabilities.maximumFieldOfView, 45) + + map.minimumFieldOfView = 5 + map.maximumFieldOfView = 18 + map.fieldOfView = 4 + compare(map.fieldOfView, 45) + compare(map.minimumFieldOfView, 45) + compare(map.maximumFieldOfView, 45) + + map.activeMapType = map.supportedMapTypes[3] + // camera caps are [1-179], user previously asked for [5-18] + compare(map.minimumFieldOfView, 5) + compare(map.maximumFieldOfView, 18) + compare(map.activeMapType.cameraCapabilities.minimumFieldOfView, 1) + compare(map.activeMapType.cameraCapabilities.maximumFieldOfView, 179) + + map.fieldOfView = 4 + compare(map.fieldOfView, 5) + + //Higher min level than curr fieldOfView, should change curr fieldOfView + map.minimumFieldOfView = 6 + compare(map.fieldOfView, 6) + compare(map.minimumFieldOfView, 6) + compare(map.maximumFieldOfView, 18) + + //Trying to set higher than max, max should be set. + map.maximumFieldOfView = 179.5 + compare(map.minimumFieldOfView, 6) + compare(map.maximumFieldOfView, 179) + + //Negative values should be ignored + map.minimumFieldOfView = -1 + map.maximumFieldOfView = -2 + compare(map.minimumFieldOfView, 6) + compare(map.maximumFieldOfView, 179) + + //Max limit lower than curr zoom, should change curr zoom + map.fieldOfView = 18 + compare(map.fieldOfView, 18) + map.maximumFieldOfView = 16 + compare(map.maximumFieldOfView, 16) + compare(map.fieldOfView, 16) + + //resetting default + map.minimumFieldOfView = 1 + map.maximumFieldOfView = 179 + compare(map.minimumFieldOfView, 1) + compare(map.maximumFieldOfView, 179) + + map.activeMapType = map.supportedMapTypes[0] + compare(map.minimumFieldOfView, 45) + compare(map.maximumFieldOfView, 45) + compare(map.fieldOfView, 45) + compare(map.activeMapType.cameraCapabilities.minimumFieldOfView, 45) + compare(map.activeMapType.cameraCapabilities.maximumFieldOfView, 45) + } + + function test_zoom() + { + wait(1000) + compare(mapZoomOnCompleted.zoomLevel, 7) + compare(mapZoomDefault.zoomLevel, 8) + compare(mapZoomUserInit.zoomLevel, 4) + + mapZoomSpy.clear() + mapZoomOnCompleted.zoomLevel = 6 + tryCompare(mapZoomSpy, "count", 1) + } + + function test_pan() + { + map.center.latitude = 30 + map.center.longitude = 60 + map.zoomLevel = 4 + mapCenterSpy.clear(); + + // up left + tryCompare(mapCenterSpy, "count", 0) + map.pan(-20,-20) + tryCompare(mapCenterSpy, "count", 1) + verify(map.center.latitude > 30) + verify(map.center.longitude < 60) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // up + map.pan(0,-20) + tryCompare(mapCenterSpy, "count", 1) + verify(map.center.latitude > 30) + compare(map.center.longitude, 60) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // up right + tryCompare(mapCenterSpy, "count", 0) + map.pan(20,-20) + tryCompare(mapCenterSpy, "count", 1) + verify(map.center.latitude > 30) + verify(map.center.longitude > 60) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // left + map.pan(-20,0) + tryCompare(mapCenterSpy, "count", 1) + verify (fuzzy_compare(map.center.latitude, 30)) + verify(map.center.longitude < 60) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // center + map.pan(0,0) + tryCompare(mapCenterSpy, "count", 0) + compare(map.center.latitude, 30) + compare(map.center.longitude, 60) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // right + map.pan(20,0) + tryCompare(mapCenterSpy, "count", 1) + verify (fuzzy_compare(map.center.latitude, 30)) + verify(map.center.longitude > 60) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // down left + map.pan(-20,20) + tryCompare(mapCenterSpy, "count", 1) + verify (map.center.latitude < 30 ) + verify (map.center.longitude < 60 ) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // down + map.pan(0,20) + tryCompare(mapCenterSpy, "count", 1) + verify (map.center.latitude < 30 ) + verify (fuzzy_compare(map.center.longitude, 60)) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + // down right + map.pan(20,20) + tryCompare(mapCenterSpy, "count", 1) + verify (map.center.latitude < 30 ) + verify (map.center.longitude > 60 ) + map.center.latitude = 30 + map.center.longitude = 60 + mapCenterSpy.clear() + } + + function test_map_tilt_bearing() + { + compare(map.bearing, 0.0) + compare(map.tilt, 0.0) + compare(mapTiltBearing.bearing, 45.0) + compare(mapTiltBearing.tilt, 25.0) + compare(mapTiltBearingHere.bearing, 45.0) + compare(mapTiltBearingHere.tilt, 25.0) + + mapTiltBearing.bearing = 0.0 + mapTiltBearing.tilt = 0.0 + compare(mapTiltBearing.bearing, 0.0) + compare(mapTiltBearing.tilt, 0.0) + + mapTiltBearing.bearing = 480.0 + mapTiltBearing.tilt = 140.0 + compare(mapTiltBearing.bearing, 120.0) + compare(mapTiltBearing.tilt, 60.0) + + mapTiltBearing.tilt = -140.0 + compare(mapTiltBearing.tilt, 0.0) + + mapTiltBearingHere.bearing = 45.0 + mapTiltBearingHere.tilt = 25.0 + compare(mapTiltBearingHere.bearing, 45.0) + compare(mapTiltBearingHere.tilt, 25.0) + mapTiltBearingHere.bearing = 0.0 + mapTiltBearingHere.tilt = 0.0 + compare(mapTiltBearingHere.bearing, 0.0) + compare(mapTiltBearingHere.tilt, 0.0) + + mapTiltBearing.bearing = 45.0 + mapTiltBearing.tilt = 25.0 + mapTiltBearing.zoomLevel = 8.0 + compare(mapTiltBearing.bearing, 45.0) + compare(mapTiltBearing.tilt, 25.0) + } + + function test_map_setbearing() + { + var zeroCoord = QtPositioning.coordinate(0,0) + mapTiltBearing.bearing = 0.0 + mapTiltBearing.tilt = 0.0 + mapTiltBearing.zoomLevel = 3 + mapTiltBearing.center = zeroCoord + compare(mapTiltBearing.bearing, 0.0) + compare(mapTiltBearing.tilt, 0.0) + compare(mapTiltBearing.zoomLevel, 3) + compare(mapTiltBearing.center, zeroCoord) + + var fulcrum = QtPositioning.coordinate(20,-20) + var fulcrumPos = mapTiltBearing.fromCoordinate(fulcrum) + var bearing = 90.0 + mapTiltBearing.setBearing(bearing, fulcrum) + var fulcrumPosAfter = mapTiltBearing.fromCoordinate(fulcrum) + compare(mapTiltBearing.bearing, bearing) + compare(fulcrumPos, fulcrumPosAfter) + + // resetting + mapTiltBearing.center = coordinate1 + mapTiltBearing.zoomLevel = 4 + mapTiltBearing.bearing = 45.0 + mapTiltBearing.tilt = 25.0 + } + + function test_map_align_coordinate_to_point() + { + var zeroCoord = QtPositioning.coordinate(0,0) + mapTiltBearing.bearing = 0.0 + mapTiltBearing.tilt = 0.0 + mapTiltBearing.zoomLevel = 3 + mapTiltBearing.center = zeroCoord + compare(mapTiltBearing.bearing, 0.0) + compare(mapTiltBearing.tilt, 0.0) + compare(mapTiltBearing.zoomLevel, 3) + compare(mapTiltBearing.center, zeroCoord) + + var coord = QtPositioning.coordinate(20,-20) + var point = Qt.point(400, 400) + mapTiltBearing.alignCoordinateToPoint(coord, point) + var coordAfter = mapTiltBearing.toCoordinate(point) + compare(coord.latitude, coordAfter.latitude) + compare(coord.longitude, coordAfter.longitude) + + // resetting + mapTiltBearing.center = coordinate1 + mapTiltBearing.zoomLevel = 4 + mapTiltBearing.bearing = 45.0 + mapTiltBearing.tilt = 25.0 + } + + function test_coordinate_conversion() + { + wait(1000) + mapCenterSpy.clear(); + compare(coordinateMap.center.latitude, 50) + compare(coordinateMap.center.longitude, 50) + // valid to screen position + var point = coordinateMap.fromCoordinate(coordinateMap.center) + verify (point.x > 495 && point.x < 505) + verify (point.y > 495 && point.y < 505) + // valid coordinate without altitude + point = coordinateMap.fromCoordinate(altitudelessCoordinate) + verify (point.x > 495 && point.x < 505) + verify (point.y > 495 && point.y < 505) + // out of map area in view + //var oldZoomLevel = coordinateMap.zoomLevel + //coordinateMap.zoomLevel = 8 + point = coordinateMap.fromCoordinate(coordinate4) + verify(isNaN(point.x)) + verify(isNaN(point.y)) + //coordinateMap.zoomLevel = oldZoomLevel + // invalid coordinates + point = coordinateMap.fromCoordinate(invalidCoordinate) + verify(isNaN(point.x)) + verify(isNaN(point.y)) + point = coordinateMap.fromCoordinate(null) + verify(isNaN(point.x)) + verify(isNaN(point.y)) + // valid point to coordinate + var coord = coordinateMap.toCoordinate(Qt.point(500,500)) + verify(coord.latitude > 49 && coord.latitude < 51) + verify(coord.longitude > 49 && coord.longitude < 51) + // beyond + coord = coordinateMap.toCoordinate(Qt.point(2000, 2000)) + verify(isNaN(coord.latitude)) + verify(isNaN(coord.longitde)) + // invalid + coord = coordinateMap.toCoordinate(Qt.point(-5, -6)) + verify(isNaN(coord.latitude)) + verify(isNaN(coord.longitde)) + + // test with tilting + coord = QtPositioning.coordinate(45.6, 17.67) + var pos = mapTestProjection.fromCoordinate(coord, false) + compare(Math.floor(pos.x), 3339) + compare(Math.floor(pos.y), 1727) + mapTestProjection.tilt = 6 + pos = mapTestProjection.fromCoordinate(coord, false) + compare(Math.floor(pos.x), 11066) + compare(Math.floor(pos.y), 5577) + mapTestProjection.tilt = 12 + pos = mapTestProjection.fromCoordinate(coord, false) + verify(isNaN(pos.latitude)) + verify(isNaN(pos.longitde)) + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_coordinateanimation.qml b/tests/auto/declarative_ui/tst_map_coordinateanimation.qml new file mode 100644 index 0000000..2047750 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_coordinateanimation.qml @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.6 +import QtPositioning 5.5 + +Item { + width:100 + height:100 + // General-purpose elements for the test: + Plugin { id: testPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true } + + + property var coordinateList: [] + property int coordinateCount: 0 + property int animationDuration: 100 + + Map {id: map + plugin: testPlugin + width: 100 + height: 100 + + Behavior on center { + id: centerBehavior + enabled: false + CoordinateAnimation { + id: coordinateAnimation + duration: animationDuration + } + } + + onCenterChanged: { + if (!coordinateList) { + coordinateList = [] + } + + coordinateList[coordinateCount] = {'latitude': center.latitude, 'longitude': center.longitude} + coordinateCount++ + } + } + + function toMercator(coord) { + var pi = Math.PI + var lon = coord.longitude / 360.0 + 0.5; + + var lat = coord.latitude; + lat = 0.5 - (Math.log(Math.tan((pi / 4.0) + (pi / 2.0) * lat / 180.0)) / pi) / 2.0; + lat = Math.max(0.0, lat); + lat = Math.min(1.0, lat); + + return {'latitude': lat, 'longitude': lon}; + } + + TestCase { + when: windowShown && map.mapReady + name: "CoordinateAnimation" + + function test_coordinate_animation() { + + coordinateList = [] + coordinateCount = 0 + + var from = {'latitude': 58.0, 'longitude': 12.0} + var to = {'latitude': 62.0, 'longitude': 24.0} + + + var fromMerc = toMercator(from) + var toMerc = toMercator(to) + + var delta = (toMerc.latitude - fromMerc.latitude) / (toMerc.longitude - fromMerc.longitude) + + // Set from coordinate with animation disabled. + map.center = QtPositioning.coordinate(from.latitude, from.longitude) + + // Expect only one update + compare(coordinateList.length, 1) + + // Set to coordinate with animation enabled + centerBehavior.enabled = true + map.center = QtPositioning.coordinate(to.latitude, to.longitude) + wait(animationDuration) + tryCompare(coordinateAnimation,"running",false) + + //check correct start position + compare(coordinateList[0].latitude, from.latitude) + compare(coordinateList[0].longitude, from.longitude) + + //check correct end position + compare(coordinateList[coordinateList.length - 1].latitude, to.latitude) + compare(coordinateList[coordinateList.length - 1].longitude, to.longitude) + + var i + var lastLatitude + for (i in coordinateList) { + var coordinate = coordinateList[i] + var mercCoordinate = toMercator(coordinate) + + //check that coordinates from the animation is along a straight line between from and to + var estimatedLatitude = fromMerc.latitude + (mercCoordinate.longitude - fromMerc.longitude) * delta + verify(mercCoordinate.latitude - estimatedLatitude < 0.00000000001); + + //check that each step has moved in the right direction + if (lastLatitude) { + verify(coordinate.latitude > lastLatitude) + } + lastLatitude = coordinate.latitude + } + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_error.qml b/tests/auto/declarative_ui/tst_map_error.qml new file mode 100644 index 0000000..35613f6 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_error.qml @@ -0,0 +1,215 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.6 +import QtPositioning 5.5 + +Item { + id: page + x: 0; y: 0; + width: 200 + height: 100 + property variant coordinate: QtPositioning.coordinate(20, 20) + + Plugin { + id: errorPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true + parameters: [ + PluginParameter { name: "error"; value: "1"}, + PluginParameter { name: "errorString"; value: "This error was expected. No worries !"} + ] + } + + Map { + id: map_error_plugin; + property alias mouseClickedSpy: mouseClickedSpy1 + x: 0; y: 0; width: 100; height: 100; plugin: errorPlugin; + + MouseArea { + id: mouseArea1 + objectName: "mouseArea" + x: 25; y: 25; width: 50; height: 50; + preventStealing: true + } + + SignalSpy {id: mouseClickedSpy1; target: mouseArea1; signalName: "clicked"} + } + + Map { + id: map_no_plugin; + property alias mouseClickedSpy: mouseClickedSpy2 + x: 100; y: 0; width: 100; height: 100; + + MouseArea { + id: mouseArea2 + objectName: "mouseArea" + x: 25; y: 25; width: 50; height: 50; + preventStealing: true + } + + SignalSpy {id: mouseClickedSpy2; target: mouseArea2; signalName: "clicked"} + } + + TestCase { + name: "MapErrorHandling" + when: windowShown + + function init() { + map_error_plugin.zoomLevel = 0 + map_no_plugin.zoomLevel = 0 + map_error_plugin.center = QtPositioning.coordinate(0, 0) + map_no_plugin.center = QtPositioning.coordinate(0, 0) + map_error_plugin.mouseClickedSpy.clear() + map_no_plugin.mouseClickedSpy.clear() + } + + function map_clicked(map) + { + mouseClick(map, 5, 5) + mouseClick(map, 50, 50) + mouseClick(map, 50, 50) + mouseClick(map, 50, 50) + tryCompare(map.mouseClickedSpy, "count", 3) + } + + function test_map_clicked_wiht_no_plugin() + { + map_clicked(map_no_plugin) + } + + function test_map_clicked_with_error_plugin() + { + map_clicked(map_error_plugin) + } + + function test_map_no_supportedMapTypes() + { + compare(map_no_plugin.supportedMapTypes.length , 0) + compare(map_error_plugin.supportedMapTypes.length , 0) + } + + function test_map_set_zoom_level() + { + map_no_plugin.zoomLevel = 9 + compare(map_no_plugin.zoomLevel,9) + map_error_plugin.zoomLevel = 9 + compare(map_error_plugin.zoomLevel,9) + } + + function test_map_set_center() + { + map_no_plugin.center = coordinate + verify(map_no_plugin.center === coordinate) + map_error_plugin.center = coordinate + verify(map_error_plugin.center === coordinate) + } + + function test_map_no_mapItems() + { + compare(map_no_plugin.mapItems.length , 0) + compare(map_error_plugin.mapItems.length , 0) + } + + function test_map_error() + { + compare(map_no_plugin.error , 0) + compare(map_no_plugin.errorString , "") + compare(map_error_plugin.error , 1) + compare(map_error_plugin.errorString ,"This error was expected. No worries !") + } + + function test_map_toCoordinate() + { + map_no_plugin.center = coordinate + compare(map_no_plugin.toCoordinate(50,50).isValid,false) + map_error_plugin.center = coordinate + compare(map_error_plugin.toCoordinate(50,50).isValid,false) + } + + function test_map_fromCoordinate() + { + verify(isNaN(map_error_plugin.fromCoordinate(coordinate).x)) + verify(isNaN(map_error_plugin.fromCoordinate(coordinate).y)) + verify(isNaN(map_no_plugin.fromCoordinate(coordinate).x)) + verify(isNaN(map_no_plugin.fromCoordinate(coordinate).y)) + } + + function test_map_gesture_enabled() + { + verify(map_error_plugin.gesture.enabled) + verify(map_no_plugin.gesture.enabled) + } + + function test_map_pan() + { + map_no_plugin.center = coordinate + map_no_plugin.pan(20,20) + verify(map_no_plugin.center === coordinate) + map_error_plugin.center = coordinate + map_error_plugin.pan(20,20) + verify(map_error_plugin.center === coordinate) + } + + function test_map_prefetchData() + { + map_error_plugin.prefetchData() + map_no_plugin.prefetchData() + } + + function test_map_fitViewportToMapItems() + { + map_error_plugin.fitViewportToMapItems() + map_no_plugin.fitViewportToMapItems() + } + + function test_map_setVisibleRegion() + { + map_no_plugin.visibleRegion = QtPositioning.circle(coordinate,1000) + verify(map_no_plugin.center != coordinate) + verify(map_no_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,0)) == true) + verify(map_no_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,90)) == true) + verify(map_no_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,180)) == true) + verify(map_no_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,270)) == true) + map_error_plugin.visibleRegion = QtPositioning.circle(coordinate,1000) + verify(map_error_plugin.center != coordinate) + verify(map_error_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,0)) == true) + verify(map_error_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,90)) == true) + verify(map_error_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,180)) == true) + verify(map_error_plugin.visibleRegion.contains(coordinate.atDistanceAndAzimuth(1000,270)) == true) + } + + function test_map_activeMapType() + { + compare(map_no_plugin.supportedMapTypes.length, 0) + compare(map_no_plugin.activeMapType.style, MapType.NoMap) + compare(map_error_plugin.supportedMapTypes.length, 0) + compare(map_error_plugin.activeMapType.style, MapType.NoMap) + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_flick.qml b/tests/auto/declarative_ui/tst_map_flick.qml new file mode 100644 index 0000000..16598d2 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_flick.qml @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtTest 1.0 +import QtLocation 5.6 +import QtPositioning 5.5 +import QtLocation.Test 5.6 + +Item { + // General-purpose elements for the test: + id: page + width: 120 + height: 120 + Plugin { id: testPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true } + + property variant coordinate: QtPositioning.coordinate(10, 11) + + MouseArea { + id: mouseAreaBottom + anchors.fill: parent + visible: false + } + + Map { + id: map + plugin: testPlugin + center: coordinate; + zoomLevel: 9; + anchors.fill: page + x:0; y:0 + + property real flickStartedLatitude + property real flickStartedLongitude + property bool disableOnPanStartedWithNoGesture: false + property bool disableOnFlickStartedWithNoGesture: false + property bool disableOnPanStartedWithDisabled: false + property bool disableOnFlickStartedWithDisabled: false + + gesture.onPanStarted: { + if (disableOnPanStartedWithNoGesture) + map.gesture.acceptedGestures = MapGestureArea.NoGesture + if (disableOnPanStartedWithDisabled) + map.gesture.enabled = false + } + gesture.onFlickStarted: { + flickStartedLatitude = map.center.latitude + flickStartedLatitude = map.center.longitude + if (disableOnFlickStartedWithNoGesture) + map.gesture.acceptedGestures = MapGestureArea.NoGesture + if (disableOnFlickStartedWithDisabled) + map.gesture.enabled = false + } + MouseArea { + id: mouseAreaTop + anchors.fill: parent + visible: false + } + } + + SignalSpy {id: centerSpy; target: map; signalName: 'centerChanged'} + SignalSpy {id: panStartedSpy; target: map.gesture; signalName: 'panStarted'} + SignalSpy {id: panFinishedSpy; target: map.gesture; signalName: 'panFinished'} + SignalSpy {id: gestureEnabledSpy; target: map.gesture; signalName: 'enabledChanged'} + SignalSpy {id: flickDecelerationSpy; target: map.gesture; signalName: 'flickDecelerationChanged'} + SignalSpy {id: flickStartedSpy; target: map.gesture; signalName: 'flickStarted'} + SignalSpy {id: flickFinishedSpy; target: map.gesture; signalName: 'flickFinished'} + SignalSpy {id: mouseAreaTopSpy; target: mouseAreaTop; signalName: 'onPressed'} + SignalSpy {id: mouseAreaBottomSpy; target: mouseAreaBottom; signalName: 'onPressed'} + + TestCase { + when: windowShown && map.mapReady + name: "MapFlick" + + function init() + { + if (Qt.platform.os === "windows" && (LocationTestHelper.x86Bits() === 32)) + skip("QTBUG-59503") + map.gesture.acceptedGestures = MapGestureArea.PanGesture | MapGestureArea.FlickGesture; + map.gesture.enabled = true + map.gesture.panEnabled = true + map.gesture.flickDeceleration = 500 + map.zoomLevel = 9 // or flicking diagonally won't work + map.disableOnPanStartedWithNoGesture = false + map.disableOnFlickStartedWithNoGesture = false + map.disableOnPanStartedWithDisabled = false + map.disableOnFlickStartedWithDisabled = false + centerSpy.clear() + gestureEnabledSpy.clear() + flickDecelerationSpy.clear() + panStartedSpy.clear() + panFinishedSpy.clear() + flickStartedSpy.clear() + flickFinishedSpy.clear() + mouseAreaTopSpy.clear() + mouseAreaBottomSpy.clear() + mouseAreaBottom.visible = false + mouseAreaTop.visible = false + compare(map.gesture.pinchActive, false) + compare(map.gesture.panActive, false) + } + + function initTestCase() + { + //check default values + compare(map.gesture.enabled, true) + map.gesture.enabled = false + compare(gestureEnabledSpy.count, 1) + compare(map.gesture.enabled, false) + map.gesture.enabled = false + compare(gestureEnabledSpy.count, 1) + compare(map.gesture.enabled, false) + map.gesture.enabled = true + compare(gestureEnabledSpy.count, 2) + compare(map.gesture.enabled, true) + compare(map.gesture.pinchActive, false) + compare(map.gesture.panActive, false) + verify(map.gesture.acceptedGestures & MapGestureArea.PinchGesture) + map.gesture.acceptedGestures = MapGestureArea.NoGesture + compare(map.gesture.acceptedGestures, MapGestureArea.NoGesture) + map.gesture.acceptedGestures = MapGestureArea.NoGesture + compare(map.gesture.acceptedGestures, MapGestureArea.NoGesture) + map.gesture.acceptedGestures = MapGestureArea.PinchGesture | MapGestureArea.PanGesture + compare(map.gesture.acceptedGestures, MapGestureArea.PinchGesture | MapGestureArea.PanGesture) + map.gesture.acceptedGestures = MapGestureArea.PanGesture + compare(map.gesture.acceptedGestures, MapGestureArea.PanGesture) + compare(map.gesture.flickDeceleration, 2500) + map.gesture.flickDeceleration = 2600 + compare(flickDecelerationSpy.count, 1) + compare(map.gesture.flickDeceleration, 2600) + map.gesture.flickDeceleration = 2600 + compare(flickDecelerationSpy.count, 1) + compare(map.gesture.flickDeceleration, 2600) + map.gesture.flickDeceleration = 400 // too small + compare(flickDecelerationSpy.count, 2) + compare(map.gesture.flickDeceleration, 500) // clipped to min + map.gesture.flickDeceleration = 11000 // too big + compare(flickDecelerationSpy.count, 3) + compare(map.gesture.flickDeceleration, 10000) // clipped to max + } + + function flick_down() + { + map.center.latitude = 10 + map.center.longitude = 11 + mousePress(page, 0, 50) + for (var i = 0; i < 50; i += 5) { + wait(25) + mouseMove(page, 0, (50 + i), 0, Qt.LeftButton); + } + mouseRelease(page, 0, 100) + + // order of signals is: flickStarted, either order: (flickEnded, movementEnded) + verify(map.center.latitude > 10) // latitude increases we are going 'up/north' (moving mouse down) + var moveLatitude = map.center.latitude // store lat and check that flick continues + + tryCompare(flickStartedSpy, "count", 1) + tryCompare(panFinishedSpy, "count", 1) + tryCompare(flickFinishedSpy, "count", 1) + + verify(map.center.latitude > moveLatitude) + compare(map.center.longitude, 11) // should remain the same + } + + function test_flick_down() + { + flick_down() + } + + function test_flick_down_with_filtering() + { + mouseAreaTop.visible = true + mouseAreaBottom.visible = true + flick_down() + tryCompare(mouseAreaTopSpy, "count", 1) + tryCompare(mouseAreaBottomSpy, "count",0) + } + + function flick_up() + { + map.center.latitude = 70 + map.center.longitude = 11 + mousePress(page, 10, 95) + for (var i = 45; i > 0; i -= 5) { + wait(25) + mouseMove(page, 10, (50 + i), 0, Qt.LeftButton); + } + mouseRelease(page, 10, 50) + verify(map.center.latitude < 70) + var moveLatitude = map.center.latitude // store lat and check that flick continues + tryCompare(flickStartedSpy, "count", 1) + tryCompare(panFinishedSpy, "count", 1) + tryCompare(flickFinishedSpy, "count", 1) + verify(map.center.latitude < moveLatitude) + compare(map.center.longitude, 11) // should remain the same + } + + function test_flick_up() + { + flick_up() + } + + function test_flick_up_with_filtering() + { + mouseAreaTop.visible = true + mouseAreaBottom.visible = true + flick_up() + tryCompare(mouseAreaTopSpy, "count", 1) + tryCompare(mouseAreaBottomSpy, "count",0) + } + + function test_flick_diagonal() + { + map.center.latitude = 50 + map.center.longitude = 50 + var pos = 5 + mousePress(page, pos, pos) + for (var i = pos; i < 50; i += 5) { + pos = i + wait(25) + mouseMove(page, pos, pos, 0, Qt.LeftButton); + } + mouseRelease(page, pos, pos) + verify(map.center.latitude > 50) + verify(map.center.longitude < 50) + var moveLatitude = map.center.latitude + var moveLongitude = map.center.longitude + tryCompare(flickStartedSpy, "count", 1) + tryCompare(panFinishedSpy, "count", 1) + tryCompare(flickFinishedSpy, "count", 1) + verify(map.center.latitude > moveLatitude) + verify(map.center.longitude < moveLongitude) + } + + function disabled_flicking() + { + map.center.latitude = 50 + map.center.longitude = 50 + mousePress(page, 0, 0) + for (var i = 0; i < 50; i += 5) { + wait(25) + mouseMove(page, i, i, 0, Qt.LeftButton); + } + mouseRelease(page, 50, 50) + compare(panStartedSpy.count, 0) + compare(panFinishedSpy.count, 0) + compare(flickStartedSpy.count, 0) + compare(flickFinishedSpy.count, 0) + } + + function test_disabled_flicking_with_nogesture() + { + map.gesture.acceptedGestures = MapGestureArea.NoGesture + } + + function test_disabled_flicking_with_disabled() + { + map.gesture.enabled = false + disabled_flicking() + } + + function disable_onFlickStarted() + { + map.center.latitude = 50 + map.center.longitude = 50 + mousePress(page, 0, 0) + for (var i = 0; i < 50; i += 5) { + wait(25) + mouseMove(page, i, i, 0, Qt.LeftButton); + } + mouseRelease(page, 50, 50) + var latitude = map.center.latitude; + var longitude = map.center.longitude + tryCompare(panStartedSpy, "count", 1) + tryCompare(flickStartedSpy, "count", 1) + verify(map.center.latitude > 50) + tryCompare(panFinishedSpy, "count", 1) + tryCompare(flickFinishedSpy, "count", 1) + // compare that flick was interrupted (less movement than without interrupting) + compare(latitude, map.center.latitude) + compare(longitude, map.center.longitude) + } + + function test_disable_onFlickStarted_with_disabled() + { + map.disableOnFlickStartedWithDisabled = true + disable_onFlickStarted() + } + + function test_disable_onFlickStarted_with_nogesture() + { + map.disableOnFlickStartedWithNoGesture = true + disable_onFlickStarted() + } + + function disable_onPanStarted() + { + map.center.latitude = 50 + map.center.longitude = 50 + mousePress(page, 0, 0) + for (var i = 0; i < 50; i += 5) { + wait(25) + mouseMove(page, i, i, 0, Qt.LeftButton); + } + mouseRelease(page, 50, 50) + compare(map.center.latitude,50) + compare(map.center.longitude,50) + tryCompare(panFinishedSpy, "count", 1) + // compare that flick was interrupted (less movement than without interrupting) + compare(map.center.latitude,50) + compare(map.center.longitude,50) + compare(map.gesture.panActive, false) + } + + function test_disable_onPanStarted_with_disabled() + { + map.disableOnPanStartedWithDisabled = true + disable_onPanStarted() + } + + function test_disable_onPanStarted_with_nogesture() + { + map.disableOnPanStartedWithNoGesture = true + disable_onPanStarted() + } + + /* + Regression test for QTBUG-67580 where touching the map + (as opposing to clicking it with a mouse) would cause a crash. + So this test is merely about surviving a tap. + */ + function test_touch() + { + touchEvent(map).press(0).commit(); + wait(25); + touchEvent(map).release(0).commit(); + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_item.qml b/tests/auto/declarative_ui/tst_map_item.qml new file mode 100644 index 0000000..1646fdb --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_item.qml @@ -0,0 +1,623 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.9 +import QtPositioning 5.5 +import QtLocation.Test 5.6 + + /* + + (0,0) ---------------------------------------------------- (240,0) + | no map | + | (20,20) | + (0,20) | ------------------------------------------ | (240,20) + | | | | + | | map | | + | | | | + | | | | + | | | | + | | (lat 20, lon 20) | | + | | x | | + | | | | + | | | | + | | | | + | | | | + | | | | + | ------------------------------------------ | + | | + (0,240) ---------------------------------------------------- (240,240) + + */ + +Item { + id: page + x: 0; y: 0; + width: 240 + height: 240 + Plugin { id: testPlugin; name : "qmlgeo.test.plugin"; allowExperimental: true } + + property variant someCoordinate1: QtPositioning.coordinate(15, 15) + property variant someCoordinate2: QtPositioning.coordinate(16, 16) + + Route { id: someRoute; + path: [ + { latitude: 22, longitude: 15 }, + { latitude: 21, longitude: 16 }, + { latitude: 23, longitude: 17 } + ] + } + Item { id: someItem } + + ItemGroup { + id: itemGroup1 + } + + MapCircle { + id: extMapCircle + center { + latitude: 35 + longitude: 15 + } + color: 'firebrick' + radius: 600000 + MouseArea { + anchors.fill: parent + SignalSpy { id: extMapCircleClicked; target: parent; signalName: "clicked" } + } + } + + MapQuickItem { + id: extMapQuickItem + MouseArea { + anchors.fill: parent + SignalSpy { id: extMapQuickItemClicked; target: parent; signalName: "clicked" } + } + coordinate { + latitude: 35 + longitude: 33 + } + sourceItem: Rectangle { + color: 'darkblue' + width: 40 + height: 20 + } + } + + Map { + id: map; + x: 20; y: 20; width: 200; height: 200 + zoomLevel: 9 + plugin: testPlugin; + + MapRectangle { + id: preMapRect + MouseArea { + id: preMapRectMa + anchors.fill: parent + drag.target: parent + preventStealing: true + SignalSpy { id: preMapRectClicked; target: parent; signalName: "clicked" } + SignalSpy { id: preMapRectActiveChanged; target: parent.drag; signalName: "activeChanged" } + } + SignalSpy {id: preMapRectTopLeftChanged; target: parent; signalName: "topLeftChanged" } + SignalSpy {id: preMapRectBottomRightChanged; target: parent; signalName: "bottomRightChanged" } + SignalSpy {id: preMapRectColorChanged; target: parent; signalName: "colorChanged"} + } + MapCircle { + id: preMapCircle + MouseArea { + id: preMapCircleMa + anchors.fill: parent + drag.target: parent + preventStealing: true + SignalSpy { id: preMapCircleClicked; target: parent; signalName: "clicked" } + SignalSpy { id: preMapCircleActiveChanged; target: parent.drag; signalName: "activeChanged" } + } + SignalSpy {id: preMapCircleCenterChanged; target: parent; signalName: "centerChanged"} + SignalSpy {id: preMapCircleColorChanged; target: parent; signalName: "colorChanged"} + SignalSpy {id: preMapCircleRadiusChanged; target: parent; signalName: "radiusChanged"} + SignalSpy {id: preMapCircleBorderColorChanged; target: parent.border; signalName: "colorChanged"} + SignalSpy {id: preMapCircleBorderWidthChanged; target: parent.border; signalName: "widthChanged"} + } + MapQuickItem { + id: preMapQuickItem + MouseArea { + id: preMapQuickItemMa + anchors.fill: parent + drag.target: parent + preventStealing: true + SignalSpy { id: preMapQuickItemClicked; target: parent; signalName: "clicked" } + SignalSpy { id: preMapQuickItemActiveChanged; target: parent.drag; signalName: "activeChanged" } + } + sourceItem: Rectangle { + id: preMapQuickItemSource + color: 'darkgreen' + width: 20 + height: 20 + } + SignalSpy { id: preMapQuickItemCoordinateChanged; target: parent; signalName: "coordinateChanged"} + SignalSpy { id: preMapQuickItemAnchorPointChanged; target: parent; signalName: "anchorPointChanged"} + SignalSpy { id: preMapQuickItemZoomLevelChanged; target: parent; signalName: "zoomLevelChanged"} + SignalSpy { id: preMapQuickItemSourceItemChanged; target: parent; signalName: "sourceItemChanged"} + } + MapPolygon { + id: preMapPolygon + color: 'darkgrey' + border.width: 0 + path: [ + { latitude: 25, longitude: 5 }, + { latitude: 20, longitude: 10 }, + { latitude: 15, longitude: 6 } + ] + MouseArea { + anchors.fill: parent + drag.target: parent + SignalSpy { id: preMapPolygonClicked; target: parent; signalName: "clicked" } + } + SignalSpy {id: preMapPolygonPathChanged; target: parent; signalName: "pathChanged"} + SignalSpy {id: preMapPolygonColorChanged; target: parent; signalName: "colorChanged"} + SignalSpy {id: preMapPolygonBorderWidthChanged; target: parent.border; signalName: "widthChanged"} + SignalSpy {id: preMapPolygonBorderColorChanged; target: parent.border; signalName: "colorChanged"} + } + MapPolyline { + id: preMapPolyline + line.color: 'darkred' + path: [ + { latitude: 25, longitude: 15 }, + { latitude: 20, longitude: 19 }, + { latitude: 15, longitude: 16 } + ] + SignalSpy {id: preMapPolylineColorChanged; target: parent.line; signalName: "colorChanged"} + SignalSpy {id: preMapPolylineWidthChanged; target: parent.line; signalName: "widthChanged"} + SignalSpy {id: preMapPolylinePathChanged; target: parent; signalName: "pathChanged"} + } + MapRoute { + id: preMapRoute + line.color: 'yellow' + // don't try this at home - route is not user instantiable + route: Route { + path: [ + { latitude: 25, longitude: 14 }, + { latitude: 20, longitude: 18 }, + { latitude: 15, longitude: 15 } + ] + } + SignalSpy {id: preMapRouteRouteChanged; target: parent; signalName: "routeChanged"} + SignalSpy {id: preMapRouteLineWidthChanged; target: parent.line; signalName: "widthChanged"} + SignalSpy {id: preMapRouteLineColorChanged; target: parent.line; signalName: "colorChanged"} + } + } + TestCase { + name: "MapItems" + when: windowShown && map.mapReady + + function initTestCase() + { + // sanity check that the coordinate conversion works, as + // rest of the case relies on it. for robustness cut + // a little slack with fuzzy compare + var mapcenter = map.fromCoordinate(map.center) + verify (fuzzy_compare(mapcenter.x, 100, 2)) + verify (fuzzy_compare(mapcenter.y, 100, 2)) + } + + function init() + { + map.center = QtPositioning.coordinate(20, 20) + preMapCircle.center = QtPositioning.coordinate(10,30) + preMapCircle.border.width = 0 + preMapCircle.color = 'red' + preMapCircle.radius = 10000 + preMapCircleClicked.clear() + preMapCircleCenterChanged.clear() + preMapCircleColorChanged.clear() + preMapCircleRadiusChanged.clear() + preMapCircleBorderColorChanged.clear() + preMapCircleBorderWidthChanged.clear() + + preMapRect.color = 'red' + preMapRect.border.width = 0 + preMapRect.topLeft = QtPositioning.coordinate(20, 20) + preMapRect.bottomRight = QtPositioning.coordinate(10, 30) + preMapRectTopLeftChanged.clear() + preMapRectBottomRightChanged.clear() + preMapRectColorChanged.clear() + preMapRectClicked.clear() + preMapRectActiveChanged.clear() + + preMapQuickItem.sourceItem = preMapQuickItemSource + preMapQuickItem.zoomLevel = 0 + preMapQuickItem.coordinate = QtPositioning.coordinate(35, 3) + preMapQuickItemClicked.clear() + preMapQuickItem.anchorPoint = Qt.point(0,0) + preMapQuickItemCoordinateChanged.clear() + preMapQuickItemAnchorPointChanged.clear() + preMapQuickItemZoomLevelChanged.clear() + preMapQuickItemSourceItemChanged.clear() + + preMapPolygonClicked.clear() + preMapPolylineColorChanged.clear() + preMapPolylineWidthChanged.clear() + preMapPolylinePathChanged.clear() + preMapPolygonPathChanged.clear() + preMapPolygonColorChanged.clear() + preMapPolygonBorderColorChanged.clear() + preMapPolygonBorderWidthChanged.clear() + preMapRouteRouteChanged.clear() + preMapRouteLineColorChanged.clear() + preMapRouteLineWidthChanged.clear() + verify(LocationTestHelper.waitForPolished(map)) + } + + function test_items_on_map() + { + // click rect + map.center = preMapRect.topLeft + verify(LocationTestHelper.waitForPolished(map)) + var point = map.fromCoordinate(preMapRect.topLeft) + mouseClick(map, point.x + 5, point.y + 5) + tryCompare(preMapRectClicked, "count", 1) + mouseClick(map, 1, 1) // no item hit + tryCompare(preMapRectClicked, "count", 1) + compare(preMapCircleClicked.count, 0) + + // click circle, overlaps and is above rect + map.center = preMapCircle.center + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapCircle.center) + mouseClick(map, point.x - 5, point.y - 5) + tryCompare(preMapCircleClicked, "count", 1) + compare(preMapRectClicked.count, 1) + + // click within circle bounding rect but not inside the circle geometry + map.center = preMapCircle.center.atDistanceAndAzimuth(preMapCircle.radius, -45) + mouseClick(map, preMapCircle.x + 4, preMapCircle.y + 4) + tryCompare(preMapRectClicked, "count", 2) + compare(preMapCircleClicked.count, 1) + + // click quick item + compare(preMapQuickItemClicked.count, 0) + map.center = preMapQuickItem.coordinate + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapQuickItem.coordinate) + mouseClick(map, point.x + 5, point.y + 5) + tryCompare(preMapQuickItemClicked, "count", 1) + + // click polygon + compare (preMapPolygonClicked.count, 0) + map.center = preMapPolygon.path[1] + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapPolygon.path[1]) + mouseClick(map, point.x - 5, point.y) + tryCompare(preMapPolygonClicked, "count", 1) + } + + function test_no_items_on_map() + { + // remove items and repeat clicks to verify they are gone + map.clearMapItems() + compare (map.mapItems.length, 0) + map.center = preMapRect.topLeft + var point = map.fromCoordinate(preMapRect.topLeft) + mouseClick(map, point.x + 5, point.y + 5) + compare(preMapRectClicked.count, 0) + verify(LocationTestHelper.waitForPolished(map)) + map.center = preMapCircle.center + point = map.fromCoordinate(preMapCircle.center) + mouseClick(map, point.x - 5, point.y - 5) + compare(preMapRectClicked.count, 0) + compare(preMapCircleClicked.count, 0) + map.center = preMapCircle.center.atDistanceAndAzimuth(preMapCircle.radius, -45) + mouseClick(map, preMapCircle.x + 4, preMapCircle.y + 4) + compare(preMapRectClicked.count, 0) + compare(preMapCircleClicked.count, 0) + compare(preMapQuickItemClicked.count, 0) + map.center = preMapQuickItem.coordinate + point = map.fromCoordinate(preMapQuickItem.coordinate) + mouseClick(map, point.x + 5, point.y + 5) + compare(preMapQuickItemClicked.count, 0) + map.center = preMapPolygon.path[1] + point = map.fromCoordinate(preMapPolygon.path[1]) + mouseClick(map, point.x - 5, point.y) + compare(preMapPolygonClicked.count, 0) + + // re-add items and verify they are back + // note: addition order is significant + map.addMapItem(preMapRect) + map.addMapItem(preMapCircle) + map.addMapItem(preMapQuickItem) + map.addMapItem(preMapPolygon) + map.addMapItem(preMapPolyline) + map.addMapItem(preMapRoute) + compare (map.mapItems.length, 6) + + map.center = preMapRect.topLeft + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapRect.topLeft) + mouseClick(map, point.x + 5, point.y + 5) + tryCompare(preMapRectClicked, "count", 1) + map.center = preMapCircle.center + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapCircle.center) + mouseClick(map, point.x - 5, point.y - 5) + tryCompare(preMapRectClicked, "count", 1) + compare(preMapCircleClicked.count, 1) + map.center = preMapCircle.center.atDistanceAndAzimuth(preMapCircle.radius, -45) + verify(LocationTestHelper.waitForPolished(map)) + mouseClick(map, preMapCircle.x + 4, preMapCircle.y + 4) + tryCompare(preMapRectClicked, "count", 2) + compare(preMapCircleClicked.count, 1) + compare(preMapQuickItemClicked.count, 0) + map.center = preMapQuickItem.coordinate + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapQuickItem.coordinate) + mouseClick(map, point.x + 5, point.y + 5) + tryCompare(preMapQuickItemClicked, "count", 1) + map.center = preMapPolygon.path[1] + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapPolygon.path[1]) + mouseClick(map, point.x - 5, point.y) + tryCompare(preMapPolygonClicked, "count", 1) + + + // item clips to map. not sure if this is sensible test + map.addMapItem(extMapCircle) + map.center = extMapCircle.center + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(extMapCircle.center) + mouseClick(map, point.x, point.y) + tryCompare(extMapCircleClicked, "count", 1) + mouseClick(map, point.x, -5) + tryCompare(extMapCircleClicked, "count", 1) + map.removeMapItem(extMapCircle) + + map.addMapItem(extMapQuickItem) + map.center = extMapQuickItem.coordinate + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(extMapQuickItem.coordinate) + mouseClick(map, point.x + 5, point.y + 5) + tryCompare(extMapQuickItemClicked, "count", 1) + mouseClick(map, map.width + 5, point.y + 5) + tryCompare(extMapQuickItemClicked, "count", 1) + map.removeMapItem(extMapQuickItem) + + var numItemsOnMap = map.mapItems.length + map.addMapItemGroup( itemGroup1 ) + compare(map.mapItems.length, numItemsOnMap + 3) + } + + function test_drag() + { + // basic drags, drag rectangle + compare (preMapRectActiveChanged.count, 0) + map.center = preMapRect.topLeft + verify(LocationTestHelper.waitForPolished(map)) + var i + var point = map.fromCoordinate(preMapRect.topLeft) + var targetCoordinate = map.toCoordinate(51, 51) + mousePress(map, point.x + 5, point.y + 5) + for (i = 0; i < 50; i += 1) { + wait(1) + mouseMove(map, point.x + 5 - i, point.y + 5 - i) + } + mouseRelease(map, point.x + 5 - i, point.y + 5 - i) + compare (preMapRectActiveChanged.count, 2) + verify(preMapRectTopLeftChanged.count > 1) + verify(preMapRectBottomRightChanged.count === preMapRectTopLeftChanged.count) + verify(fuzzy_compare(preMapRect.topLeft.latitude, targetCoordinate.latitude, 0.2)) + verify(fuzzy_compare(preMapRect.topLeft.longitude, targetCoordinate.longitude, 0.2)) + var latH = preMapRect.bottomRight.latitude - preMapRect.topLeft.latitude + var lonW = preMapRect.bottomRight.longitude - preMapRect.topLeft.longitude + verify(fuzzy_compare(preMapRect.bottomRight.latitude, preMapRect.topLeft.latitude + latH, 0.1)) + verify(fuzzy_compare(preMapRect.bottomRight.longitude, preMapRect.topLeft.longitude + lonW, 0.1)) + + // drag circle + compare (preMapCircleActiveChanged.count, 0) + map.center = preMapCircle.center + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapCircle.center) + targetCoordinate = map.toCoordinate(51, 51) + mousePress(map, point.x, point.y) + for (i = 0; i < 50; i += 1) { + wait(1) + mouseMove(map, point.x - i, point.y - i) + } + mouseRelease(map, point.x - i, point.y - i) + verify(LocationTestHelper.waitForPolished(map)) + compare(preMapRectActiveChanged.count, 2) + compare(preMapCircleActiveChanged.count, 2) + verify(preMapCircleCenterChanged.count > 1) + verify(fuzzy_compare(preMapCircle.center.latitude, targetCoordinate.latitude, 0.2)) + verify(fuzzy_compare(preMapCircle.center.longitude, targetCoordinate.longitude, 0.2)) + + // drag quick item + compare (preMapQuickItemActiveChanged.count, 0) + map.center = preMapQuickItem.coordinate + verify(LocationTestHelper.waitForPolished(map)) + point = map.fromCoordinate(preMapQuickItem.coordinate) + targetCoordinate = map.toCoordinate(51, 51) + mousePress(map, point.x + 5, point.y + 5) + for (i = 0; i < 50; i += 1) { + wait(1) + mouseMove(map, point.x - i, point.y - i) + } + mouseRelease(map, point.x - i, point.y - i) + verify(LocationTestHelper.waitForPolished(map)) + compare(preMapQuickItemActiveChanged.count, 2) + verify(preMapQuickItemCoordinateChanged.count > 1) + verify(fuzzy_compare(preMapQuickItem.coordinate.latitude, targetCoordinate.latitude, 0.2)) + verify(fuzzy_compare(preMapQuickItem.coordinate.longitude, targetCoordinate.longitude, 0.2)) + } + + function test_basic_items_properties() + { + // circle + preMapCircle.center = someCoordinate1 + compare (preMapCircleCenterChanged.count, 1) + preMapCircle.center = someCoordinate1 + compare (preMapCircleCenterChanged.count, 1) + preMapCircle.color = 'blue' + compare (preMapCircleColorChanged.count, 1) + preMapCircle.color = 'blue' + compare (preMapCircleColorChanged.count, 1) + preMapCircle.radius = 50 + compare (preMapCircleRadiusChanged.count, 1) + preMapCircle.radius = 50 + compare (preMapCircleRadiusChanged.count, 1) + preMapCircle.border.color = 'blue' + compare(preMapCircleBorderColorChanged.count, 1) + preMapCircle.border.color = 'blue' + compare(preMapCircleBorderColorChanged.count, 1) + preMapCircle.border.width = 5 + compare(preMapCircleBorderWidthChanged.count, 1) + preMapCircle.border.width = 5 + compare(preMapCircleBorderWidthChanged.count, 1) + + // rectangle + preMapRect.topLeft = someCoordinate1 + compare (preMapRectTopLeftChanged.count, 1) + compare (preMapRectBottomRightChanged.count, 0) + preMapRect.bottomRight = someCoordinate2 + compare (preMapRectTopLeftChanged.count, 1) + compare (preMapRectBottomRightChanged.count, 1) + preMapRect.bottomRight = someCoordinate2 + preMapRect.topLeft = someCoordinate1 + compare (preMapRectTopLeftChanged.count, 1) + compare (preMapRectBottomRightChanged.count, 1) + preMapRect.color = 'blue' + compare (preMapRectColorChanged.count, 1) + preMapRect.color = 'blue' + compare (preMapRectColorChanged.count, 1) + + // polyline + preMapPolyline.line.width = 5 + compare (preMapPolylineWidthChanged.count, 1) + preMapPolyline.line.width = 5 + compare (preMapPolylineWidthChanged.count, 1) + preMapPolyline.line.color = 'blue' + compare(preMapPolylineColorChanged.count, 1) + preMapPolyline.line.color = 'blue' + compare(preMapPolylineColorChanged.count, 1) + preMapPolyline.addCoordinate(someCoordinate1) + compare (preMapPolylinePathChanged.count, 1) + preMapPolyline.addCoordinate(someCoordinate1) + compare (preMapPolylinePathChanged.count, 2) + preMapPolyline.removeCoordinate(someCoordinate1) + compare (preMapPolylinePathChanged.count, 3) + preMapPolyline.removeCoordinate(someCoordinate1) + compare (preMapPolylinePathChanged.count, 4) + preMapPolyline.removeCoordinate(someCoordinate1) + compare (preMapPolylinePathChanged.count, 4) + + // polygon + preMapPolygon.border.width = 5 + compare (preMapPolylineWidthChanged.count, 1) + preMapPolygon.border.width = 5 + compare (preMapPolylineWidthChanged.count, 1) + preMapPolygon.border.color = 'blue' + compare(preMapPolylineColorChanged.count, 1) + preMapPolygon.border.color = 'blue' + preMapPolygon.color = 'blue' + compare (preMapPolygonColorChanged.count, 1) + preMapPolygon.color = 'blue' + compare (preMapPolygonColorChanged.count, 1) + preMapPolygon.addCoordinate(someCoordinate1) + compare (preMapPolygonPathChanged.count, 1) + preMapPolygon.addCoordinate(someCoordinate1) + compare (preMapPolygonPathChanged.count, 2) + preMapPolygon.removeCoordinate(someCoordinate1) + compare (preMapPolygonPathChanged.count, 3) + preMapPolygon.removeCoordinate(someCoordinate1) + compare (preMapPolygonPathChanged.count, 4) + preMapPolygon.removeCoordinate(someCoordinate1) + compare (preMapPolygonPathChanged.count, 4) + + // route + preMapRoute.line.width = 5 + compare (preMapRouteLineWidthChanged.count, 1) + preMapRoute.line.width = 5 + compare (preMapRouteLineWidthChanged.count, 1) + preMapRoute.line.color = 'blue' + compare (preMapRouteLineColorChanged.count, 1) + preMapRoute.line.color = 'blue' + compare (preMapRouteLineColorChanged.count, 1) + preMapRoute.route = someRoute + compare (preMapRouteRouteChanged.count, 1) + preMapRoute.route = someRoute + compare (preMapRouteRouteChanged.count, 1) + + // quick + compare (preMapQuickItemCoordinateChanged.count, 0) + preMapQuickItem.coordinate = someCoordinate1 + compare (preMapQuickItemCoordinateChanged.count, 1) + preMapQuickItem.coordinate = someCoordinate1 + compare (preMapQuickItemCoordinateChanged.count, 1) + preMapQuickItem.anchorPoint = Qt.point(39, 3) + compare (preMapQuickItemAnchorPointChanged.count, 1) + preMapQuickItem.anchorPoint = Qt.point(39, 3) + compare (preMapQuickItemAnchorPointChanged.count, 1) + preMapQuickItem.zoomLevel = 6 + compare (preMapQuickItemZoomLevelChanged.count, 1) + preMapQuickItem.zoomLevel = 6 + compare (preMapQuickItemZoomLevelChanged.count, 1) + preMapQuickItem.sourceItem = someItem + compare (preMapQuickItemSourceItemChanged.count, 1) + preMapQuickItem.sourceItem = someItem + compare (preMapQuickItemSourceItemChanged.count, 1) + } + + function fuzzy_compare(val, ref, tol) { + var tolerance = 2 + if (tol !== undefined) + tolerance = tol + if ((val >= ref - tolerance) && (val <= ref + tolerance)) + return true; + console.log('map fuzzy cmp returns false for value, ref, tolerance: ' + val + ', ' + ref + ', ' + tolerance) + return false; + } + + // these 'real_' prefixed functions do sequences as + // it would occur on real app (e.g. doubleclick is in fact + // a sequence of press, release, doubleclick, release). + // (they were recorded as seen on test app). mouseClick() works ok + // because testlib internally converts it to mousePress + mouseRelease events + function real_double_click (target, x, y) { + mousePress(target, x,y) + mouseRelease(target, x, y) + mouseDoubleClick(target, x, y) + mouseRelease(target, x, y) + } + function real_press_and_hold(target, x,y) { + mousePress(target,x,y) + wait(850) // threshold is 800 ms + mouseRelease(target,x, y) + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_item_details.qml b/tests/auto/declarative_ui/tst_map_item_details.qml new file mode 100644 index 0000000..e769528 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_item_details.qml @@ -0,0 +1,651 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtPositioning 5.5 +import QtLocation 5.6 +import QtLocation.Test 5.6 + +Item { + id: page + x: 0; y: 0; + width: 240 + height: 240 + Plugin { id: testPlugin + name : "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ PluginParameter { name: "finishRequestImmediately"; value: true}] + } + + property variant mapDefaultCenter: QtPositioning.coordinate(20, 20) + + property variant datelineCoordinate: QtPositioning.coordinate(20, 180) + property variant datelineCoordinateLeft: QtPositioning.coordinate(20, 170) + property variant datelineCoordinateRight: QtPositioning.coordinate(20, -170) + + MapPolygon { + id: extMapPolygon + color: 'darkgrey' + path: [ + { latitude: 25, longitude: 5 }, + { latitude: 20, longitude: 10 } + ] + MouseArea { + anchors.fill: parent + drag.target: parent + SignalSpy { id: extMapPolygonClicked; target: parent; signalName: "clicked" } + } + SignalSpy {id: extMapPolygonPathChanged; target: parent; signalName: "pathChanged"} + SignalSpy {id: extMapPolygonColorChanged; target: parent; signalName: "colorChanged"} + SignalSpy {id: extMapPolygonBorderWidthChanged; target: parent.border; signalName: "widthChanged"} + SignalSpy {id: extMapPolygonBorderColorChanged; target: parent.border; signalName: "colorChanged"} + } + + property variant polyCoordinate: QtPositioning.coordinate(15, 6) + + MapPolygon { + id: extMapPolygon0 + color: 'darkgrey' + } + + MapPolyline { + id: extMapPolyline0 + } + + MapPolyline { + id: extMapPolyline + path: [ + { latitude: 25, longitude: 5 }, + { latitude: 20, longitude: 10 } + ] + SignalSpy {id: extMapPolylineColorChanged; target: parent.line; signalName: "colorChanged"} + SignalSpy {id: extMapPolylineWidthChanged; target: parent.line; signalName: "widthChanged"} + SignalSpy {id: extMapPolylinePathChanged; target: parent; signalName: "pathChanged"} + } + + MapRectangle { + id: extMapRectDateline + color: 'darkcyan' + topLeft { + latitude: 20 + longitude: 175 + } + bottomRight { + latitude: 10 + longitude: -175 + } + MouseArea { + anchors.fill: parent + drag.target: parent + preventStealing: true + } + } + + MapCircle { + id: extMapCircleDateline + color: 'darkmagenta' + center { + latitude: 20 + longitude: 180 + } + radius: 400000 + MouseArea { + anchors.fill: parent + drag.target: parent + preventStealing: true + } + } + + MapQuickItem { + id: extMapQuickItemDateline + MouseArea { + anchors.fill: parent + drag.target: parent + preventStealing: true + } + coordinate { + latitude: 20 + longitude: 175 + } + sourceItem: Rectangle { + color: 'darkgreen' + width: 20 + height: 20 + } + } + + MapPolygon { + id: extMapPolygonDateline + color: 'darkmagenta' + path: [ + { latitude: 20, longitude: 175 }, + { latitude: 20, longitude: -175 }, + { latitude: 10, longitude: -175 }, + { latitude: 10, longitude: 175 } + ] + MouseArea { + anchors.fill: parent + drag.target: parent + preventStealing: true + } + } + + MapPolyline { + id: polylineForSetpath + line.width: 3 + path: [ + { latitude: 20, longitude: 175 }, + { latitude: 20, longitude: -175 }, + { latitude: 10, longitude: -175 }, + { latitude: 10, longitude: 175 } + ] + } + + MapPolyline { + id: extMapPolylineDateline + line.width : 3 + path: [ + { latitude: 20, longitude: 175 }, + { latitude: 25, longitude: -175 } + ] + MouseArea { + anchors.fill: parent + drag.target: parent + } + } + + MapRoute { + id: extMapRouteDateline + line.color: 'yellow' + route: Route { + path: [ + { latitude: 25, longitude: 175 }, + { latitude: 20, longitude: -175 } + ] + } + } + + MapRectangle { + id: extMapRectEdge + color: 'darkcyan' + topLeft { + latitude: 20 + longitude: -15 + } + bottomRight { + latitude: 10 + longitude: -5 + } + MouseArea { + anchors.fill: parent + drag.target: parent + } + } + + MapCircle { + id: extMapCircleEdge + color: 'darkmagenta' + center { + latitude: 20 + longitude: -15 + } + radius: 400000 + MouseArea { + anchors.fill: parent + drag.target: parent + } + } + + MapQuickItem { + id: extMapQuickItemEdge + MouseArea { + anchors.fill: parent + drag.target: parent + } + coordinate { + latitude: 20 + longitude: -15 + } + sourceItem: Rectangle { + color: 'darkgreen' + width: 20 + height: 20 + } + } + + MapPolygon { + id: extMapPolygonEdge + color: 'darkmagenta' + path: [ + { latitude: 20, longitude: -15 }, + { latitude: 20, longitude: -5 }, + { latitude: 10, longitude: -5 }, + { latitude: 10, longitude: -15 } + ] + MouseArea { + anchors.fill: parent + drag.target: parent + } + } + + MapPolyline { + id: extMapPolylineEdge + line.width : 3 + path: [ + { latitude: 20, longitude: -15 }, + { latitude: 25, longitude: -5 } + ] + MouseArea { + anchors.fill: parent + drag.target: parent + } + } + + MapRoute { + id: extMapRouteEdge + line.color: 'yellow' + route: Route { + path: [ + { latitude: 25, longitude: -15 }, + { latitude: 20, longitude: -5 } + ] + } + } + + Map { + id: map; + x: 20; y: 20; width: 200; height: 200 + center: mapDefaultCenter + plugin: testPlugin; + } + + Text {id: progressText} + + TestCase { + name: "MapItemDetails" + when: windowShown && map.mapReady + + /* + + (0,0) ---------------------------------------------------- (240,0) + | no map | + | (20,20) | + (0,20) | ------------------------------------------ | (240,20) + | | | | + | | map | | + | | | | + | | | | + | | | | + | | (lat 20, lon 20) | | + | | x | | + | | | | + | | | | + | | | | + | | | | + | | | | + | ------------------------------------------ | + | | + (0,240) ---------------------------------------------------- (240,240) + + */ + function initTestCase() + { + // sanity check that the coordinate conversion works + var mapcenter = map.fromCoordinate(map.center) + verify (fuzzy_compare(mapcenter.x, 100, 2)) + verify (fuzzy_compare(mapcenter.y, 100, 2)) + } + + function init() + { + map.clearMapItems() + map.zoomLevel = 3 + extMapPolygon.border.width = 1.0 + extMapPolygonClicked.clear() + extMapPolylineColorChanged.clear() + extMapPolylineWidthChanged.clear() + extMapPolylinePathChanged.clear() + extMapPolygonPathChanged.clear() + extMapPolygonColorChanged.clear() + extMapPolygonBorderColorChanged.clear() + extMapPolygonBorderWidthChanged.clear() + } + + function test_polygon() + { + map.center = extMapPolygon.path[1] + var point = map.fromCoordinate(extMapPolygon.path[1]) + map.addMapItem(extMapPolygon) + verify(LocationTestHelper.waitForPolished(map)) + verify(extMapPolygon.path.length == 2) + mouseClick(map, point.x - 5, point.y) + compare(extMapPolygonClicked.count, 0) + map.addMapItem(extMapPolygon0) // mustn't crash or ill-behave + verify(extMapPolygon0.path.length == 0) + extMapPolygon.addCoordinate(polyCoordinate) + verify(extMapPolygon.path.length == 3) + verify(LocationTestHelper.waitForPolished(map)) + mouseClick(map, point.x - 5, point.y) + tryCompare(extMapPolygonClicked, "count", 1) + + extMapPolygon.path[0].latitude = 10 + verify(extMapPolygon.path[0].latitude, 10) + extMapPolygon.path[0].latitude = polyCoordinate.latitude + verify(extMapPolygon.path[0].latitude, 15) + extMapPolygon.path[0].longitude = 2 + verify(extMapPolygon.path[0].longitude, 2) + extMapPolygon.path[0].longitude = polyCoordinate.longitude + verify(extMapPolygon.path[0].longitude, 6) + + extMapPolygon.removeCoordinate(polyCoordinate) + verify(extMapPolygon.path.length == 2) + extMapPolygon.removeCoordinate(extMapPolygon.path[1]) + verify(extMapPolygon.path.length == 1) + extMapPolygon.removeCoordinate(extMapPolygon.path[0]) + verify(extMapPolygon.path.length == 0) + } + + function test_polyline() + { + compare (extMapPolyline.line.width, 1.0) + var point = map.fromCoordinate(extMapPolyline.path[1]) + map.addMapItem(extMapPolyline0) // mustn't crash or ill-behave + verify(extMapPolyline0.path.length == 0) + map.addMapItem(extMapPolyline) + verify(extMapPolyline.path.length == 2) + extMapPolyline.addCoordinate(polyCoordinate) + verify(extMapPolyline.path.length == 3) + extMapPolyline.addCoordinate(extMapPolyline.path[0]) + verify(extMapPolyline.path.length == 4) + + extMapPolyline.path[0].latitude = 10 + verify(extMapPolyline.path[0].latitude, 10) + extMapPolyline.path[0].latitude = polyCoordinate.latitude + verify(extMapPolyline.path[0].latitude, 15) + extMapPolyline.path[0].longitude = 2 + verify(extMapPolyline.path[0].longitude, 2) + extMapPolyline.path[0].longitude = polyCoordinate.longitude + verify(extMapPolyline.path[0].longitude, 6) + + // TODO when line rendering is ready + //mouseClick(map, point.x - 5, point.y) + //compare(extMapPolylineClicked.count, 1) + extMapPolyline.removeCoordinate(extMapPolyline.path[0]) + verify(extMapPolyline.path.length == 3) + extMapPolyline.removeCoordinate(polyCoordinate) + verify(extMapPolyline.path.length == 2) + extMapPolyline.removeCoordinate(extMapPolyline.path[1]) + verify(extMapPolyline.path.length == 1) + extMapPolyline.removeCoordinate(extMapPolyline.path[0]) + verify(extMapPolyline.path.length == 0) + } + + function test_polyline_setpath() + { + compare (polylineForSetpath.line.width, 3.0) + verify(polylineForSetpath.path.length == 4) + compare(polylineForSetpath.path[0], QtPositioning.coordinate(20, 175)) + compare(polylineForSetpath.path[3], QtPositioning.coordinate(10, 175)) + + + var originalPath = QtPositioning.shapeToPath(polylineForSetpath.geoShape) + + var geoPath = QtPositioning.path([ { latitude: 20, longitude: -15 }, + { latitude: 20, longitude: -5 }, + { latitude: 10, longitude: -5 }, + { latitude: 10, longitude: -15 }, + { latitude: 10, longitude: -105 } ], 50) + + polylineForSetpath.setPath(geoPath) + verify(polylineForSetpath.path.length == 5) + compare(polylineForSetpath.path[0], QtPositioning.coordinate(20, -15)) + compare(polylineForSetpath.path[3], QtPositioning.coordinate(10, -15)) + + polylineForSetpath.setPath(originalPath) + verify(polylineForSetpath.path.length == 4) + compare(polylineForSetpath.path[0], QtPositioning.coordinate(20, 175)) + compare(polylineForSetpath.path[3], QtPositioning.coordinate(10, 175)) + } + + /* + + (0,0) ---------------------------------------------------- (600,0) + | no map | + | (20,20) | + (0,20) | ------------------------------------------ | (600,20) + | | | | + | | map | | + | | | | + | | | | + | | | | + | | (lat 20, lon 180) | | + | | x | | + | | | | + | | | | + | | | | + | | | | + | | | | + | ------------------------------------------ | + | | + (0,240) ---------------------------------------------------- (600,240) + + */ + function test_dateline() { + map.center = datelineCoordinate + map.zoomLevel = 2.2 + var inspectionTime = 0 // change this to inspect the behavior. + + // rectangle + // item spanning across dateline + map.addMapItem(extMapRectDateline) + verify(extMapRectDateline.topLeft.longitude == 175) + verify(extMapRectDateline.bottomRight.longitude == -175) + var point = map.fromCoordinate(extMapRectDateline.topLeft) + verify(point.x < map.width / 2.0) + point = map.fromCoordinate(extMapRectDateline.bottomRight) + verify(point.x > map.width / 2.0) + // move item away from dataline by directly setting its coords + extMapRectDateline.bottomRight.longitude = datelineCoordinateRight.longitude + point = map.fromCoordinate(extMapRectDateline.bottomRight) + verify(point.x > map.width / 2.0) + // move item edge onto dateline + extMapRectDateline.topLeft.longitude = datelineCoordinate.longitude + point = map.fromCoordinate(extMapRectDateline.topLeft) + compare(point.x, map.width / 2.0) + // drag item back onto dateline + verify(LocationTestHelper.waitForPolished(map)) + visualInspectionPoint(inspectionTime) + mousePress(map, point.x + 5, point.y + 5) + var i + for (i=0; i < 20; i += 2) { + wait(1) + mouseMove(map, point.x + 5 - i, point.y + 5 ) + } + mouseRelease(map, point.x + 5 - i, point.y + 5) + verify(LocationTestHelper.waitForPolished(map)) + visualInspectionPoint(inspectionTime) + point = map.fromCoordinate(extMapRectDateline.topLeft) + verify(point.x < map.width / 2.0) + point = map.fromCoordinate(extMapRectDateline.bottomRight) + verify(point.x > map.width / 2.0) + map.removeMapItem(extMapRectDateline) + + // circle + map.addMapItem(extMapCircleDateline) + verify(extMapCircleDateline.center.longitude === 180) + map.center = datelineCoordinate + point = map.fromCoordinate(extMapCircleDateline.center) + compare(point.x, map.width / 2.0) // center of the screen + visualInspectionPoint() + extMapCircleDateline.center.longitude = datelineCoordinateRight.longitude // -170, moving the circle to the right + point = map.fromCoordinate(extMapCircleDateline.center) + verify(LocationTestHelper.waitForPolished(map)) + verify(point.x > map.width / 2.0) + visualInspectionPoint(inspectionTime) + mousePress(map, point.x, point.y) + for (i=0; i < 50; i += 4) { + wait(1) + mouseMove(map, point.x - i, point.y) + } + mouseRelease(map, point.x - i, point.y) + verify(LocationTestHelper.waitForPolished(map)) + visualInspectionPoint(inspectionTime) + point = map.fromCoordinate(extMapCircleDateline.center) + visualInspectionPoint() + verify(point.x < map.width / 2.0) + map.removeMapItem(extMapCircleDateline) + + // quickitem + map.addMapItem(extMapQuickItemDateline) + map.center = datelineCoordinate + verify(extMapQuickItemDateline.coordinate.longitude === 175) + point = map.fromCoordinate(extMapQuickItemDateline.coordinate) + verify(point.x < map.width / 2.0) + visualInspectionPoint() + extMapQuickItemDateline.coordinate.longitude = datelineCoordinateRight.longitude + point = map.fromCoordinate(extMapQuickItemDateline.coordinate) + verify(point.x > map.width / 2.0) + verify(LocationTestHelper.waitForPolished(map)) + visualInspectionPoint(inspectionTime) + mousePress(map, point.x + 5, point.y + 5) + for (i=0; i < 64; i += 5) { + wait(1) + mouseMove(map, point.x + 5 - i, point.y + 5 ) + } + mouseRelease(map, point.x + 5 - i, point.y + 5) + verify(LocationTestHelper.waitForPolished(map)) + visualInspectionPoint(inspectionTime) + point = map.fromCoordinate(extMapQuickItemDateline.coordinate) + visualInspectionPoint() + verify(point.x < map.width / 2.0) + map.removeMapItem(extMapQuickItemDateline) + + // polygon + map.addMapItem(extMapPolygonDateline) + map.center = datelineCoordinate + verify(extMapPolygonDateline.path[0].longitude == 175) + verify(extMapPolygonDateline.path[1].longitude == -175) + verify(extMapPolygonDateline.path[2].longitude == -175) + verify(extMapPolygonDateline.path[3].longitude == 175) + point = map.fromCoordinate(extMapPolygonDateline.path[0]) + verify(point.x < map.width / 2.0) + point = map.fromCoordinate(extMapPolygonDateline.path[1]) + verify(point.x > map.width / 2.0) + point = map.fromCoordinate(extMapPolygonDateline.path[2]) + verify(point.x > map.width / 2.0) + point = map.fromCoordinate(extMapPolygonDateline.path[3]) + verify(point.x < map.width / 2.0) + extMapPolygonDateline.path[1].longitude = datelineCoordinateRight.longitude + point = map.fromCoordinate(extMapPolygonDateline.path[1]) + verify(point.x > map.width / 2.0) + extMapPolygonDateline.path[2].longitude = datelineCoordinateRight.longitude + point = map.fromCoordinate(extMapPolygonDateline.path[2]) + verify(point.x > map.width / 2.0) + var path = extMapPolygonDateline.path; + path[0].longitude = datelineCoordinate.longitude; + extMapPolygonDateline.path = path; + point = map.fromCoordinate(extMapPolygonDateline.path[0]) + compare(point.x, map.width / 2.0) + path = extMapPolygonDateline.path; + path[3].longitude = datelineCoordinate.longitude; + extMapPolygonDateline.path = path; + point = map.fromCoordinate(extMapPolygonDateline.path[3]) + compare(point.x, map.width / 2.0) + verify(LocationTestHelper.waitForPolished(map)) + visualInspectionPoint(inspectionTime) + mousePress(map, point.x + 5, point.y - 5) + for (i=0; i < 16; i += 2) { + wait(1) + mouseMove(map, point.x + 5 - i, point.y - 5 ) + } + mouseRelease(map, point.x + 5 - i, point.y - 5) + verify(LocationTestHelper.waitForPolished(map)) + visualInspectionPoint(inspectionTime) + point = map.fromCoordinate(extMapPolygonDateline.path[0]) + verify(point.x < map.width / 2.0) + point = map.fromCoordinate(extMapPolygonDateline.path[1]) + verify(point.x > map.width / 2.0) + point = map.fromCoordinate(extMapPolygonDateline.path[2]) + verify(point.x > map.width / 2.0) + point = map.fromCoordinate(extMapPolygonDateline.path[3]) + verify(point.x < map.width / 2.0) + map.removeMapItem(extMapPolygonDateline) + + // polyline + map.addMapItem(extMapPolylineDateline) + map.center = datelineCoordinate + verify(extMapPolylineDateline.path[0].longitude == 175) + verify(extMapPolylineDateline.path[1].longitude == -175) + point = map.fromCoordinate(extMapPolylineDateline.path[0]) + verify(point.x < map.width / 2.0) + point = map.fromCoordinate(extMapPolylineDateline.path[1]) + verify(point.x > map.width / 2.0) + extMapPolylineDateline.path[1].longitude = datelineCoordinateRight.longitude + point = map.fromCoordinate(extMapPolylineDateline.path[1]) + verify(point.x > map.width / 2.0) + var path = extMapPolygonDateline.path; + path[0].longitude = datelineCoordinate.longitude; + extMapPolylineDateline.path = path; + point = map.fromCoordinate(extMapPolylineDateline.path[0]) + compare(point.x, map.width / 2.0) + map.removeMapItem(extMapPolylineDateline) + + // map route + // (does not support setting of path coords) + map.addMapItem(extMapRouteDateline) + verify(extMapRouteDateline.route.path[0].longitude == 175) + verify(extMapRouteDateline.route.path[1].longitude == -175) + point = map.fromCoordinate(extMapRouteDateline.route.path[0]) + verify(point.x < map.width / 2.0) + point = map.fromCoordinate(extMapRouteDateline.route.path[1]) + verify(point.x > map.width / 2.0) + map.removeMapItem(extMapRouteDateline) + } + + function fuzzy_compare(val, ref, tol) { + var tolerance = 2 + if (tol !== undefined) + tolerance = tol + if ((val >= ref - tolerance) && (val <= ref + tolerance)) + return true; + console.log('map fuzzy cmp returns false for value, ref, tolerance: ' + val + ', ' + ref + ', ' + tolerance) + return false; + } + // call to visualInspectionPoint testcase (for dev time visual inspection) + function visualInspectionPoint(time) { + var waitTime = 0 // 300 + if (time !== undefined) + waitTime = time + if (waitTime > 0) { + console.log('halting for ' + waitTime + ' milliseconds') + wait (waitTime) + } + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_item_fit_viewport.qml b/tests/auto/declarative_ui/tst_map_item_fit_viewport.qml new file mode 100644 index 0000000..8d1ee42 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_item_fit_viewport.qml @@ -0,0 +1,690 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.6 +import QtPositioning 5.5 +import QtLocation.Test 5.5 + + /* + + (0,0) ---------------------------------------------------- (296,0) + | no map | + | (20,20) | + (0,20) | ------------------------------------------ | (296,20) + | | | | + | | map | | + | | | | + | | | | + | | | | + | | (lat 20, lon 20) | | + | | x | | + | | | | + | | | | + | | | | + | | | | + | | | | + | ------------------------------------------ | + | | + (0,296) ---------------------------------------------------- (296,296) + + */ + +Item { + id: page + x: 0; y: 0; + width: 296 + height: 296 + Plugin { id: testPlugin; name : "qmlgeo.test.plugin"; allowExperimental: true } + + property variant mapDefaultCenter: QtPositioning.coordinate(20, 20) + property variant preMapRectangleDefaultTopLeft: QtPositioning.coordinate(20, 20) + property variant preMapRectangleDefaultBottomRight: QtPositioning.coordinate(10, 30) + property variant preMapCircleDefaultCenter: QtPositioning.coordinate(10, 30) + property variant preMapQuickItemDefaultCoordinate: QtPositioning.coordinate(35, 3) + + property variant preMapPolygonDefaultPath: [ + { latitude: 25, longitude: 5 }, + { latitude: 20, longitude: 10 }, + { latitude: 15, longitude: 6 } + ] + + property variant preMapPolylineDefaultPath: [ + { latitude: 25, longitude: 15 }, + { latitude: 20, longitude: 19 }, + { latitude: 15, longitude: 16 } + ] + + property variant preMapRouteDefaultPath: [ + { latitude: 25, longitude: 14 }, + { latitude: 20, longitude: 18 }, + { latitude: 15, longitude: 15 } + ] + + property variant mapCircleTopLeft: QtPositioning.coordinate(0, 0) + property variant mapCircleBottomRight: QtPositioning.coordinate(0, 0) + property variant mapQuickItemTopLeft: QtPositioning.coordinate(0, 0) + property variant mapQuickItemBottomRight: QtPositioning.coordinate(0, 0) + property variant mapPolygonTopLeft: QtPositioning.coordinate(0, 0) + property variant mapPolygonBottomRight: QtPositioning.coordinate(0, 0) + property variant mapPolylineTopLeft: QtPositioning.coordinate(0, 0) + property variant mapPolylineBottomRight: QtPositioning.coordinate(0, 0) + property variant mapRouteTopLeft: QtPositioning.coordinate(0, 0) + property variant mapRouteBottomRight: QtPositioning.coordinate(0, 0) + property variant fitRect: QtPositioning.rectangle(QtPositioning.coordinate(80, 80), + QtPositioning.coordinate(78, 82)) + property variant fitEmptyRect: QtPositioning.rectangle(QtPositioning.coordinate(79, 79),-1, -1) + property variant fitCircle: QtPositioning.circle(QtPositioning.coordinate(-50, -100), 1500) + property variant fitInvalidShape: QtPositioning.shape() + + property variant fitCircleTopLeft: QtPositioning.coordinate(0, 0) + property variant fitCircleBottomRight: QtPositioning.coordinate(0, 0) + + Map { + id: map; + x: 20; y: 20; width: 256; height: 256 + zoomLevel: 3 + center: mapDefaultCenter + plugin: testPlugin; + + MapRectangle { + id: preMapRect + color: 'darkcyan' + border.width: 0 + topLeft: preMapRectangleDefaultTopLeft + bottomRight: preMapRectangleDefaultBottomRight + MouseArea { + id: preMapRectMa + anchors.fill: parent + drag.target: parent + SignalSpy { id: preMapRectClicked; target: parent; signalName: "clicked" } + SignalSpy { id: preMapRectActiveChanged; target: parent.drag; signalName: "activeChanged" } + } + SignalSpy {id: preMapRectTopLeftChanged; target: parent; signalName: "topLeftChanged" } + SignalSpy {id: preMapRectBottomRightChanged; target: parent; signalName: "bottomRightChanged" } + SignalSpy {id: preMapRectColorChanged; target: parent; signalName: "colorChanged"} + } + MapCircle { + id: preMapCircle + color: 'darkmagenta' + border.width: 0 + center: preMapCircleDefaultCenter + radius: 400000 + MouseArea { + anchors.fill: parent + drag.target: parent + SignalSpy { id: preMapCircleClicked; target: parent; signalName: "clicked" } + SignalSpy { id: preMapCircleActiveChanged; target: parent.drag; signalName: "activeChanged" } + } + SignalSpy {id: preMapCircleCenterChanged; target: parent; signalName: "centerChanged"} + SignalSpy {id: preMapCircleColorChanged; target: parent; signalName: "colorChanged"} + SignalSpy {id: preMapCircleRadiusChanged; target: parent; signalName: "radiusChanged"} + SignalSpy {id: preMapCircleBorderColorChanged; target: parent.border; signalName: "colorChanged"} + SignalSpy {id: preMapCircleBorderWidthChanged; target: parent.border; signalName: "widthChanged"} + } + MapQuickItem { + id: preMapQuickItem + MouseArea { + anchors.fill: parent + drag.target: parent + SignalSpy { id: preMapQuickItemClicked; target: parent; signalName: "clicked" } + SignalSpy { id: preMapQuickItemActiveChanged; target: parent.drag; signalName: "activeChanged" } + } + coordinate: preMapQuickItemDefaultCoordinate + sourceItem: Rectangle { + color: 'darkgreen' + width: 20 + height: 20 + } + SignalSpy { id: preMapQuickItemCoordinateChanged; target: parent; signalName: "coordinateChanged"} + SignalSpy { id: preMapQuickItemAnchorPointChanged; target: parent; signalName: "anchorPointChanged"} + SignalSpy { id: preMapQuickItemZoomLevelChanged; target: parent; signalName: "zoomLevelChanged"} + SignalSpy { id: preMapQuickItemSourceItemChanged; target: parent; signalName: "sourceItemChanged"} + } + MapPolygon { + id: preMapPolygon + color: 'darkgrey' + border.width: 0 + path: [ + { latitude: 25, longitude: 5 }, + { latitude: 20, longitude: 10 }, + { latitude: 15, longitude: 6 } + ] + MouseArea { + anchors.fill: parent + drag.target: parent + SignalSpy { id: preMapPolygonClicked; target: parent; signalName: "clicked" } + } + SignalSpy {id: preMapPolygonPathChanged; target: parent; signalName: "pathChanged"} + SignalSpy {id: preMapPolygonColorChanged; target: parent; signalName: "colorChanged"} + SignalSpy {id: preMapPolygonBorderWidthChanged; target: parent.border; signalName: "widthChanged"} + SignalSpy {id: preMapPolygonBorderColorChanged; target: parent.border; signalName: "colorChanged"} + } + MapPolyline { + id: preMapPolyline + line.color: 'darkred' + path: [ + { latitude: 25, longitude: 15 }, + { latitude: 20, longitude: 19 }, + { latitude: 15, longitude: 16 } + ] + SignalSpy {id: preMapPolylineColorChanged; target: parent.line; signalName: "colorChanged"} + SignalSpy {id: preMapPolylineWidthChanged; target: parent.line; signalName: "widthChanged"} + SignalSpy {id: preMapPolylinePathChanged; target: parent; signalName: "pathChanged"} + } + MapRoute { + id: preMapRoute + line.color: 'yellow' + // don't try this at home - route is not user instantiable + route: Route { + path: [ + { latitude: 25, longitude: 14 }, + { latitude: 20, longitude: 18 }, + { latitude: 15, longitude: 15 } + ] + } + SignalSpy {id: preMapRouteRouteChanged; target: parent; signalName: "routeChanged"} + SignalSpy {id: preMapRouteLineWidthChanged; target: parent.line; signalName: "widthChanged"} + SignalSpy {id: preMapRouteLineColorChanged; target: parent.line; signalName: "colorChanged"} + } + } + + TestCase { + name: "MapItemsFitViewport" + when: windowShown && map.mapReady + + function initTestCase() + { + // sanity check that the coordinate conversion works + var mapcenter = map.fromCoordinate(map.center) + verify (fuzzy_compare(mapcenter.x, 128, 2)) + verify (fuzzy_compare(mapcenter.y, 128, 2)) + } + + function init() + { + preMapRect.topLeft.latitude = 20 + preMapRect.topLeft.longitude = 20 + preMapRect.bottomRight.latitude = 10 + preMapRect.bottomRight.longitude = 30 + preMapCircle.center.latitude = 10 + preMapCircle.center.longitude = 30 + preMapQuickItem.coordinate.latitude = 35 + preMapQuickItem.coordinate.longitude = 3 + var i + for (i = 0; i < preMapPolygon.path.length; ++i) { + preMapPolygon.path[i].latitude = preMapPolygonDefaultPath[i].latitude + preMapPolygon.path[i].longitude = preMapPolygonDefaultPath[i].longitude + } + for (i = 0; i < preMapPolyline.path.length; ++i) { + preMapPolyline.path[i].latitude = preMapPolylineDefaultPath[i].latitude + preMapPolyline.path[i].longitude = preMapPolylineDefaultPath[i].longitude + } + for (i = 0; i < preMapRoute.route.path.length; ++i) { + preMapRoute.route.path[i].latitude = preMapRouteDefaultPath[i].latitude + preMapRoute.route.path[i].longitude = preMapRouteDefaultPath[i].longitude + } + // remove items + map.clearMapItems() + //clear_data() + compare (map.mapItems.length, 0) + // reset map + map.center.latitude = 20 + map.center.longitude = 20 + map.zoomLevel = 3 + // re-add items and verify they are back (without needing to pan map etc.) + map.addMapItem(preMapRect) + map.addMapItem(preMapCircle) + map.addMapItem(preMapQuickItem) + map.addMapItem(preMapPolygon) + map.addMapItem(preMapPolyline) + map.addMapItem(preMapRoute) + compare (map.mapItems.length, 6) + calculate_bounds() + } + + function test_visible_itmes() + { + // normal case - fit viewport to items which are all already visible + verify_visibility_all_items() + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + } + + function test_visible_zoom_in() + { + // zoom in (clipping also occurs) + var z = map.zoomLevel + for (var i = (z + 1); i < map.maximumZoomLevel; ++i ) { + map.zoomLevel = i + visualInspectionPoint() + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + } + } + + function test_visible_zoom_out() + { + // zoom out + for (var i = (z - 1); i >= 0; --i ) { + map.zoomLevel = i + visualInspectionPoint() + verify_visibility_all_items() + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + } + } + + function test_visible_map_move() { + // move map so all items are out of screen + // then fit viewport + var xDir = 1 + var yDir = 0 + var xDirChange = -1 + var yDirChange = 1 + var dir = 0 + for (dir = 0; dir < 4; dir++) { + verify_visibility_all_items() + var i = 0 + var panX = map.width * xDir * 0.5 + var panY = map.height * yDir * 0.5 + map.pan(panX, panY) + map.pan(panX, panY) + visualInspectionPoint() + // check all items are indeed not within screen bounds + calculate_bounds() + verify(!is_coord_on_screen(preMapRect.topLeft)) + verify(!is_coord_on_screen(preMapRect.bottomRight)) + verify(!is_coord_on_screen(mapCircleTopLeft)) + verify(!is_coord_on_screen(mapCircleBottomRight)) + verify(!is_coord_on_screen(mapPolygonTopLeft)) + verify(!is_coord_on_screen(mapPolygonBottomRight)) + verify(!is_coord_on_screen(mapQuickItemTopLeft)) + verify(!is_coord_on_screen(mapQuickItemBottomRight)) + verify(!is_coord_on_screen(mapPolylineTopLeft)) + verify(!is_coord_on_screen(mapPolylineBottomRight)) + verify(!is_coord_on_screen(mapRouteTopLeft)) + verify(!is_coord_on_screen(mapRouteBottomRight)) + // fit viewport and verify that all items are visible again + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + if (dir == 2) + xDirChange *= -1 + if (dir == 1) + yDirChange *= -1 + xDir += xDirChange + yDir += yDirChange + } + } + + function test_fit_to_geoshape() { + visualInspectionPoint() + calculate_fit_circle_bounds() + //None should be visible + verify(!is_coord_on_screen(fitCircleTopLeft)) + verify(!is_coord_on_screen(fitCircleBottomRight)) + verify(!is_coord_on_screen(fitRect.topLeft)) + verify(!is_coord_on_screen(fitRect.bottomRight)) + + map.visibleRegion = fitRect + visualInspectionPoint() + calculate_fit_circle_bounds() + //Rectangle should be visible, not circle + verify(!is_coord_on_screen(fitCircleTopLeft)) + verify(!is_coord_on_screen(fitCircleBottomRight)) + verify(is_coord_on_screen(fitRect.topLeft)) + verify(is_coord_on_screen(fitRect.bottomRight)) + + map.visibleRegion = fitCircle + visualInspectionPoint() + calculate_fit_circle_bounds() + //Circle should be visible, not rectangle + verify(is_coord_on_screen(fitCircleTopLeft)) + verify(is_coord_on_screen(fitCircleBottomRight)) + verify(!is_coord_on_screen(fitRect.topLeft)) + verify(!is_coord_on_screen(fitRect.bottomRight)) + + map.visibleRegion = fitInvalidShape + visualInspectionPoint() + calculate_fit_circle_bounds() + //Invalid shape, map should be in the same position as before + verify(is_coord_on_screen(fitCircleTopLeft)) + verify(is_coord_on_screen(fitCircleBottomRight)) + verify(!is_coord_on_screen(fitRect.topLeft)) + verify(!is_coord_on_screen(fitRect.bottomRight)) + + map.visibleRegion = fitEmptyRect + visualInspectionPoint() + calculate_fit_circle_bounds() + //Empty shape, map should change centerlocation, empty rect visible + verify(!is_coord_on_screen(fitCircleTopLeft)) + verify(!is_coord_on_screen(fitCircleBottomRight)) + verify(is_coord_on_screen(fitEmptyRect.topLeft)) + verify(is_coord_on_screen(fitEmptyRect.bottomRight)) + + // Test if this can be reset + map.visibleRegion = fitRect + verify(is_coord_on_screen(fitRect.topLeft)) + verify(is_coord_on_screen(fitRect.bottomRight)) + // move map + map.center = QtPositioning.coordinate(0,0) + verify(!is_coord_on_screen(fitRect.topLeft)) + verify(!is_coord_on_screen(fitRect.bottomRight)) + // recheck + map.visibleRegion = fitRect + verify(is_coord_on_screen(fitRect.topLeft)) + verify(is_coord_on_screen(fitRect.bottomRight)) + //zoom map + map.zoomLevel++ + verify(!is_coord_on_screen(fitRect.topLeft)) + verify(!is_coord_on_screen(fitRect.bottomRight)) + // recheck + map.visibleRegion = fitRect + verify(is_coord_on_screen(fitRect.topLeft)) + verify(is_coord_on_screen(fitRect.bottomRight)) + } + + // checks that circles belongs to the view port + function circle_in_viewport(center, radius, visible) + { + for (var i = 0; i < 128; ++i) { + var azimuth = 360.0 * i / 128.0; + var coord = center.atDistanceAndAzimuth(radius,azimuth) + if (coord.isValid) + verify(is_coord_on_screen(coord) === visible, visible ? + "circle not visible" : "circle visible") + } + } + + function test_fit_circle_to_viewport(data) + { + verify(!is_coord_on_screen(data.center)) + circle_in_viewport(data.center, data.radius, false) + map.visibleRegion = QtPositioning.circle(data.center, data.radius) + circle_in_viewport(data.center, data.radius, true) + } + + function test_fit_circle_to_viewport_data() + { + return [ + { tag: "circle 1", center: + QtPositioning.coordinate(70,70), radius: 10 }, + { tag: "circle 2", center: + QtPositioning.coordinate(80,30), radius: 2000000 }, + { tag: "circle 3", center: + QtPositioning.coordinate(-82,30), radius: 2000000 }, + { tag: "circle 4", center: + QtPositioning.coordinate(60,179), radius: 20000 }, + { tag: "circle 5", center: + QtPositioning.coordinate(60,-179), radius: 20000 }, + ] + } + + function test_fit_rectangle_to_viewport(data) + { + verify(!is_coord_on_screen(data.topLeft),"rectangle visible") + verify(!is_coord_on_screen(data.bottomRight),"rectangle visible") + map.visibleRegion = QtPositioning.rectangle(data.topLeft,data.bottomRight) + verify(is_coord_on_screen(data.topLeft),"rectangle not visible") + verify(is_coord_on_screen(data.bottomRight),"rectangle not visible") + } + + function test_fit_rectangle_to_viewport_data() + { + return [ + { tag: "rectangle 1", + topLeft: QtPositioning.coordinate(80, 80), + bottomRight: QtPositioning.coordinate(78, 82) }, + { tag: "rectangle 2", + topLeft: QtPositioning.coordinate(30,-130), + bottomRight: QtPositioning.coordinate(0,-100)} + ] + } + + /*function test_ad_visible_items_move() { + // move different individual items out of screen + // then fit viewport + var xDir = 1 + var yDir = 0 + var xDirChange = -1 + var yDirChange = 1 + var dir = 0 + var move = 50 + for (dir = 0; dir < 4; dir++) { + // move rect out of screen + reset() + verify_visibility_all_items() + preMapRect.topLeft.longitude += move * xDir + preMapRect.topLeft.latitude += move * yDir + preMapRect.bottomRight.longitude += move * xDir + preMapRect.bottomRight.latitude += move * yDir + calculate_bounds() + verify(!is_coord_on_screen(preMapRect.topLeft)) + verify(!is_coord_on_screen(preMapRect.bottomRight)) + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + + // move circle out of screen + reset() + verify_visibility_all_items() + preMapCircle.center.longitude += move * xDir + preMapCircle.center.latitude += move * yDir + calculate_bounds() + verify(!is_coord_on_screen(mapCircleTopLeft)) + verify(!is_coord_on_screen(mapCircleBottomRight)) + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + + // move quick item out of screen + reset() + verify_visibility_all_items() + preMapQuickItem.coordinate.longitude += move * xDir + preMapQuickItem.coordinate.latitude += move * yDir + calculate_bounds() + verify(!is_coord_on_screen(mapQuickItemTopLeft)) + verify(!is_coord_on_screen(mapQuickItemBottomRight)) + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + + // move map polygon out of screen + reset() + verify_visibility_all_items() + var i + for (i = 0; i < preMapPolygonDefaultPath.length; ++i) { + preMapPolygon.path[i].longitude += move * xDir + preMapPolygon.path[i].latitude += move * yDir + } + calculate_bounds() + verify(!is_coord_on_screen(mapPolygonTopLeft)) + verify(!is_coord_on_screen(mapPolygonBottomRight)) + map.fitViewportToMapItems() + visualInspectionPoint() + verify_visibility_all_items() + if (dir == 2) + xDirChange *= -1 + if (dir == 1) + yDirChange *= -1 + xDir += xDirChange + yDir += yDirChange + } + }*/ + + function clear_data() { + preMapRectClicked.clear() + preMapCircleClicked.clear() + preMapQuickItemClicked.clear() + preMapPolygonClicked.clear() + preMapCircleCenterChanged.clear() + preMapCircleColorChanged.clear() + preMapCircleRadiusChanged.clear() + preMapCircleBorderColorChanged.clear() + preMapCircleBorderWidthChanged.clear() + preMapRectTopLeftChanged.clear() + preMapRectBottomRightChanged.clear() + preMapRectColorChanged.clear() + preMapPolylineColorChanged.clear() + preMapPolylineWidthChanged.clear() + preMapPolylinePathChanged.clear() + preMapPolygonPathChanged.clear() + preMapPolygonColorChanged.clear() + preMapPolygonBorderColorChanged.clear() + preMapPolygonBorderWidthChanged.clear() + preMapRouteRouteChanged.clear() + preMapRouteLineColorChanged.clear() + preMapRouteLineWidthChanged.clear() + preMapQuickItemCoordinateChanged.clear() + preMapQuickItemAnchorPointChanged.clear() + preMapQuickItemZoomLevelChanged.clear() + preMapQuickItemSourceItemChanged.clear() + } + + function calculate_fit_circle_bounds() { + var circleDiagonal = Math.sqrt(2) * fitCircle.radius + fitCircleTopLeft = fitCircle.center.atDistanceAndAzimuth(circleDiagonal,-45) + fitCircleBottomRight = fitCircle.center.atDistanceAndAzimuth(circleDiagonal,135) + } + + function calculate_bounds(){ + var circleDiagonal = Math.sqrt(2) * preMapCircle.radius + var itemTopLeft = preMapCircle.center.atDistanceAndAzimuth(circleDiagonal,-45) + var itemBottomRight = preMapCircle.center.atDistanceAndAzimuth(circleDiagonal,135) + + mapCircleTopLeft = itemTopLeft; + mapCircleBottomRight = itemBottomRight; + + itemTopLeft = preMapQuickItem.coordinate + var preMapQuickItemScreenPosition = map.fromCoordinate(preMapQuickItem.coordinate) + preMapQuickItemScreenPosition.x += preMapQuickItem.sourceItem.width + preMapQuickItemScreenPosition.y += preMapQuickItem.sourceItem.height + itemBottomRight = map.toCoordinate(preMapQuickItemScreenPosition) + + mapQuickItemTopLeft = itemTopLeft; + mapQuickItemBottomRight = itemBottomRight; + + var bounds = min_max_bounds_from_list(preMapPolygon.path) + mapPolygonTopLeft = bounds.topLeft; + mapPolygonBottomRight = bounds.bottomRight; + + bounds = min_max_bounds_from_list(preMapPolyline.path) + mapPolylineTopLeft = bounds.topLeft; + mapPolylineBottomRight = bounds.bottomRight; + + bounds = min_max_bounds_from_list(preMapRoute.route.path) + mapRouteTopLeft = bounds.topLeft; + mapRouteBottomRight = bounds.bottomRight; + } + + function min_max_bounds_from_list(coorindates){ + var i = 0 + var point = map.fromCoordinate(coorindates[0]) + var minX = point.x + var minY = point.y + var maxX = point.x + var maxY = point.y + + for (i=1; i < coorindates.length; ++i) { + point = map.fromCoordinate(coorindates[i]) + if (point.x < minX) + minX = point.x + if (point.x > maxX) + maxX = point.x + if (point.y < minY) + minY = point.y + if (point.y > maxY) + maxY = point.y + } + point.x = minX + point.y = minY + var itemTopLeft = map.toCoordinate(point) + point.x = maxX + point.y = maxY + var itemBottomRight = map.toCoordinate(point) + + return QtPositioning.rectangle(itemTopLeft, itemBottomRight); + } + + function verify_visibility_all_items(){ + calculate_bounds() + verify(is_coord_on_screen(preMapRect.topLeft)) + verify(is_coord_on_screen(preMapRect.bottomRight)) + verify(is_coord_on_screen(mapCircleTopLeft)) + verify(is_coord_on_screen(mapCircleBottomRight)) + verify(is_coord_on_screen(mapPolygonTopLeft)) + verify(is_coord_on_screen(mapPolygonBottomRight)) + verify(is_coord_on_screen(mapQuickItemTopLeft)) + verify(is_coord_on_screen(mapQuickItemBottomRight)) + verify(is_coord_on_screen(mapPolylineTopLeft)) + verify(is_coord_on_screen(mapPolylineBottomRight)) + verify(is_coord_on_screen(mapRouteTopLeft)) + verify(is_coord_on_screen(mapRouteBottomRight)) + } + + + function is_coord_on_screen(coord) { + return is_point_on_screen(map.fromCoordinate(coord)) + } + + function is_point_on_screen(point) { + if (point.x >= 0 && point.x <= (map.x + map.width) + && point.y >=0 && point.y <= (map.y + map.height) ) + return true; + else + return false; + } + + function fuzzy_compare(val, ref, tol) { + var tolerance = 2 + if (tol !== undefined) + tolerance = tol + if ((val >= ref - tolerance) && (val <= ref + tolerance)) + return true; + console.log('map fuzzy cmp returns false for value, ref, tolerance: ' + val + ', ' + ref + ', ' + tolerance) + return false; + } + + // call to visualInspectionPoint testcase (for dev time visual inspection) + function visualInspectionPoint(time) { + var waitTime = 0 // 300 + if (time !== undefined) + waitTime = time + if (waitTime > 0) { + console.log('halting for ' + waitTime + ' milliseconds') + wait (waitTime) + } + } + } +} + diff --git a/tests/auto/declarative_ui/tst_map_itemview.qml b/tests/auto/declarative_ui/tst_map_itemview.qml new file mode 100644 index 0000000..097212c --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_itemview.qml @@ -0,0 +1,576 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.6 +import QtPositioning 5.5 +import QtLocation.Test 5.5 + +Item { + id: masterItem + width: 200 + height: 350 + // General-purpose elements for the test: + Plugin { id: testPlugin; name : "qmlgeo.test.plugin"; allowExperimental: true } + + property variant mapDefaultCenter: QtPositioning.coordinate(10, 30) + property bool allMapsReady: map.mapReady + && map3.mapReady + && mapForView.mapReady + && mapForTestingListModel.mapReady + && mapForTestingRouteModel.mapReady + + MapItemView { + id: routeItemViewExtra + model: routeModel + delegate: Component { + MapRoute { + route: routeData + } + } + } + + MapItemView { + id: listModelItemViewExtra + model: ListModel { + id: testingListModelExtra + ListElement { lat: 11; lon: 31 } + ListElement { lat: 12; lon: 32 } + ListElement { lat: 13; lon: 33 } + } + delegate: Component { + MapCircle { + radius: 1500000 + center { + latitude: lat + longitude: lon + } + } + } + } + + Map { + id: map + objectName: 'staticallyDeclaredMap' + center: mapDefaultCenter; + plugin: testPlugin; + width: 100 + height: 100 + zoomLevel: 2 + MapCircle { + id: prepopulatedCircle + objectName: 'prepopulatedCircle' + center: mapDefaultCenter; + radius: 100 + } + } + + Map { + id: map3 + objectName: 'staticallyDeclaredMapWithView' + center: mapDefaultCenter; + plugin: testPlugin; + width: 100 + height: 100 + zoomLevel: 2 + MapItemView { + id: theItemView3 + model: testModel3 + delegate: Component { + MapCircle { + radius: 1500000 + center { + latitude: modeldata.coordinate.latitude + longitude: modeldata.coordinate.longitude + } + } + } + } + } + + MapCircle { + id: externalCircle + objectName: 'externalCircle' + radius: 200 + center: mapDefaultCenter + } + + SignalSpy {id: mapItemSpy; target: map; signalName: 'mapItemsChanged'} + + + MapCircle { + objectName: "externalCircle2" + id: externalCircle2 + radius: 2000000 + center: mapDefaultCenter + } + + MapCircle { + objectName: "externalCircle3" + id: externalCircle3 + radius: 2000000 + center: mapDefaultCenter + } + + MapRectangle { + objectName: "externalRectangle" + id: externalRectangle + } + + MapPolygon { + objectName: "externalPolygon" + id: externalPolygon + } + + MapPolyline { + objectName: 'externalPolyline' + id: externalPolyline + } + + MapQuickItem { + objectName: 'externalQuickItem' + id: externalQuickItem + sourceItem: Rectangle {} + } + + TestModel { + id: testModel + datatype: 'coordinate' + datacount: 7 + delay: 0 + } + + TestModel { + id: testModel2 + datatype: 'coordinate' + datacount: 3 + delay: 0 + } + + TestModel { + id: testModel3 + datatype: 'coordinate' + datacount: 0 + delay: 0 + } + + Plugin { + id: testPlugin_immediate; + name: "qmlgeo.test.plugin" + allowExperimental: true + parameters: [ + // Parms to guide the test plugin + PluginParameter { name: "gc_supported"; value: true}, + PluginParameter { name: "gc_finishRequestImmediately"; value: true}, + PluginParameter { name: "gc_validateWellKnownValues"; value: true} + ] + } + RouteQuery {id: routeQuery; + waypoints: [ + { latitude: 60, longitude: 60 }, + { latitude: 61, longitude: 62 }, + { latitude: 63, longitude: 64 }, + { latitude: 65, longitude: 66 }, + { latitude: 67, longitude: 68 } + ] + } + + RouteModel {id: routeModel; plugin: testPlugin_immediate; query: routeQuery } + SignalSpy {id: mapItemsChangedSpy; target: mapForTestingRouteModel; signalName: "mapItemsChanged"} + + Map { + id: mapForView + + property int mapItemsLength: mapItems.length + + center: mapDefaultCenter + plugin: testPlugin + anchors.fill: parent + zoomLevel: 2 + + MapCircle { + id: internalCircle + radius: 2000000 + center: mapDefaultCenter + } + MapItemView { + id: theItemView + model: testModel + delegate: Component { + id: theItemViewsComponent + MapCircle { + radius: 1500000 + center { + latitude: modeldata.coordinate.latitude + longitude: modeldata.coordinate.longitude + } + } + } + } + } + + Map { + id: mapForTestingListModel + + center: mapDefaultCenter + plugin: testPlugin + anchors.fill: parent + zoomLevel: 2 + + property int mapItemsLength: mapItems.length + property variant itemCoordinates: [ + QtPositioning.coordinate(11, 31), + QtPositioning.coordinate(12, 32), + QtPositioning.coordinate(13, 33) + ] + + MapItemView { + id: listModelItemView + model: ListModel { + id: testingListModel + ListElement { lat: 11; lon: 31 } + ListElement { lat: 12; lon: 32 } + ListElement { lat: 13; lon: 33 } + } + delegate: Component { + MapCircle { + radius: 1500000 + center { + latitude: lat + longitude: lon + } + } + } + } + } + + Map { + id: mapForTestingRouteModel + + property int mapItemsLength: mapItems.length + + plugin: testPlugin + center: mapDefaultCenter + anchors.fill: parent + zoomLevel: 2 + + MapItemView { + id: routeItemView + model: routeModel + delegate: Component { + MapRoute { + route: routeData + } + } + } + } + + TestCase { + name: "MapItem" + when: windowShown && allMapsReady + function clear_data() { + mapItemSpy.clear() + } + + function test_basics() { + compare(theItemView.delegate, theItemViewsComponent); + compare(theItemView.model, testModel); + } + + function test_aaa_basic_add_remove() { // aaa to ensure execution first + clear_data() + compare(map.mapItems.length, 1) + compare(map.mapItems[0], prepopulatedCircle) + compare(mapItemSpy.count, 0) + // nonexistent + map.removeMapItem(externalCircle) + compare(mapItemSpy.count, 0) + compare(map.mapItems.length, 1) + compare(map.mapItems[0], prepopulatedCircle) + // real + map.removeMapItem(prepopulatedCircle) + compare(mapItemSpy.count, 1) + compare(map.mapItems.length, 0) + map.addMapItem(externalCircle) + map.addMapItem(prepopulatedCircle) + compare(mapItemSpy.count, 3) + compare(map.mapItems.length, 2) + // same again + map.addMapItem(prepopulatedCircle) + compare(mapItemSpy.count, 3) + compare(map.mapItems.length, 2) + compare(map.mapItems[0], externalCircle) + compare(map.mapItems[1], prepopulatedCircle) + map.removeMapItem(externalCircle) + compare(map.mapItems[0], prepopulatedCircle) + compare(mapItemSpy.count, 4) + compare(map.mapItems.length, 1) + map.clearMapItems() + compare(mapItemSpy.count, 5) + compare(map.mapItems.length, 0) + // empty map, do not crash + map.clearMapItems() + compare(mapItemSpy.count, 5) + compare(map.mapItems.length, 0) + } + + function test_dynamic_map_and_items() { + clear_data(); + /* + // basic create-destroy without items, mustn't crash + var dynamicMap = Qt.createQmlObject('import QtQuick 2.0; import QtLocation 5.3; Map { x:0; y:0; objectName: \'dynomik map\'; width: masterItem.width; height: masterItem.height; plugin: testPlugin} ', masterItem, "dynamicCreationErrors" ); + verify(dynamicMap !== null) + dynamicMap.destroy(1) + //wait(5) + + // add rm add, destroy with item on it + dynamicMap = Qt.createQmlObject('import QtQuick 2.0; import QtLocation 5.3; Map { x:0; y:0; objectName: \'dynomik map\'; width: masterItem.width; height: masterItem.height; plugin: testPlugin} ', masterItem, "dynamicCreationErrors" ); + verify(dynamicMap !== null) + dynamicMap.addMapItem(externalCircle); + compare(dynamicMap.mapItems.length, 1) + dynamicMap.removeMapItem(externalCircle); + compare(dynamicMap.mapItems.length, 0) + dynamicMap.addMapItem(externalCircle); + compare(dynamicMap.mapItems.length, 1) + dynamicMap.destroy(1) + //wait(5) + + // try adding same item to two maps, will not be allowed + var dynamicMap2 = Qt.createQmlObject('import QtQuick 2.0; import QtLocation 5.3; Map { x:0; y:0; objectName: \'dynomik map2\'; width: masterItem.width; height: masterItem.height; plugin: testPlugin} ', masterItem, "dynamicCreationErrors" ); + dynamicMap = Qt.createQmlObject('import QtQuick 2.0; import QtLocation 5.3; Map { x:0; y:0; objectName: \'dynomik map\'; width: masterItem.width; height: masterItem.height; plugin: testPlugin} ', masterItem, "dynamicCreationErrors" ); + verify(dynamicMap !== null) + verify(dynamicMap2 !== null) + compare(dynamicMap.mapItems.length, 0) + dynamicMap.addMapItem(externalCircle3); + compare(dynamicMap.mapItems.length, 1) + dynamicMap2.addMapItem(externalCircle3); + compare(dynamicMap2.mapItems.length, 0) + + // create and destroy a dynamic item that is in the map + var dynamicCircle = Qt.createQmlObject('import QtQuick 2.0; import QtLocation 5.3; MapCircle { objectName: \'dynamic circle 1\'; center { latitude: 5; longitude: 5 } radius: 15 } ', masterItem, "dynamicCreationErrors" ); + verify (dynamicCircle !== null) + compare(map.mapItems.length, 0) + map.addMapItem(dynamicCircle) + compare(mapItemSpy.count, 1) + compare(map.mapItems.length, 1) + dynamicCircle.destroy(1) + tryCompare(mapItemSpy, "count", 2) + compare(map.mapItems.length, 0) + + // leave one map item, will be destroyed at the end of the case + dynamicMap.addMapItem(externalCircle); + compare(dynamicMap.mapItems.length, 2) + + // leave a handful of item from model to the map and let it destroy + compare(map3.mapItems.length, 0) + testModel3.datacount = 4 + testModel3.update() + compare(map3.mapItems.length, 4) + */ + } + + function test_add_and_remove_with_view() { + // Basic adding and removing of static object + tryCompare(mapForView, "mapItemsLength", 8) // 1 declared and 7 from model + mapForView.addMapItem(internalCircle) + compare(mapForView.mapItems.length, 8) + mapForView.removeMapItem(internalCircle) + compare(mapForView.mapItems.length, 7) + mapForView.removeMapItem(internalCircle) + compare(mapForView.mapItems.length, 7) + // Basic adding and removing of dynamic object + var dynamicCircle = Qt.createQmlObject( "import QtQuick 2.0; import QtLocation 5.3; MapCircle {radius: 4000; center: mapDefaultCenter}", map, ""); + mapForView.addMapItem(dynamicCircle) + compare(mapForView.mapItems.length, 8) + mapForView.removeMapItem(dynamicCircle) + compare(mapForView.mapItems.length, 7) + mapForView.removeMapItem(dynamicCircle) + compare(mapForView.mapItems.length, 7) + } + SignalSpy {id: model1Spy; target: testModel; signalName: "modelChanged"} + SignalSpy {id: model2Spy; target: testModel2; signalName: "modelChanged"} + function test_model_change() { + // Ensure that internalCircle is removed + mapForView.removeMapItem(internalCircle) + + // Change the model of an MapItemView on the fly + // and verify that object counts change accordingly. + testModel.datacount = 7 + testModel.update() + + tryCompare(mapForView, "mapItemsLength", 7) + testModel.datacount += 2 + testModel2.datacount += 1 + // delegate spawning is async. wait a bit. + wait(1) + tryCompare(mapForView, "mapItemsLength", 9) + + theItemView.model = testModel + compare(mapForView.mapItems.length, 9) + theItemView.model = testModel2 + tryCompare(mapForView, "mapItemsLength", 4) + } + + function test_listmodel() { + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + + for (var i = 0; i < 3; ++i) { + var itemCoord = mapForTestingListModel.mapItems[i].center + var index = mapForTestingListModel.itemCoordinates.indexOf(itemCoord) + verify(0 <= index && index < 3) + } + + testingListModel.remove(0) + // default exit animation kicks in here, of length 300msec + tryCompare(mapForTestingListModel, "mapItemsLength", 2) + + for (var i = 0; i < 2; ++i) { + itemCoord = mapForTestingListModel.mapItems[i].center + index = mapForTestingListModel.itemCoordinates.indexOf(itemCoord) + verify(1 <= index && index < 3) + } + + testingListModel.append({ lat: 1, lon: 1 }) + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + compare(mapForTestingListModel.mapItems[2].center, QtPositioning.coordinate(1, 1)) + + testingListModel.clear() + // default exit animation kicks in here, of length 300msec + tryCompare(mapForTestingListModel, "mapItemsLength", 0) + + // Repopulating the model with initial data + testingListModel.append({ "lat": 11, "lon": 31 }) + testingListModel.append({ "lat": 12, "lon": 32 }) + testingListModel.append({ "lat": 13, "lon": 33 }) + } + + function test_add_extra_listmodel() { + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + mapForTestingListModel.addMapItemView(listModelItemViewExtra) + tryCompare(mapForTestingListModel, "mapItemsLength", 6) + mapForTestingListModel.removeMapItemView(listModelItemViewExtra) + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + mapForTestingListModel.addMapItemView(listModelItemViewExtra) + tryCompare(mapForTestingListModel, "mapItemsLength", 6) + mapForTestingListModel.removeMapItemView(listModelItemView) + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + mapForTestingListModel.removeMapItemView(listModelItemViewExtra) + tryCompare(mapForTestingListModel, "mapItemsLength", 0) + mapForTestingListModel.addMapItemView(listModelItemViewExtra) + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + testingListModelExtra.clear() + tryCompare(mapForTestingListModel, "mapItemsLength", 0) + mapForTestingListModel.removeMapItemView(listModelItemViewExtra) + + mapForTestingListModel.addMapItemView(listModelItemView) + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + + testingListModelExtra.append({ "lat": 11, "lon": 31 }) + testingListModelExtra.append({ "lat": 12, "lon": 32 }) + testingListModelExtra.append({ "lat": 13, "lon": 33 }) + + mapForTestingListModel.addMapItemView(listModelItemViewExtra) + tryCompare(mapForTestingListModel, "mapItemsLength", 6) + mapForTestingListModel.removeMapItemView(listModelItemViewExtra) + tryCompare(mapForTestingListModel, "mapItemsLength", 3) + } + + function test_routemodel() { + testModel.reset(); + mapItemsChangedSpy.clear() + compare(mapForTestingRouteModel.mapItems.length, 0) // precondition + compare(mapItemsChangedSpy.count, 0) + routeQuery.numberAlternativeRoutes = 4 + routeModel.update(); + tryCompare(mapForTestingRouteModel, "mapItemsLength", 4) + routeQuery.numberAlternativeRoutes = 3 + routeModel.update(); + tryCompare(mapForTestingRouteModel, "mapItemsLength", 3) + routeModel.reset(); + // default exit animation kicks in here, of length 300msec + tryCompare(mapForTestingRouteModel, "mapItemsLength", 0) + routeModel.reset(); // clear empty model + routeQuery.numberAlternativeRoutes = 3 + routeModel.update(); + tryCompare(mapForTestingRouteModel, "mapItemsLength", 3) + + // Test adding the extra mapitemview fed from the same route model + mapForTestingRouteModel.addMapItemView(routeItemViewExtra) + tryCompare(mapForTestingRouteModel, "mapItemsLength", 6) + routeQuery.numberAlternativeRoutes = 4 + routeModel.update(); + tryCompare(mapForTestingRouteModel, "mapItemsLength", 8) + routeQuery.numberAlternativeRoutes = 3 + routeModel.update(); + mapForTestingRouteModel.removeMapItemView(routeItemViewExtra) + tryCompare(mapForTestingRouteModel, "mapItemsLength", 3) + + mapForTestingRouteModel.addMapItem(externalCircle2) + compare(mapForTestingRouteModel.mapItems.length, 4) + compare(mapForTestingRouteModel.mapItems[3], externalCircle2) + routeModel.reset(); + // default exit animation kicks in here, of length 300msec + tryCompare(mapForTestingRouteModel, "mapItemsLength", 1) + mapForTestingRouteModel.clearMapItems() + compare(mapForTestingRouteModel.mapItems.length, 0) + + // Test the mapItems list + mapForTestingRouteModel.addMapItem(externalCircle2) + compare(mapForTestingRouteModel.mapItems.length, 1) + compare(mapForTestingRouteModel.mapItems[0], externalCircle2) + + mapForTestingRouteModel.addMapItem(externalRectangle) + compare(mapForTestingRouteModel.mapItems.length, 2) + compare(mapForTestingRouteModel.mapItems[1], externalRectangle) + + mapForTestingRouteModel.addMapItem(externalRectangle) + compare(mapForTestingRouteModel.mapItems.length, 2) + compare(mapForTestingRouteModel.mapItems[1], externalRectangle) + + mapForTestingRouteModel.addMapItem(externalPolygon) + compare(mapForTestingRouteModel.mapItems.length, 3) + compare(mapForTestingRouteModel.mapItems[2], externalPolygon) + + mapForTestingRouteModel.addMapItem(externalQuickItem) + compare(mapForTestingRouteModel.mapItems.length, 4) + compare(mapForTestingRouteModel.mapItems[3], externalQuickItem) + + mapForTestingRouteModel.removeMapItem(externalCircle2) + compare(mapForTestingRouteModel.mapItems.length, 3) + compare(mapForTestingRouteModel.mapItems[0], externalRectangle) + + mapForTestingRouteModel.removeMapItem(externalRectangle) + compare(mapForTestingRouteModel.mapItems.length, 2) + compare(mapForTestingRouteModel.mapItems[0], externalPolygon) + + mapForTestingRouteModel.clearMapItems() + compare(mapForTestingRouteModel.mapItems.length, 0) + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_keepgrab.qml b/tests/auto/declarative_ui/tst_map_keepgrab.qml new file mode 100644 index 0000000..7690b78 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_keepgrab.qml @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtTest 1.0 +import QtLocation 5.6 +import QtPositioning 5.5 + +Item { + // General-purpose elements for the test: + id: page + width: 200 + height: 200 + Plugin { id: testPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true } + + + Flickable { + id: flickable + anchors.fill: parent + contentWidth: flickable.width * 4; contentHeight: flickable.height + + Map { + id: map + x: flickable.width + height: flickable.height + width:flickable.width + plugin: testPlugin + } + } + + SignalSpy { id: mapPanStartedSpy; target: map.gesture; signalName: 'panStarted' } + SignalSpy { id: mapPanFinishedSpy; target: map.gesture; signalName: 'panFinished' } + SignalSpy { id: flickStartedSpy; target: flickable; signalName: 'flickStarted' } + SignalSpy { id: flickEndedSpy; target: flickable; signalName: 'flickEnded' } + SignalSpy { id: preventStealingChangedSpy; target: map.gesture; signalName: 'preventStealingChanged' } + + + TestCase { + when: windowShown && map.mapReady + name: "MapKeepGrabAndPreventSteal" + + function initTestCase() + { + compare(map.gesture.preventStealing, false) + } + + function init() + { + map.gesture.acceptedGestures = MapGestureArea.PanGesture | MapGestureArea.FlickGesture; + map.gesture.flickDeceleration = 500 + map.zoomLevel = 1 + map.center = QtPositioning.coordinate(50,50) + map.gesture.preventStealing = false + flickable.contentX = 0 + flickable.contentY = 0 + mapPanStartedSpy.clear() + mapPanFinishedSpy.clear() + flickStartedSpy.clear() + flickEndedSpy.clear() + preventStealingChangedSpy.clear() + } + + function flick() + { + var i = 0 + mousePress(flickable, flickable.width - 1, 0) + for (i = flickable.width; i > 0; i -= 5) { + wait(5) + mouseMove(flickable, i, 0, 0, Qt.LeftButton); + } + mouseRelease(flickable, i, 0) + } + + function pan() + { + var i = 0 + mousePress(map, 0, 0) + for (i = 0; i < flickable.width; i += 5) { + wait(5) + mouseMove(map, i, 0, 0, Qt.LeftButton); + } + mouseRelease(map, i, 0) + } + + function test_flick() + { + var center = QtPositioning.coordinate(map.center.latitude,map.center.longitude) + flick() //flick flickable + tryCompare(flickStartedSpy,"count",1) + pan() //pan map + tryCompare(flickStartedSpy,"count",2) // both directions + tryCompare(flickEndedSpy,"count",1) + tryCompare(mapPanStartedSpy,"count", 0) + tryCompare(mapPanFinishedSpy,"count", 0) + //map should not change + verify(center == map.center) + } + + function test_map_grab() + { + var center = QtPositioning.coordinate(map.center.latitude,map.center.longitude) + pan() //pan map + tryCompare(mapPanStartedSpy,"count",1) + tryCompare(mapPanFinishedSpy, "count", 1) + + compare(flickStartedSpy.count, 0) + compare(flickEndedSpy.count, 0) + //map should change + verify(center != map.center) + } + + function test_map_preventsteal() + { + map.gesture.preventStealing = false + compare(preventStealingChangedSpy.count, 0) + map.gesture.preventStealing = true + compare(preventStealingChangedSpy.count, 1) + + var center = QtPositioning.coordinate(map.center.latitude,map.center.longitude) + flick() //flick flickable + tryCompare(flickStartedSpy,"count",1) + pan() //pan map + tryCompare(flickStartedSpy,"count",1) // both directions + tryCompare(flickEndedSpy,"count",1) + tryCompare(mapPanStartedSpy,"count", 1) + tryCompare(mapPanFinishedSpy,"count", 1) + //map should not change + verify(center != map.center) + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_maptype.qml b/tests/auto/declarative_ui/tst_map_maptype.qml new file mode 100644 index 0000000..f4564ea --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_maptype.qml @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.10 + +Item{ + id: page + x: 0; y: 0; + width: 100 + height: 100 + + Plugin { id: testPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true } + Map { id: map; anchors.fill: parent } + SignalSpy { id: supportedMapTypesSpy; target: map; signalName: "supportedMapTypesChanged" } + SignalSpy { id: activeMapTypeChangedSpy; target: map; signalName: "activeMapTypeChanged" } + SignalSpy { id: minimumZoomLevelChangedSpy; target: map; signalName: "minimumZoomLevelChanged" } + SignalSpy { id: maximumZoomLevelChangedSpy; target: map; signalName: "maximumZoomLevelChanged" } + SignalSpy { id: minimumTiltChangedSpy; target: map; signalName: "minimumTiltChanged" } + SignalSpy { id: maximumTiltChangedSpy; target: map; signalName: "maximumTiltChanged" } + SignalSpy { id: minimumFieldOfViewChangedSpy; target: map; signalName: "minimumFieldOfViewChanged" } + SignalSpy { id: maximumFieldOfViewChangedSpy; target: map; signalName: "maximumFieldOfViewChanged" } + + TestCase { + id: testCase + name: "MapType" + when: windowShown + + function initTestCase() + { + compare(map.supportedMapTypes.length, 0) + compare(map.activeMapType.style, MapType.NoMap) + map.plugin = testPlugin + tryCompare(supportedMapTypesSpy, "count", 1) + compare(map.supportedMapTypes.length, 4) + compare(map.supportedMapTypes[0].style, MapType.StreetMap) + compare(map.supportedMapTypes[0].name, "StreetMap") + compare(map.supportedMapTypes[0].description, "StreetMap") + compare(map.supportedMapTypes[1].style, MapType.SatelliteMapDay) + compare(map.supportedMapTypes[1].name, "SatelliteMapDay") + compare(map.supportedMapTypes[1].description, "SatelliteMapDay") + compare(map.supportedMapTypes[2].style, MapType.CycleMap) + compare(map.supportedMapTypes[2].name, "CycleMap") + compare(map.supportedMapTypes[2].description, "CycleMap") + compare(map.supportedMapTypes[3].style, MapType.CustomMap) + compare(map.supportedMapTypes[3].name, "AlternateCameraCapabilities") + compare(map.supportedMapTypes[3].description, "AlternateCameraCapabilities") + //default + compare(map.activeMapType.style, MapType.StreetMap) + } + + function init() + { + map.activeMapType = map.supportedMapTypes[0] + + supportedMapTypesSpy.clear() + activeMapTypeChangedSpy.clear() + minimumZoomLevelChangedSpy.clear(); + maximumZoomLevelChangedSpy.clear(); + minimumTiltChangedSpy.clear(); + maximumTiltChangedSpy.clear(); + minimumFieldOfViewChangedSpy.clear(); + maximumFieldOfViewChangedSpy.clear(); + } + + function test_setting_types() + { + // resetting it first + map.activeMapType = map.supportedMapTypes[0] + tryCompare(activeMapTypeChangedSpy, "count", 0) + + map.activeMapType = map.supportedMapTypes[1] + tryCompare(activeMapTypeChangedSpy, "count", 1) + compare(map.supportedMapTypes[1].name, map.activeMapType.name) + compare(map.supportedMapTypes[1].style, map.activeMapType.style) + + map.activeMapType = map.supportedMapTypes[2] + tryCompare(activeMapTypeChangedSpy, "count", 2) + compare(map.supportedMapTypes[2].name, map.activeMapType.name) + compare(map.supportedMapTypes[2].style, map.activeMapType.style) + + map.activeMapType = map.supportedMapTypes[3] + tryCompare(activeMapTypeChangedSpy, "count", 3) + compare(map.supportedMapTypes[3].name, map.activeMapType.name) + compare(map.supportedMapTypes[3].style, map.activeMapType.style) + } + + function test_maptype_capabilities() + { + map.activeMapType = map.supportedMapTypes[0] + + compare(map.minimumZoomLevel, 0) + compare(map.maximumZoomLevel, 20) + compare(map.minimumTilt, 0) + compare(map.maximumTilt, 60) + compare(map.minimumFieldOfView, 45) + compare(map.maximumFieldOfView, 45) + + compare(map.activeMapType.cameraCapabilities.minimumZoomLevel, 0) + compare(map.activeMapType.cameraCapabilities.maximumZoomLevel, 20) + compare(map.activeMapType.cameraCapabilities.minimumTilt, 0) + compare(map.activeMapType.cameraCapabilities.maximumTilt, 60) + compare(map.activeMapType.cameraCapabilities.minimumFieldOfView, 45) + compare(map.activeMapType.cameraCapabilities.maximumFieldOfView, 45) + + tryCompare(minimumZoomLevelChangedSpy, "count", 0) + tryCompare(maximumZoomLevelChangedSpy, "count", 0) + tryCompare(minimumTiltChangedSpy, "count", 0) + tryCompare(maximumTiltChangedSpy, "count", 0) + tryCompare(minimumFieldOfViewChangedSpy, "count", 0) + tryCompare(maximumFieldOfViewChangedSpy, "count", 0) + + + map.activeMapType = map.supportedMapTypes[1] + + compare(map.minimumZoomLevel, 0) + compare(map.maximumZoomLevel, 20) + compare(map.minimumTilt, 0) + compare(map.maximumTilt, 60) + compare(map.minimumFieldOfView, 45) + compare(map.maximumFieldOfView, 45) + + compare(map.activeMapType.cameraCapabilities.minimumZoomLevel, 0) + compare(map.activeMapType.cameraCapabilities.maximumZoomLevel, 20) + compare(map.activeMapType.cameraCapabilities.minimumTilt, 0) + compare(map.activeMapType.cameraCapabilities.maximumTilt, 60) + compare(map.activeMapType.cameraCapabilities.minimumFieldOfView, 45) + compare(map.activeMapType.cameraCapabilities.maximumFieldOfView, 45) + + tryCompare(minimumZoomLevelChangedSpy, "count", 0) + tryCompare(maximumZoomLevelChangedSpy, "count", 0) + tryCompare(minimumTiltChangedSpy, "count", 0) + tryCompare(maximumTiltChangedSpy, "count", 0) + tryCompare(minimumFieldOfViewChangedSpy, "count", 0) + tryCompare(maximumFieldOfViewChangedSpy, "count", 0) + + + map.activeMapType = map.supportedMapTypes[3] + + compare(map.minimumZoomLevel, 0) + compare(map.maximumZoomLevel, 19) + compare(map.minimumTilt, 0) + compare(map.maximumTilt, 80) + compare(map.minimumFieldOfView, 1) + compare(map.maximumFieldOfView, 179) + + compare(map.activeMapType.cameraCapabilities.minimumZoomLevel, 0) + compare(map.activeMapType.cameraCapabilities.maximumZoomLevel, 19) + compare(map.activeMapType.cameraCapabilities.minimumTilt, 0) + compare(map.activeMapType.cameraCapabilities.maximumTilt, 80) + compare(map.activeMapType.cameraCapabilities.minimumFieldOfView, 1) + compare(map.activeMapType.cameraCapabilities.maximumFieldOfView, 179) + + tryCompare(minimumZoomLevelChangedSpy, "count", 0) + tryCompare(maximumZoomLevelChangedSpy, "count", 1) + tryCompare(minimumTiltChangedSpy, "count", 0) + tryCompare(maximumTiltChangedSpy, "count", 1) + tryCompare(minimumFieldOfViewChangedSpy, "count", 1) + tryCompare(maximumFieldOfViewChangedSpy, "count", 1) + + + map.activeMapType = map.supportedMapTypes[0] + + compare(map.minimumZoomLevel, 0) + compare(map.maximumZoomLevel, 20) + compare(map.minimumTilt, 0) + compare(map.maximumTilt, 60) + compare(map.minimumFieldOfView, 45) + compare(map.maximumFieldOfView, 45) + + compare(map.activeMapType.cameraCapabilities.minimumZoomLevel, 0) + compare(map.activeMapType.cameraCapabilities.maximumZoomLevel, 20) + compare(map.activeMapType.cameraCapabilities.minimumTilt, 0) + compare(map.activeMapType.cameraCapabilities.maximumTilt, 60) + compare(map.activeMapType.cameraCapabilities.minimumFieldOfView, 45) + compare(map.activeMapType.cameraCapabilities.maximumFieldOfView, 45) + + tryCompare(minimumZoomLevelChangedSpy, "count", 0) + tryCompare(maximumZoomLevelChangedSpy, "count", 2) + tryCompare(minimumTiltChangedSpy, "count", 0) + tryCompare(maximumTiltChangedSpy, "count", 2) + tryCompare(minimumFieldOfViewChangedSpy, "count", 2) + tryCompare(maximumFieldOfViewChangedSpy, "count", 2) + } + + function test_maptype_metadata() + { + map.activeMapType = map.supportedMapTypes[0] + compare(Object.keys(map.activeMapType.metadata).length, 0) + + map.activeMapType = map.supportedMapTypes[3] + compare(Object.keys(map.activeMapType.metadata).length, 1) + compare(map.activeMapType.metadata['foo'], 42) + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_mouse.qml b/tests/auto/declarative_ui/tst_map_mouse.qml new file mode 100644 index 0000000..dd98df6 --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_mouse.qml @@ -0,0 +1,731 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtTest 1.0 +import QtLocation 5.6 +import QtPositioning 5.5 + + /* + MouseArea setup for this test case. + Map dimensions are 100 * 100 + Item containing map is 120,120 + + (50,50) + (0,0) ---------------------------------------------------- (100,0) + | no mouse area | mouse area overlapper | + | | | + (0,20) ---------------------------------------------------- (100,20) + | mouse area upper | mouse area upper, | + | | mouse area overlapper | + | | | + | | | + | | | + (0,50) ---------------------------------------------------- (100,50) + | mouse area lower | mouse area lower, | + | | mouse area overlapper | + | | | + | | | + | | | + | | | + | | | + | | | + (0,100) ---------------------------------------------------- (100,100) | + | + | + ----------(120, 120) + + */ + +Item { + id: page + x: 0; y: 0; + width: 120 + height: 120 + // General-purpose elements for the test: + Plugin { id: testPlugin; name : "qmlgeo.test.plugin"; allowExperimental: true } + + function setMouseData(ma, me) + { + ma.lastX = me.x + ma.lastY = me.y + ma.lastButton = me.button + ma.lastButtons = me.buttons + ma.lastModifiers = me.modifiers + ma.lastWasHeld = me.wasHeld + ma.lastIsClick = me.isClick + ma.lastAccepted = me.accepted + } + + Map { + id: map; + x: 0; y: 0; width: 100; height: 100 + center { + latitude: 20 + longitude: 20 + } + + plugin: testPlugin; + + MouseArea { + id: mouseUpper + objectName: "mouseUpper" + x: 0; y: 20; width: 100; height: 29 + property int lastX: -1 + property int lastY: -1 + property int lastButton: Qt.NoButton + property int lastButtons: Qt.NoButton + property int lastModifiers: Qt.NoModifier + property bool lastWasHeld: false; + property bool lastIsClick: false + property bool lastAccepted: false; + + preventStealing: true + + onClicked: page.setMouseData(mouseUpper, mouse) + onDoubleClicked: page.setMouseData(mouseUpper, mouse) + onPressed: page.setMouseData(mouseUpper, mouse) + onReleased: page.setMouseData(mouseUpper, mouse) + onPositionChanged: page.setMouseData(mouseUpper, mouse) + onPressAndHold: page.setMouseData(mouseUpper, mouse) + } + MouseArea { + id: mouseLower + objectName: "mouseLower" + x: 0; y: 50; width: 100; height: 50 + property int lastX: -1 + property int lastY: -1 + property int lastButton: Qt.NoButton + property int lastButtons: Qt.NoButton + property int lastModifiers: Qt.NoModifier + property bool lastWasHeld: false; + property bool lastIsClick: false + property bool lastAccepted: false; + + onClicked: page.setMouseData(mouseLower, mouse) + onDoubleClicked: page.setMouseData(mouseLower, mouse) + onPressed: page.setMouseData(mouseLower, mouse) + onReleased: page.setMouseData(mouseLower, mouse) + onPositionChanged: page.setMouseData(mouseLower, mouse) + onPressAndHold: page.setMouseData(mouseLower, mouse) + } + MouseArea { + id: mouseOverlapper + objectName: "mouseOverlapper" + x: 50; y: 0; width: 50; height: 100 + property int lastX: -1 + property int lastY: -1 + property int lastButton: Qt.NoButton + property int lastButtons: Qt.NoButton + property int lastModifiers: Qt.NoModifier + property bool lastWasHeld: false; + property bool lastIsClick: false + property bool lastAccepted: false; + + onClicked: page.setMouseData(mouseOverlapper, mouse) + onDoubleClicked: page.setMouseData(mouseOverlapper, mouse) + onPressed: page.setMouseData(mouseOverlapper, mouse) + onReleased: page.setMouseData(mouseOverlapper, mouse) + onPositionChanged: page.setMouseData(mouseOverlapper, mouse) + onPressAndHold: page.setMouseData(mouseOverlapper, mouse) + } + } + + TestCase { + name: "MouseArea" + when: windowShown && map.mapReady + SignalSpy {id: mouseUpperClickedSpy; target: mouseUpper; signalName: "clicked"} + SignalSpy {id: mouseLowerClickedSpy; target: mouseLower; signalName: "clicked"} + SignalSpy {id: mouseOverlapperClickedSpy; target: mouseOverlapper; signalName: "clicked"} + SignalSpy {id: mouseUpperDoubleClickedSpy; target: mouseUpper; signalName: "doubleClicked"} + SignalSpy {id: mouseLowerDoubleClickedSpy; target: mouseLower; signalName: "doubleClicked"} + SignalSpy {id: mouseOverlapperDoubleClickedSpy; target: mouseOverlapper; signalName: "doubleClicked"} + SignalSpy {id: mouseUpperPressedSpy; target: mouseUpper; signalName: "onPressed"} + SignalSpy {id: mouseLowerPressedSpy; target: mouseLower; signalName: "onPressed"} + SignalSpy {id: mouseOverlapperPressedSpy; target: mouseOverlapper; signalName: "onPressed"} + SignalSpy {id: mouseUpperReleasedSpy; target: mouseUpper; signalName: "released"} + SignalSpy {id: mouseLowerReleasedSpy; target: mouseLower; signalName: "released"} + SignalSpy {id: mouseOverlapperReleasedSpy; target: mouseOverlapper; signalName: "released"} + SignalSpy {id: mouseUpperPositionChangedSpy; target: mouseUpper; signalName: "positionChanged"} + SignalSpy {id: mouseLowerPositionChangedSpy; target: mouseLower; signalName: "positionChanged"} + SignalSpy {id: mouseOverlapperPositionChangedSpy; target: mouseOverlapper; signalName: "positionChanged"} + SignalSpy {id: mouseUpperPressAndHoldSpy; target: mouseUpper; signalName: "pressAndHold"} + SignalSpy {id: mouseLowerPressAndHoldSpy; target: mouseLower; signalName: "pressAndHold"} + SignalSpy {id: mouseOverlapperPressAndHoldSpy; target: mouseOverlapper; signalName: "pressAndHold"} + SignalSpy {id: mouseUpperEnteredSpy; target: mouseUpper; signalName: "entered"} + SignalSpy {id: mouseLowerEnteredSpy; target: mouseLower; signalName: "entered"} + SignalSpy {id: mouseOverlapperEnteredSpy; target: mouseOverlapper; signalName: "entered"} + SignalSpy {id: mouseUpperExitedSpy; target: mouseUpper; signalName: "exited"} + SignalSpy {id: mouseLowerExitedSpy; target: mouseLower; signalName: "exited"} + SignalSpy {id: mouseOverlapperExitedSpy; target: mouseOverlapper; signalName: "exited"} + + SignalSpy {id: mouseUpperEnabledChangedSpy; target: mouseUpper; signalName: "enabledChanged"} + SignalSpy {id: mouseUpperAcceptedButtonsChangedSpy; target: mouseUpper; signalName: "acceptedButtonsChanged"} + SignalSpy {id: mouseUpperPressedButtonsChangedSpy; target: mouseUpper; signalName: "pressedChanged"} + SignalSpy {id: mouseUpperHoveredChangedSpy; target: mouseUpper; signalName: "hoveredChanged"} + SignalSpy {id: mouseUpperPressedChangedSpy; target: mouseUpper; signalName: "pressedChanged"} + + SignalSpy {id: mouseOverlapperEnabledChangedSpy; target: mouseOverlapper; signalName: "enabledChanged"} + + function clear_data() { + mouseUpperClickedSpy.clear() + mouseLowerClickedSpy.clear() + mouseOverlapperClickedSpy.clear() + mouseUpperDoubleClickedSpy.clear() + mouseLowerDoubleClickedSpy.clear() + mouseOverlapperDoubleClickedSpy.clear() + mouseUpperPressedSpy.clear() + mouseLowerPressedSpy.clear() + mouseOverlapperPressedSpy.clear() + mouseUpperReleasedSpy.clear() + mouseLowerReleasedSpy.clear() + mouseOverlapperReleasedSpy.clear() + mouseUpperPositionChangedSpy.clear() + mouseLowerPositionChangedSpy.clear() + mouseOverlapperPositionChangedSpy.clear() + mouseUpperPressAndHoldSpy.clear() + mouseLowerPressAndHoldSpy.clear() + mouseOverlapperPressAndHoldSpy.clear() + mouseUpperEnteredSpy.clear() + mouseLowerEnteredSpy.clear() + mouseOverlapperEnteredSpy.clear() + mouseUpperExitedSpy.clear() + mouseLowerExitedSpy.clear() + mouseOverlapperExitedSpy.clear() + + mouseUpperEnabledChangedSpy.clear() + mouseUpperAcceptedButtonsChangedSpy.clear() + mouseUpperPressedButtonsChangedSpy.clear() + mouseUpperHoveredChangedSpy.clear() + mouseUpperPressedChangedSpy.clear() + mouseUpperPositionChangedSpy.clear() + + mouseOverlapperEnabledChangedSpy.clear() + } + // these 'real_' prefixed functions do sequences as + // it would occur on real app (e.g. doubleclick is in fact + // a sequence of press, release, doubleclick, release). + // (they were recorded as seen on test app). mouseClick() works ok + // because testlib internally converts it to mousePress + mouseRelease events + function real_click (target, x, y) { + mousePress(target, x,y) + mouseRelease(target, x, y) + } + function real_double_click (target, x, y) { + mousePress(target, x,y) + mouseRelease(target, x, y) + mousePress(target, x, y) + mouseDoubleClick(target, x, y) + mouseRelease(target, x, y) + } + function real_press_and_hold(target, x,y) { + mousePress(target,x,y) + wait(1000) // threshold is 800 ms + mouseRelease(target,x, y) + } + + function test_enabled() { + clear_data() + // check that disabling overlapping mouse areas let events flow through + mouseUpper.enabled = false + compare(mouseUpperEnabledChangedSpy.count, 1) + compare(mouseUpperClickedSpy.count, 0) + mouseClick(map, 5, 25) + compare(mouseUpperClickedSpy.count, 0) + mouseUpper.enabled = true + mouseClick(map, 5, 25) + tryCompare(mouseUpperClickedSpy, "count", 1) + compare(mouseUpperEnabledChangedSpy.count, 2) + // when overlapping are is disabled, the event should flow through + compare(mouseOverlapperClickedSpy.count, 0) + mouseClick(map, 55, 25) + tryCompare(mouseUpperClickedSpy, "count", 1) + compare(mouseOverlapperClickedSpy.count, 1) + mouseOverlapper.enabled = false + compare(mouseOverlapperEnabledChangedSpy.count, 1) + compare(mouseOverlapper.enabled, false) + mouseClick(map, 55, 25) + tryCompare(mouseOverlapperClickedSpy, "count", 1) + compare(mouseUpperClickedSpy.count, 2) + // re-enable and verify that still works + mouseOverlapper.enabled = true + compare(mouseOverlapperEnabledChangedSpy.count, 2) + compare(mouseOverlapper.enabled, true) + mouseClick(map, 55, 25) + tryCompare(mouseOverlapperClickedSpy, "count", 2) // should consume again + compare(mouseUpperClickedSpy.count, 2) + } + + function test_wheel() { + clear_data() + wait(500); + // on map but without mouse area + var startZoomLevel = 6.20 + map.zoomLevel = startZoomLevel + mouseWheel(map, 5, 5, 15, 5, Qt.LeftButton, Qt.NoModifiers) + //see QDeclarativeGeoMapGestureArea::handleWheelEvent + var endZoomLevel = startZoomLevel + 5 * 0.001 + compare(map.zoomLevel,endZoomLevel) + + map.zoomLevel = startZoomLevel + mouseWheel(map, 5, 5, -15, -5, Qt.LeftButton, Qt.NoModifiers) + //see QDeclarativeGeoMapGestureArea::handleWheelEvent + endZoomLevel = startZoomLevel - 5 * 0.001 + compare(map.zoomLevel,endZoomLevel) + + // on map on top of mouse area + map.zoomLevel = startZoomLevel + mouseWheel(map, 55, 75, -30, -2, Qt.LeftButton, Qt.NoModifiers) + endZoomLevel = startZoomLevel - 2 * 0.001 + compare(map.zoomLevel,endZoomLevel) + + // outside of map + map.zoomLevel = startZoomLevel + mouseWheel(map, -100, -100, 40, 4, Qt.LeftButton, Qt.NoModifiers) + compare(map.zoomLevel,startZoomLevel) + } + + function test_aaa_basic_properties() // _aaa_ to ensure execution first + { + clear_data() + wait(50) + // default values + compare(mouseUpper.containsMouse, false) + compare(mouseUpper.pressed, false) + compare(mouseUpper.enabled, true) + compare(mouseUpper.pressedButtons, 0) + compare(mouseUpper.acceptedButtons, Qt.LeftButton) + // accepted buttons + compare(mouseUpperAcceptedButtonsChangedSpy.count, 0) + mouseUpper.acceptedButtons = Qt.RightButton | Qt.MiddleButton + compare(mouseUpper.acceptedButtons, Qt.RightButton | Qt.MiddleButton) + compare(mouseUpperAcceptedButtonsChangedSpy.count, 1) + mouseClick(map, 5, 25) + compare(mouseUpperClickedSpy.count, 0) // left button not accepted + mouseUpper.acceptedButtons = Qt.LeftButton + compare(mouseUpperAcceptedButtonsChangedSpy.count, 2) + mouseClick(map, 5, 25) + tryCompare(mouseUpperClickedSpy, "count", 1) + } + + function test_basic_position_changed() { + // tests basic position changed/move when button is being pressed + clear_data(); + wait(500); + mousePress(map, 5, 25) + compare(mouseUpperPressedSpy.count, 1) + compare(mouseUpper.lastAccepted, true) + compare(mouseUpper.lastButton, Qt.LeftButton) + compare(mouseUpper.lastButtons, Qt.LeftButton) + compare(mouseUpper.lastModifiers, Qt.NoModifier) + // moves within the mouse area + mouseMove(map, 5, 26, 0, Qt.LeftButton) // '0' is 'delay' + wait(1) // mouseMove event goes one extra eventloop round in the test lib + compare(mouseUpperEnteredSpy.count, 1) + compare(mouseUpperPositionChangedSpy.count, 1) + compare(mouseUpper.mouseX, 5) + compare(mouseUpper.mouseY, 6) // 20 offset, mouseXY is relative to the mouse area + compare(mouseUpper.lastAccepted, true) + compare(mouseUpper.lastButton, Qt.NoButton) + compare(mouseUpper.lastButtons, Qt.LeftButton) // buttons being pressed whilst movin' + compare(mouseUpper.lastModifiers, Qt.NoModifier) + compare(mouseUpper.lastWasHeld, false) // testfunction won't take required 800 ms + compare(mouseUpper.lastX, 5) + compare(mouseUpper.lastY, 6) // remember 20 offset of the mouse area + + mouseMove(map, 6, 27, 0, Qt.LeftButton | Qt.RightButton) + wait(1) + compare(mouseUpperEnteredSpy.count, 1) // no re-entry + compare(mouseUpperPositionChangedSpy.count, 2) + compare(mouseUpper.mouseX, 6) + compare(mouseUpper.mouseY, 7) + compare(mouseUpper.lastAccepted, true) + compare(mouseUpper.lastButton, Qt.NoButton) + compare(mouseUpper.lastButtons, Qt.LeftButton | Qt.RightButton) // buttons being pressed whilst movin' + compare(mouseUpper.lastModifiers, Qt.NoModifier) + compare(mouseUpper.lastWasHeld, false) // testfunction won't take required 800 ms + compare(mouseUpper.lastX, 6) + compare(mouseUpper.lastY, 7) // remember 20 offset of the mouse area + + // moves outside of mouse but within map + mouseMove(map, 2, 2, 0) + wait(1) + compare(mouseUpperExitedSpy.count, 1) + compare(mouseUpperPositionChangedSpy.count, 3) + compare(mouseUpper.mouseX, 2) + compare(mouseUpper.mouseY, -18) + // come back to map + mouseMove(map, 7, 28, 0) + wait(1) + compare(mouseUpperEnteredSpy.count, 2) + compare(mouseUpperExitedSpy.count, 1) + compare(mouseUpperPositionChangedSpy.count, 4) + compare(mouseUpper.mouseX, 7) + compare(mouseUpper.mouseY, 8) + + // move outside of widget area (left). make sure that other mouse areas won't get the events + mouseMove(map, -10, 10, 0) + wait(1) + compare(mouseUpperPositionChangedSpy.count, 5) + compare(mouseUpperExitedSpy.count, 2) + compare(mouseUpper.mouseX, -10) + compare(mouseUpper.mouseY, -10) + + // back in and then on top of the widget + mouseMove(map, 5, 25, 0) + wait(1) + compare(mouseUpperPositionChangedSpy.count, 6) + compare(mouseUpperExitedSpy.count, 2) + compare(mouseUpperEnteredSpy.count, 3) + compare(mouseUpper.mouseX, 5) + compare(mouseUpper.mouseY, 5) + mouseMove(map, 5, -25, 0) + wait(1) + compare(mouseUpperPositionChangedSpy.count, 7) + compare(mouseUpperExitedSpy.count, 3) + compare(mouseUpperEnteredSpy.count, 3) + compare(mouseUpper.mouseX, 5) + compare(mouseUpper.mouseY, -45) + + // back in then float on top of other mouse areas + mouseMove(map, 5, 25, 0) + wait(1) + compare(mouseUpperPositionChangedSpy.count, 8) + compare(mouseUpperExitedSpy.count, 3) + compare(mouseUpperEnteredSpy.count, 4) + compare(mouseUpper.mouseX, 5) + compare(mouseUpper.mouseY, 5) + mouseMove(map, 5, 75, 0) + wait(1) + compare(mouseUpperPositionChangedSpy.count, 9) + compare(mouseUpperExitedSpy.count, 4) + compare(mouseUpperEnteredSpy.count, 4) + compare(mouseUpper.mouseX, 5) + compare(mouseUpper.mouseY, 55) // remember the 20 offset of upper mouse area + mouseMove(map, 75, 75, 0) + wait(1) + compare(mouseUpperPositionChangedSpy.count, 10) + compare(mouseUpperExitedSpy.count, 4) + compare(mouseUpperEnteredSpy.count, 4) + compare(mouseUpper.mouseX, 75) + compare(mouseUpper.mouseY, 55) + // finally back in + mouseMove(map, 5, 25, 0) + wait(1) + compare(mouseUpperPositionChangedSpy.count, 11) + compare(mouseUpperExitedSpy.count, 4) + compare(mouseUpperEnteredSpy.count, 5) + compare(mouseUpper.mouseX, 5) + compare(mouseUpper.mouseY, 5) + + // check that these fellas didn't get any stupid ideas + compare(mouseLowerEnteredSpy.count, 0) + compare(mouseLowerPositionChangedSpy.count, 0) + compare(mouseOverlapperEnteredSpy.count, 0) + compare(mouseOverlapperPositionChangedSpy.count, 0) + // release mouse + mouseRelease(map, 5, 25) + // TODO enable these! + compare(mouseUpperEnteredSpy.count, 5) + compare(mouseUpperExitedSpy.count, 5) // release triggers one more exited() + } + + function test_basic_press_release() { + clear_data() + wait(500); + // send to emptiness + mousePress(map, 5, 5) + compare(mouseUpperPressedSpy.count, 0) + compare(mouseLowerPressedSpy.count, 0) + compare(mouseOverlapperPressedSpy.count, 0) + mouseRelease(map, 5, 5) + compare(mouseUpperReleasedSpy.count, 0) + compare(mouseLowerReleasedSpy.count, 0) + compare(mouseOverlapperReleasedSpy.count, 0) + // send to upper mouse area + mousePress(map, 5, 25) + compare(mouseUpperPressedSpy.count, 1) + compare(mouseLowerPressedSpy.count, 0) + compare(mouseOverlapperPressedSpy.count, 0) + + compare(mouseUpper.lastAccepted, true) + compare(mouseUpper.lastButton, Qt.LeftButton) + compare(mouseUpper.lastModifiers, Qt.NoModifier) + compare(mouseUpper.lastWasHeld, false) + compare(mouseUpper.lastX, 5) + compare(mouseUpper.lastY, 5) // remember 20 offset of the mouse area + + mouseRelease(map, 5, 25) + compare(mouseUpperPressedSpy.count, 1) + compare(mouseUpperReleasedSpy.count, 1) + compare(mouseLowerPressedSpy.count, 0) + compare(mouseLowerReleasedSpy.count, 0) + + mousePress(map, 5, 26) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseLowerPressedSpy.count, 0) + compare(mouseOverlapperPressedSpy.count, 0) + + mouseRelease(map, 5, 26) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseUpperReleasedSpy.count, 2) + compare(mouseLowerPressedSpy.count, 0) + compare(mouseLowerReleasedSpy.count, 0) + compare(mouseUpper.lastAccepted, true) + compare(mouseUpper.lastButton, Qt.LeftButton) + compare(mouseUpper.lastModifiers, Qt.NoModifier) + compare(mouseUpper.lastWasHeld, false) + compare(mouseUpper.lastX, 5) + compare(mouseUpper.lastY, 6) // remember 20 offset of the mouse area + + mousePress(map, 5, 75) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseLowerPressedSpy.count, 1) + compare(mouseOverlapperPressedSpy.count, 0) + compare(mouseLower.lastAccepted, true) + compare(mouseLower.lastButton, Qt.LeftButton) + compare(mouseLower.lastModifiers, Qt.NoModifier) + compare(mouseLower.lastWasHeld, false) + compare(mouseLower.lastX, 5) + compare(mouseLower.lastY, 25) // remember 50 offset of the mouse area + + mouseRelease(map, 5, 75) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseUpperReleasedSpy.count, 2) + compare(mouseLowerPressedSpy.count, 1) + compare(mouseLowerReleasedSpy.count, 1) + compare(mouseOverlapperPressedSpy.count, 0) + + mousePress(map, 55, 75) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseLowerPressedSpy.count, 1) + compare(mouseOverlapperPressedSpy.count, 1) + compare(mouseOverlapperReleasedSpy.count, 0) + + mouseMove(map, 55, 25) + mouseRelease(map, 55, 25) + compare(mouseUpperPressedSpy.count, 2) + compare(mouseUpperReleasedSpy.count, 2) + compare(mouseLowerPressedSpy.count, 1) + compare(mouseLowerReleasedSpy.count, 1) + //this should follow the same logic as Flickable, after the gesture is detected, the map should steal events. + compare(mouseOverlapperReleasedSpy.count, 0) + } + + function test_basic_click() { + clear_data(); + wait(500); + + mouseClick(map, 5, 5, Qt.RightButton, Qt.AltModifier) + compare(mouseUpperClickedSpy.count, 0) + compare(mouseLowerClickedSpy.count, 0) + compare(mouseOverlapperClickedSpy.count, 0) + mouseUpper.acceptedButtons = Qt.LeftButton | Qt.RightButton + // TC sending click event to upper mouse area 5,25 + mouseClick(map, 5, 25, Qt.RightButton, Qt.AltModifier) + tryCompare(mouseUpperClickedSpy, "count", 1) + // TC done and clicked was received + //compare(mouseUpperClickedSpy.count, 1) + compare(mouseLowerClickedSpy.count, 0) + compare(mouseOverlapperClickedSpy.count, 0) + compare(mouseUpper.lastAccepted, true) + compare(mouseUpper.lastButton, Qt.RightButton) + compare(mouseUpper.lastModifiers, Qt.AltModifier) + compare(mouseUpper.lastWasHeld, false) + compare(mouseUpper.lastX, 5) + compare(mouseUpper.lastY, 5) // remember 20 offset of the mouse area + // check we get valid geocoordinates (would be NaN if something was wrong) + // todo + //verify(mouseUpper.lastMouseEvent.coordinate.longitude > -180 && mouseUpper.lastMouseEvent.coordinate.longitude < 180) + //verify(mouseUpper.lastMouseEvent.coordinate.longitude > -90 && mouseUpper.lastMouseEvent.coordinate.latitude < 90) + + // mouse click with unaccepted buttons should not cause click + mouseUpper.acceptedButtons = Qt.LeftButton + mouseClick(map, 5, 25, Qt.RightButton, Qt.AltModifier) + tryCompare(mouseUpperClickedSpy, "count", 1) + compare(mouseLowerClickedSpy.count, 0) + compare(mouseOverlapperClickedSpy.count, 0) + + mouseClick(map, 5, 25) + tryCompare(mouseUpperClickedSpy, "count", 2) + compare(mouseLowerClickedSpy.count, 0) + compare(mouseOverlapperClickedSpy.count, 0) + compare(mouseUpper.lastModifiers, Qt.NoModifier) + compare(mouseUpper.lastButton, Qt.LeftButton) + mouseClick(map, 5, 55) + tryCompare(mouseUpperClickedSpy, "count", 2) + compare(mouseLowerClickedSpy.count, 1) + compare(mouseOverlapperClickedSpy.count, 0) + mouseClick(map, 5, 55) + tryCompare(mouseUpperClickedSpy,"count", 2) + compare(mouseLowerClickedSpy.count, 2) + compare(mouseOverlapperClickedSpy.count, 0) + // declaration order counts on overlap case; overlapping area + // declared later will get the events + mouseClick(map, 55, 25) + tryCompare(mouseUpperClickedSpy, "count", 2) + compare(mouseLowerClickedSpy.count, 2) + compare(mouseOverlapperClickedSpy.count, 1) + mouseClick(map, 55, 75) + tryCompare(mouseUpperClickedSpy, "count", 2) + compare(mouseLowerClickedSpy.count, 2) + compare(mouseOverlapperClickedSpy.count, 2) + real_click(map, 55, 25) + tryCompare(mouseUpperClickedSpy, "count", 2) + compare(mouseLowerClickedSpy.count, 2) + compare(mouseOverlapperClickedSpy.count, 3) + real_click(map, 55, 75) + tryCompare(mouseUpperClickedSpy, "count", 2) + compare(mouseLowerClickedSpy.count, 2) + compare(mouseOverlapperClickedSpy.count, 4) + } + + function test_basic_double_click() { + clear_data(); + wait(500); + real_double_click(map, 5, 5) + + compare(mouseUpperDoubleClickedSpy.count, 0) + compare(mouseLowerDoubleClickedSpy.count, 0) + compare(mouseOverlapperDoubleClickedSpy.count, 0) + real_double_click(map, 5, 25) + tryCompare(mouseUpper, "lastAccepted", true) + compare(mouseUpper.lastButton, Qt.LeftButton) + compare(mouseUpper.lastModifiers, Qt.NoModifier) + compare(mouseUpper.lastWasHeld, false) + compare(mouseUpper.lastX, 5) + compare(mouseUpper.lastY, 5) // remember 20 offset of the mouse area + + compare(mouseUpperDoubleClickedSpy.count, 1) + compare(mouseLowerDoubleClickedSpy.count, 0) + compare(mouseOverlapperDoubleClickedSpy.count, 0) + real_double_click(map, 5, 25) + tryCompare(mouseUpperDoubleClickedSpy, "count", 2) + compare(mouseLowerDoubleClickedSpy.count, 0) + compare(mouseOverlapperDoubleClickedSpy.count, 0) + real_double_click(map, 5, 55) + tryCompare(mouseUpperDoubleClickedSpy, "count", 2) + compare(mouseLowerDoubleClickedSpy.count, 1) + compare(mouseOverlapperDoubleClickedSpy.count, 0) + real_double_click(map, 5, 55) + tryCompare(mouseUpperDoubleClickedSpy, "count", 2) + compare(mouseLowerDoubleClickedSpy.count, 2) + compare(mouseOverlapperDoubleClickedSpy.count, 0) + // declaration order counts on overlap case; overlapping area declared later will get the events + real_double_click(map, 55, 25) + tryCompare(mouseUpperDoubleClickedSpy, "count", 2) + compare(mouseLowerDoubleClickedSpy.count, 2) + compare(mouseOverlapperDoubleClickedSpy.count, 1) + compare(mouseOverlapperPressedSpy.count, 2) + compare(mouseOverlapperReleasedSpy.count, 2) + real_double_click(map, 55, 75) + tryCompare(mouseUpperDoubleClickedSpy, "count", 2) + compare(mouseLowerDoubleClickedSpy.count, 2) + compare(mouseOverlapperDoubleClickedSpy.count, 2) + compare(mouseOverlapperPressedSpy.count, 4) + compare(mouseOverlapperReleasedSpy.count, 4) + // disable overlapping area and check event is delivered to the ones beneath + mouseOverlapper.enabled = false + real_double_click(map, 55, 25) + tryCompare(mouseUpperDoubleClickedSpy, "count", 3) + compare(mouseLowerDoubleClickedSpy.count, 2) + compare(mouseOverlapperDoubleClickedSpy.count, 2) + real_double_click(map, 55, 75) + tryCompare(mouseUpperDoubleClickedSpy, "count", 3) + compare(mouseLowerDoubleClickedSpy.count, 3) + compare(mouseOverlapperDoubleClickedSpy.count, 2) + mouseOverlapper.enabled = true + real_double_click(map, 55, 25) + tryCompare(mouseUpperDoubleClickedSpy, "count", 3) + compare(mouseLowerDoubleClickedSpy.count, 3) + compare(mouseOverlapperDoubleClickedSpy.count, 3) + real_double_click(map, 55, 75) + tryCompare(mouseUpperDoubleClickedSpy, "count", 3) + compare(mouseLowerDoubleClickedSpy.count, 3) + compare(mouseOverlapperDoubleClickedSpy.count, 4) + } + + function test_release_does_not_block_clicked() { // QTBUG-66534 + clear_data() + mousePress(map, 55, 75) + compare(mouseOverlapperPressedSpy.count, 1) + mouseRelease(map, 55, 25) + compare(mouseOverlapperReleasedSpy.count, 1) + mouseClick(map, 25, 25) + compare(mouseUpperClickedSpy.count, 1) + } + + function test_zzz_basic_press_and_hold() { // _zzz_ to ensure execution last (takes time) + clear_data(); + wait(1000); + real_press_and_hold(map, 5, 5) + compare(mouseUpperPressAndHoldSpy.count, 0) + compare(mouseLowerPressAndHoldSpy.count, 0) + compare(mouseOverlapperPressAndHoldSpy.count, 0) + + mousePress(map,5,25) + wait(1000) // threshold is 800 ms + compare(mouseUpperPressAndHoldSpy.count, 1) + compare(mouseLowerPressAndHoldSpy.count, 0) + compare(mouseOverlapperPressAndHoldSpy.count, 0) + compare(mouseUpper.lastAccepted, true) + compare(mouseUpper.lastButton, Qt.LeftButton) + compare(mouseUpper.lastModifiers, Qt.NoModifier) + compare(mouseUpper.lastWasHeld, true) // notable part + compare(mouseUpper.lastX, 5) + compare(mouseUpper.lastY, 5) // remember 20 offset of the mouse area + mouseRelease(map,5,25) + real_press_and_hold(map, 5, 55) + tryCompare(mouseUpperPressAndHoldSpy, "count", 1) + compare(mouseLowerPressAndHoldSpy.count, 1) + compare(mouseOverlapperPressAndHoldSpy.count, 0) + real_press_and_hold(map, 55, 75) + tryCompare(mouseUpperPressAndHoldSpy, "count", 1) + compare(mouseLowerPressAndHoldSpy.count, 1) + compare(mouseOverlapperPressAndHoldSpy.count, 1) + compare(mouseOverlapper.lastAccepted, true) + compare(mouseOverlapper.lastButton, Qt.LeftButton) + compare(mouseOverlapper.lastModifiers, Qt.NoModifier) + compare(mouseOverlapper.lastWasHeld, true) + compare(mouseOverlapper.lastX, 5) + compare(mouseOverlapper.lastY, 75) + // make sure that the wasHeld is cleared + mouseClick(map, 55, 75) + tryCompare(mouseOverlapper, "lastAccepted", true) + compare(mouseOverlapper.lastButton, Qt.LeftButton) + compare(mouseOverlapper.lastModifiers, Qt.NoModifier) + compare(mouseOverlapper.lastWasHeld, false) + compare(mouseOverlapper.lastX, 5) + compare(mouseOverlapper.lastY, 75) + real_press_and_hold(map, 55, 25) + tryCompare(mouseUpperPressAndHoldSpy, "count", 1) + compare(mouseLowerPressAndHoldSpy.count, 1) + compare(mouseOverlapperPressAndHoldSpy.count, 2) + } + } +} diff --git a/tests/auto/declarative_ui/tst_map_pinch.qml.QTBUG-47970 b/tests/auto/declarative_ui/tst_map_pinch.qml.QTBUG-47970 new file mode 100644 index 0000000..6d913ea --- /dev/null +++ b/tests/auto/declarative_ui/tst_map_pinch.qml.QTBUG-47970 @@ -0,0 +1,576 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.5 +import QtTest 1.0 +import QtLocation 5.5 +import QtPositioning 5.5 +import QtLocation.Test 5.5 + +Item { + // General-purpose elements for the test: + id: page + width: 100 + height: 100 + Plugin { id: testPlugin; name: "qmlgeo.test.plugin"; allowExperimental: true } + + property variant coordinate: QtPositioning.coordinate(10, 11) + + // From QtLocationTest plugin + PinchGenerator { + id: pinchGenerator + anchors.fill: parent + target: page + enabled: false + } + + MouseArea { + id: mouseAreaBottom + anchors.fill: parent + visible: false + } + + Map { + id: map + plugin: testPlugin + center: coordinate; + zoomLevel: 9; + anchors.fill: page + x:0; y:0 + property variant lastPinchEvent: null + property point startPinchPoint1: Qt.point(0,0) + property point startPinchPoint2: Qt.point(0,0) + property point endPinchPoint1: Qt.point(0,0) + property point endPinchPoint2: Qt.point(0,0) + property bool rejectPinch: false + gesture.onPinchStarted: { + map.lastPinchEvent = pinch; + map.startPinchPoint1= pinch.point1; + map.startPinchPoint2= pinch.point2; + if (rejectPinch) + pinch.accepted = false; + } + gesture.onPinchUpdated: map.lastPinchEvent = pinch; + + gesture.onPinchFinished: { + map.lastPinchEvent = pinch; + map.endPinchPoint1 = pinch.point1; + map.endPinchPoint2 = pinch.point2; + } + MouseArea { + id: mouseAreaTop + anchors.fill: parent + visible: false + } + } + + SignalSpy {id: centerSpy; target: map; signalName: 'centerChanged'} + SignalSpy {id: pinchStartedSpy; target: map.gesture; signalName: 'pinchStarted'} + SignalSpy {id: pinchUpdatedSpy; target: map.gesture; signalName: 'pinchUpdated'} + SignalSpy {id: pinchFinishedSpy; target: map.gesture; signalName: 'pinchFinished'} + SignalSpy {id: pinchMaximumZoomLevelChangeSpy; target: map.gesture; signalName: 'maximumZoomLevelChangeChanged'} + SignalSpy {id: gestureEnabledSpy; target: map.gesture; signalName: 'enabledChanged'} + SignalSpy {id: pinchActiveSpy; target: map.gesture; signalName: 'pinchActiveChanged'} + SignalSpy {id: pinchActiveGesturesSpy; target: map.gesture; signalName: 'activeGesturesChanged'} + SignalSpy {id: mapZoomLevelSpy; target: map; signalName: 'zoomLevelChanged'} + SignalSpy {id: mouseAreaTopSpy; target: mouseAreaTop; signalName: 'onPressed'} + SignalSpy {id: mouseAreaBottomSpy; target: mouseAreaBottom; signalName: 'onPressed'} + + TestCase { + when: windowShown && map.mapReady + name: "MapPinch" + + function init() + { + map.gesture.activeGestures = MapGestureArea.ZoomGesture + map.gesture.enabled = true + map.rejectPinch = false + map.center = coordinate + map.minimumZoomLevel = 0 + map.maximumZoomLevel = 20 + mouseRelease(mouseAreaTop,0,0) //Fixme: mouse area state gets broken across the tests + mouseAreaBottom.visible = false + mouseAreaTop.visible = false + pinchGenerator.clear() + centerSpy.clear() + pinchStartedSpy.clear() + pinchUpdatedSpy.clear() + pinchFinishedSpy.clear() + pinchMaximumZoomLevelChangeSpy.clear() + gestureEnabledSpy.clear() + pinchActiveSpy.clear() + pinchActiveGesturesSpy.clear() + mapZoomLevelSpy.clear() + mouseAreaTopSpy.clear() + mouseAreaBottomSpy.clear() + } + + //see QDeclarativeGeoMapGestureArea::updatePinch() + function calculateZoom(startPinchPoint1,startPinchPoint2,endPinchPoint1,endPinchPoint2, + width,height, maximumZoomLevelChange, startZoomLevel) + { + var startDistance = Math.sqrt(Math.pow(startPinchPoint2.x - startPinchPoint1.x,2) + + + Math.pow(startPinchPoint2.y - startPinchPoint1.y,2)) + var endDistance = Math.sqrt(Math.pow(endPinchPoint2.x - endPinchPoint1.x,2) + + + Math.pow(endPinchPoint2.y - endPinchPoint1.y,2)) + return 2 * (endDistance - startDistance) * maximumZoomLevelChange / + (width + height) + startZoomLevel; + } + + function initTestCase() + { + //test default properties + compare(map.gesture.enabled, true) + map.gesture.enabled = false + compare(gestureEnabledSpy.count, 1) + compare(map.gesture.enabled, false) + map.gesture.enabled = false + compare(gestureEnabledSpy.count, 1) + compare(map.gesture.enabled, false) + map.gesture.enabled = true + compare(gestureEnabledSpy.count, 2) + compare(map.gesture.enabled, true) + compare(map.gesture.isPinchActive, false) + verify(map.gesture.activeGestures & MapGestureArea.ZoomGesture) + map.gesture.activeGestures = MapGestureArea.NoGesture + compare(map.gesture.activeGestures, MapGestureArea.NoGesture) + compare(pinchActiveGesturesSpy.count, 1) + map.gesture.activeGestures = MapGestureArea.NoGesture + compare(map.gesture.activeGestures, MapGestureArea.NoGesture) + compare(pinchActiveGesturesSpy.count, 1) + map.gesture.activeGestures = MapGestureArea.ZoomGesture | MapGestureArea.PanGesture + compare(map.gesture.activeGestures, MapGestureArea.ZoomGesture | MapGestureArea.PanGesture) + compare(pinchActiveGesturesSpy.count, 2) + map.gesture.activeGestures = MapGestureArea.PanGesture + compare(map.gesture.activeGestures, MapGestureArea.PanGesture) + compare(pinchActiveGesturesSpy.count, 3) + map.gesture.activeGestures = MapGestureArea.ZoomGesture + compare(map.gesture.activeGestures, MapGestureArea.ZoomGesture) + compare(pinchActiveGesturesSpy.count, 4) + compare(map.gesture.maximumZoomLevelChange, 4) + map.gesture.maximumZoomLevelChange = 8 + compare(pinchMaximumZoomLevelChangeSpy.count, 1) + compare (map.gesture.maximumZoomLevelChange, 8) + map.gesture.maximumZoomLevelChange = 8 + compare(pinchMaximumZoomLevelChangeSpy.count, 1) + compare (map.gesture.maximumZoomLevelChange, 8) + map.gesture.maximumZoomLevelChange = 11 // too big + map.gesture.maximumZoomLevelChange = 0.01 // too small + map.gesture.maximumZoomLevelChange = -1 // too small + compare(pinchMaximumZoomLevelChangeSpy.count, 1) + compare (map.gesture.maximumZoomLevelChange, 8) + map.gesture.maximumZoomLevelChange = 2 + compare(pinchMaximumZoomLevelChangeSpy.count, 2) + compare (map.gesture.maximumZoomLevelChange, 2) + } + + + function zoom_in() + { + var startZoomLevel = 9 + map.zoomLevel = startZoomLevel + mapZoomLevelSpy.clear() + map.gesture.maximumZoomLevelChange = 2 + + compare(map.gesture.isPinchActive, false) + pinchGenerator.pinch( + Qt.point(0,50), // point1From + Qt.point(50,50), // point1To + Qt.point(100,50), // point2From + Qt.point(50,50), // point2To + 40, // interval between touch events (swipe1), default 20ms + 40, // interval between touch events (swipe2), default 20ms + 10, // number of touchevents in point1from -> point1to, default 10 + 10); // number of touchevents in point2from -> point2to, default 10 + tryCompare(pinchStartedSpy, "count", 1); + // check the pinch event data for pinchStarted + compare(map.lastPinchEvent.center.x, 50) + compare(map.lastPinchEvent.center.y, 50) + compare(map.lastPinchEvent.angle, 0) + verify((map.lastPinchEvent.point1.x > pinchGenerator.startDragDistance()) + && (map.lastPinchEvent.point1.x < 25)) + compare(map.lastPinchEvent.point1.y, 50) + verify((map.lastPinchEvent.point2.x > 75) + && (map.lastPinchEvent.point2.x < 100 - pinchGenerator.startDragDistance())) + compare(map.lastPinchEvent.point2.y, 50) + compare(map.lastPinchEvent.accepted, true) + compare(map.lastPinchEvent.pointCount, 2) + tryCompare(pinchActiveSpy, "count", 2) // check that pinch is active + compare(map.gesture.isPinchActive, true) + wait(200) // five points, each 40ms + // check the pinch event data for pinchUpdated + compare(map.lastPinchEvent.center.x, 50) + compare(map.lastPinchEvent.center.y, 50) + compare(map.lastPinchEvent.angle, 0) + verify((map.lastPinchEvent.point1.x) > 25 && (map.lastPinchEvent.point1.x <= 50)) + compare(map.lastPinchEvent.point1.y, 50) + verify((map.lastPinchEvent.point2.x) >= 50 && (map.lastPinchEvent.point2.x < 85)) + compare(map.lastPinchEvent.point2.y, 50) + compare(map.lastPinchEvent.accepted, true) + compare(map.lastPinchEvent.pointCount, 2) + tryCompare(pinchFinishedSpy, "count", 1); + compare(map.gesture.isPinchActive, false) + // check the pinch event data for pinchFinished + compare(map.lastPinchEvent.center.x, 50) + compare(map.lastPinchEvent.center.y, 50) + compare(map.lastPinchEvent.angle, 0) + verify((map.lastPinchEvent.point1.x) > 35 && (map.lastPinchEvent.point1.x <= 50)) + compare(map.lastPinchEvent.point1.y, 50) + verify((map.lastPinchEvent.point2.x) >= 50 && (map.lastPinchEvent.point2.x < 65)) + compare(map.lastPinchEvent.point2.y, 50) + compare(map.lastPinchEvent.accepted, true) + compare(map.lastPinchEvent.pointCount, 0) + + verify(pinchUpdatedSpy.count >= 5); // verify 'sane' number of updates received + compare(pinchActiveSpy.count,3) + compare(map.gesture.isPinchActive, false) + compare(mapZoomLevelSpy.count, pinchUpdatedSpy.count) + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + compare(map.zoomLevel, endZoomLevel) + } + + function test_zoom_in() + { + zoom_in() + } + + function test_zoom_in_with_top_filtering() + { + mouseAreaTop.visible = true + zoom_in() + tryCompare(mouseAreaTopSpy, "count", 1) + } + + function test_zoom_in_with_below_filtering() + { + mouseAreaBottom.visible=true + zoom_in() + tryCompare(mouseAreaBottomSpy, "count",0) + } + + function zoom_out() + { + var startZoomLevel = 7.8 + map.zoomLevel = startZoomLevel + map.gesture.maximumZoomLevelChange = 2 + compare (map.gesture.maximumZoomLevelChange, 2) + mapZoomLevelSpy.clear() + pinchGenerator.pinch(Qt.point(45,50), Qt.point(0,50), + Qt.point(55,50), Qt.point(100,50), + 40, 40, 10, 10); + tryCompare(pinchStartedSpy, "count", 1); + tryCompare(pinchFinishedSpy, "count", 1); + compare(map.gesture.isPinchActive, false) + verify(pinchUpdatedSpy.count >= 5); // verify 'sane' number of updates received + compare(mapZoomLevelSpy.count, pinchUpdatedSpy.count) + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + + compare(map.zoomLevel, endZoomLevel) + } + + function test_zoom_out() + { + zoom_out() + } + + function test_zoom_out_with_top_filtering() + { + mouseAreaTop.visible=true + zoom_out() + tryCompare(mouseAreaTopSpy, "count", 1) + } + + function test_zoom_out_with_below_filtering() + { + mouseAreaBottom.visible=true + zoom_out() + tryCompare(mouseAreaBottomSpy, "count",0) + } + + function test_zoom_in_and_back_out() + { + // direction change during same pinch + var startZoomLevel = 7.8 + map.gesture.maximumZoomLevelChange = 2 + map.zoomLevel = startZoomLevel + pinchGenerator.pinch(Qt.point(0,50), Qt.point(100,50), + Qt.point(100,50),Qt.point(0,50), + 40, 40, 10, 10); + tryCompare(pinchStartedSpy, "count", 1); + tryCompare(pinchFinishedSpy, "count", 1); + verify(pinchUpdatedSpy.count >= 5); // verify 'sane' number of updates received + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + compare(map.zoomLevel, endZoomLevel) // should remain the same + } + + function test_zoom_in_with_different_change_level() + { + var startZoomLevel = 8 + map.zoomLevel = startZoomLevel + map.gesture.maximumZoomLevelChange = 4 + compare (map.gesture.maximumZoomLevelChange, 4) + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50), + Qt.point(100,50),Qt.point(50,50), + 40, 40, 10, 10); + tryCompare(pinchFinishedSpy, "count", 1); + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + compare(map.zoomLevel, endZoomLevel) + } + + function test_zoom_out_with_different_change_level() + { + var startZoomLevel = 8 + map.gesture.maximumZoomLevelChange = 1 + map.zoomLevel = startZoomLevel + compare (map.gesture.maximumZoomLevelChange, 1) + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50), + Qt.point(50,50), Qt.point(100,50), + 40, 40, 10, 10); + tryCompare(pinchFinishedSpy, "count", 1); + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + compare(map.zoomLevel, endZoomLevel) + } + + function test_zoom_in_below_minimum_zoom_level() + { + map.zoomLevel = 8 + map.gesture.maximumZoomLevelChange = 4 + map.minimumZoomLevel = 7 + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50),Qt.point(100,50),Qt.point(50,50)); + wait(250); + tryCompare(pinchFinishedSpy, "count", 1); + compare(map.zoomLevel, 7) + } + + function test_zoom_out_above_maximum_zoom_level() + { + map.gesture.maximumZoomLevelChange = 4 + map.maximumZoomLevel = 8 + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50),Qt.point(50,50), Qt.point(100,50)); + tryCompare(pinchFinishedSpy, "count", 1); + compare(map.zoomLevel, 8) + } + + function test_pinch_when_max_and_min_are_same() + { + map.maximumZoomLevel = 8 + map.minimumZoomLevel = 8 + compare(map.maximumZoomLevel, 8) + compare(map.minimumZoomLevel, 8) + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50),Qt.point(100,50),Qt.point(50,50)); + tryCompare(pinchFinishedSpy, "count", 1); + compare(map.zoomLevel, 8) + map.minimumZoomLevel = 1 + map.maximumZoomLevel = 20 + } + + function test_pinch_when_max_min_is_not_where_map_zoomLevel_currently_is() + { + map.gesture.maximumZoomLevelChange = 4 + map.minimumZoomLevel = 4 + map.maximumZoomLevel = 6 + // first when above the zoom range + map.zoomLevel = 5 + pinchGenerator.pinch(Qt.point(50,50),Qt.point(0,50),Qt.point(50,50),Qt.point(100,50)); // zoom out + tryCompare(pinchFinishedSpy, "count", 1); + compare(map.zoomLevel, 6) + map.zoomLevel = 5 + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50),Qt.point(100,50),Qt.point(50,50)); // zoom in + tryCompare(pinchFinishedSpy, "count", 2); + compare(map.zoomLevel, 4) + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50),Qt.point(100,50),Qt.point(50,50)); // zoom in + tryCompare(pinchFinishedSpy, "count", 3); + compare(map.zoomLevel, 4) + map.minimumZoomLevel = 1 + map.maximumZoomLevel = 20 + } + + function test_pinch_while_pinch_area_is_disabled() + { + map.zoomLevel = 7.5 + map.gesture.enabled = false + map.gesture.maximumZoomLevelChange = 2 + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50), + Qt.point(50,50), Qt.point(100,50), + 40, 40, 10, 10); + wait(200); + compare(pinchActiveSpy.count, 0) + compare(map.gesture.isPinchActive, false) + compare(pinchStartedSpy.count, 0) + compare(pinchUpdatedSpy.count, 0); + compare(pinchFinishedSpy.count, 0); + compare(map.zoomLevel, 7.5) + pinchGenerator.stop() + } + + function test_pinch_disabling_during_pinching() + { + var startZoomLevel = 7.5 + map.zoomLevel = startZoomLevel + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50), + Qt.point(50,50), Qt.point(100,50), + 40, 40, 10, 10); + tryCompare(pinchStartedSpy, "count", 1); + // check that pinch is active. then disable the pinch. pinch area should still process + // as long as it is active + compare(pinchActiveSpy.count,2) + compare(map.gesture.isPinchActive, true) + map.gesture.enabled = false + tryCompare(pinchFinishedSpy, "count", 1) + var pinchupdates = pinchUpdatedSpy.count + verify(pinchupdates > 0) + compare(pinchActiveSpy.count,3) + compare(map.gesture.isPinchActive, false) + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + compare(map.zoomLevel, endZoomLevel) + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50), + Qt.point(50,50), Qt.point(100,50), + 40, 40, 10, 10); + compare(map.zoomLevel, endZoomLevel) + } + + function test_check_no_active_gestures() + { + map.zoomLevel = 8.5 + map.gesture.activeGestures = MapGestureArea.NoGesture + + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50), + Qt.point(50,50), Qt.point(100,50), + 40, 40, 10, 10); + tryCompare(pinchStartedSpy, "count", 0); + wait(300); + compare(pinchUpdatedSpy.count, 0); + compare(pinchStartedSpy.count, 0); + compare(map.zoomLevel, 8.5) + pinchGenerator.stop() + } + + function test_changing_zoom_level_during_active_pinch_zoom() + { + var startZoomLevel = 8.5 + map.zoomLevel = startZoomLevel + map.gesture.maximumZoomLevelChange = 2 + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50), + Qt.point(50,50), Qt.point(100,50), + 40, 40, 10, 10); + tryCompare(pinchStartedSpy, "count", 1); + tryCompare(pinchActiveSpy, "count", 2) + compare(map.gesture.isPinchActive, true) + map.zoomLevel = 3 // will get overridden by pinch + tryCompare(pinchFinishedSpy, "count", 1); + verify(pinchUpdatedSpy.count >= 5); // verify 'sane' number of updates received + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + compare(map.zoomLevel, endZoomLevel) + } + + function test_zoom_below_and_above_plugin_support() + { + map.gesture.maximumZoomLevelChange = 4 + map.zoomLevel = map.minimumZoomLevel + 0.5 + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50), + Qt.point(100,50),Qt.point(50,50), + 40, 40, 10, 10); + tryCompare(pinchFinishedSpy, "count", 1); + compare(map.zoomLevel, map.minimumZoomLevel) + map.zoomLevel = map.maximumZoomLevel - 0.5 + pinchGenerator.pinch(Qt.point(50,50), Qt.point(0,50),Qt.point(50,50), Qt.point(100,50)); + tryCompare(pinchFinishedSpy, "count", 2); + compare(map.zoomLevel, map.maximumZoomLevel) + } + + function test_check_pinch_accepted() + { + map.zoomLevel = 10 + map.rejectPinch = true + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50), + Qt.point(100,50),Qt.point(50,50), + 40, 40, 10, 10); + wait(300) + compare(pinchUpdatedSpy.count, 0) + compare(pinchFinishedSpy.count, 0) + compare(map.gesture.isPinchActive, false) + compare(map.zoomLevel, 10) + var startZoomLevel = 10 + map.rejectPinch = false + wait(500) + pinchGenerator.pinch(Qt.point(0,50),Qt.point(50,50),Qt.point(100,50),Qt.point(50,50),40, 40, 10, 10); + tryCompare(pinchFinishedSpy, "count", 1) + var endZoomLevel = calculateZoom( map.startPinchPoint1, map.startPinchPoint2, + map.endPinchPoint1, map.endPinchPoint2, + map.width,map.height, + map.gesture.maximumZoomLevelChange,startZoomLevel) + compare(map.zoomLevel, endZoomLevel) + compare(map.lastPinchEvent.accepted, true) + } + + function test_moving_center() + { + pinchGenerator.pinch(Qt.point(0, 50), Qt.point(50,100), Qt.point(50,0), Qt.point(100, 50)) + tryCompare(pinchStartedSpy, "count", 1) + compare(map.lastPinchEvent.center.x, (map.lastPinchEvent.point1.x + map.lastPinchEvent.point2.x) /2) + compare(map.lastPinchEvent.center.x, (map.lastPinchEvent.point1.y + map.lastPinchEvent.point2.y) /2) + tryCompare(pinchFinishedSpy, "count", 1) + compare(map.lastPinchEvent.center.x, (map.lastPinchEvent.point1.x + map.lastPinchEvent.point2.x) /2) + compare(map.lastPinchEvent.center.x, (map.lastPinchEvent.point1.y + map.lastPinchEvent.point2.y) /2) + // sanity check that we are not comparing wrong (points) with wrong (center) and calling it a success + verify((map.lastPinchEvent.center.x > 50) && (map.lastPinchEvent.center.x < 100)) + verify((map.lastPinchEvent.center.y > 50) && (map.lastPinchEvent.center.y < 100)) + } + + function test_angle_between_points() + { + // todo calculate the angle from points for comparison + pinchGenerator.pinch(Qt.point(0,0), Qt.point(0,100), Qt.point(100,100), Qt.point(100,0)) + tryCompare(pinchStartedSpy, "count", 1) + verify(map.lastPinchEvent.angle >= -45 && map.lastPinchEvent.angle < -20) + tryCompare(pinchFinishedSpy, "count", 1) + verify(map.lastPinchEvent.angle >= 20 && map.lastPinchEvent.angle <= 45) + } + } +} diff --git a/tests/auto/doublevectors/doublevectors.pro b/tests/auto/doublevectors/doublevectors.pro new file mode 100644 index 0000000..841a19e --- /dev/null +++ b/tests/auto/doublevectors/doublevectors.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_doublevectors + +SOURCES += tst_doublevectors.cpp + +QT += positioning-private testlib diff --git a/tests/auto/doublevectors/tst_doublevectors.cpp b/tests/auto/doublevectors/tst_doublevectors.cpp new file mode 100644 index 0000000..ae1b2ba --- /dev/null +++ b/tests/auto/doublevectors/tst_doublevectors.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include + +QT_USE_NAMESPACE + +class tst_doubleVectors : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + // 2D + void constructor2dTest(); + void basicFunctions2dTest(); + void unaryOperator2dTest(); + void binaryOperator2dTest(); + + // 3D + void constructor3dTest(); + void basicFunctions3dTest(); + void unaryOperator3dTest(); + void binaryOperator3dTest(); +}; + +// DoubleVector2D + +void tst_doubleVectors::constructor2dTest() +{ + // empty constructor, since it sets to 0, we should check, in case people rely on it + QDoubleVector2D v1; + QCOMPARE(v1.x(), 0.0); + QCOMPARE(v1.y(), 0.0); + QCOMPARE(v1.isNull(), true); + v1 = QDoubleVector2D(1.1, -2.5); // assignment and constructor + QCOMPARE(v1.x(), 1.1); + QCOMPARE(v1.y(), -2.5); + QDoubleVector2D v2(v1); // copy constructor + QCOMPARE(v2.x(), 1.1); + QCOMPARE(v2.y(), -2.5); + const QDoubleVector3D v3d(2.2, 3.3, 4.4); + QDoubleVector2D v3(v3d); // constructor from 3d vector, just copies x and y + QCOMPARE(v3.x(), 2.2); + QCOMPARE(v3.y(), 3.3); + QCOMPARE(v3.isNull(), false); +} + +void tst_doubleVectors::basicFunctions2dTest() +{ + QDoubleVector2D v1; + v1.setX(3.0); + v1.setY(4.0); + QCOMPARE(v1.x(), 3.0); + QCOMPARE(v1.y(), 4.0); + QCOMPARE(v1.length(), 5.0); + QDoubleVector2D v2 = v1.normalized(); + QCOMPARE(v1.lengthSquared(), 25.0); + v1.normalize(); + QCOMPARE(v1.x(), 3.0/5.0); + QCOMPARE(v1.y(), 4.0/5.0); + QCOMPARE(v2.x(), 3.0/5.0); + QCOMPARE(v2.y(), 4.0/5.0); + + QDoubleVector3D v3d = v1.toVector3D(); + QCOMPARE(v3d.x(), 3.0/5.0); + QCOMPARE(v3d.y(), 4.0/5.0); + QCOMPARE(v3d.z(), 0.0); +} + +void tst_doubleVectors::unaryOperator2dTest() +{ + QDoubleVector2D v1(1.1, 2.2); + QDoubleVector2D v2 = -v1; + QCOMPARE(v2.x(), -1.1); + QCOMPARE(v2.y(), -2.2); + + v1 *= 2.0; + QCOMPARE(v1.x(), 2.2); + QCOMPARE(v1.y(), 4.4); + + v2 /= 2.0; + QCOMPARE(v2.x(), -0.55); + QCOMPARE(v2.y(), -1.1); + + v1 += v2; + QCOMPARE(v1.x(), 1.65); + QCOMPARE(v1.y(), 3.3); + + v1 -= v2; + QCOMPARE(v1.x(), 2.2); + QCOMPARE(v1.y(), 4.4); + + v1 *= v2; + QCOMPARE(v1.x(), -1.21); + QCOMPARE(v1.y(), -4.84); +} + +void tst_doubleVectors::binaryOperator2dTest() +{ + QDoubleVector2D v1(1.1, 2.2); + QDoubleVector2D v2(3.4, 4.4); + QDoubleVector2D v3 = v1 + v2; + QCOMPARE(v3.x(), 4.5); + QCOMPARE(v3.y(), 6.6); + + QDoubleVector2D v4 = v1 - v2; + QCOMPARE(v4.x(), -2.3); + QCOMPARE(v4.y(), -2.2); + + QDoubleVector2D v5 = v2 * 2; + QCOMPARE(v5.x(), 6.8); + QCOMPARE(v5.y(), 8.8); + + QDoubleVector2D v6 = 2 * v2; + QCOMPARE(v6.x(), 6.8); + QCOMPARE(v6.y(), 8.8); + + QDoubleVector2D v7 = v2 / 2; + QCOMPARE(v7.x(), 1.7); + QCOMPARE(v7.y(), 2.2); + + double d = QDoubleVector2D::dotProduct(v1, v2); + QCOMPARE(d, 13.42); + + QCOMPARE(v5 == v6, true); + QCOMPARE(v5 != v6, false); + QCOMPARE(v6 == v7, false); + QCOMPARE(v6 != v7, true); +} + + + +// DoubleVector3D + + +void tst_doubleVectors::constructor3dTest() +{ + // empty constructor, since it sets to 0, we should check, in case people rely on it + QDoubleVector3D v1; + QCOMPARE(v1.x(), 0.0); + QCOMPARE(v1.y(), 0.0); + QCOMPARE(v1.z(), 0.0); + QCOMPARE(v1.isNull(), true); + v1 = QDoubleVector3D(1.1, -2.5, 3.2); // assignment and constructor + QCOMPARE(v1.x(), 1.1); + QCOMPARE(v1.y(), -2.5); + QCOMPARE(v1.z(), 3.2); + QDoubleVector3D v2(v1); // copy constructor + QCOMPARE(v2.x(), 1.1); + QCOMPARE(v2.y(), -2.5); + QCOMPARE(v2.z(), 3.2); + const QDoubleVector2D v2d(2.2, 3.3); + QDoubleVector3D v3(v2d); // constructor from 3d vector, just copies x and y + QCOMPARE(v3.x(), 2.2); + QCOMPARE(v3.y(), 3.3); + QCOMPARE(v3.z(), 0.0); + QCOMPARE(v3.isNull(), false); + const QDoubleVector2D v2d2(2.2, 3.3); + QDoubleVector3D v4(v2d2, -13.6); // constructor from 2d vector + QCOMPARE(v4.x(), 2.2); + QCOMPARE(v4.y(), 3.3); + QCOMPARE(v4.z(), -13.6); +} + +void tst_doubleVectors::basicFunctions3dTest() +{ + QDoubleVector3D v1; + v1.setX(2.0); + v1.setY(3.0); + v1.setZ(6.0); + QCOMPARE(v1.x(), 2.0); + QCOMPARE(v1.y(), 3.0); + QCOMPARE(v1.z(), 6.0); + QCOMPARE(v1.length(), 7.0); + QDoubleVector3D v2 = v1.normalized(); + QCOMPARE(v1.lengthSquared(), 49.0); + v1.normalize(); + QCOMPARE(v1.x(), 2.0/7.0); + QCOMPARE(v1.y(), 3.0/7.0); + QCOMPARE(v1.z(), 6.0/7.0); + QCOMPARE(v2.x(), 2.0/7.0); + QCOMPARE(v2.y(), 3.0/7.0); + QCOMPARE(v2.z(), 6.0/7.0); + + QDoubleVector2D v2d = v1.toVector2D(); + QCOMPARE(v2d.x(), 2.0/7.0); + QCOMPARE(v2d.y(), 3.0/7.0); +} + +void tst_doubleVectors::unaryOperator3dTest() +{ + QDoubleVector3D v1(1.1, 2.2, 3.3); + QDoubleVector3D v2 = -v1; + QCOMPARE(v2.x(), -1.1); + QCOMPARE(v2.y(), -2.2); + QCOMPARE(v2.z(), -3.3); + + v1 *= 2.0; + QCOMPARE(v1.x(), 2.2); + QCOMPARE(v1.y(), 4.4); + QCOMPARE(v1.z(), 6.6); + + v2 /= 2.0; + QCOMPARE(v2.x(), -0.55); + QCOMPARE(v2.y(), -1.1); + QCOMPARE(v2.z(), -1.65); + + v1 += v2; + QCOMPARE(v1.x(), 1.65); + QCOMPARE(v1.y(), 3.3); + QCOMPARE(v1.z(), 4.95); + + v1 -= v2; + QCOMPARE(v1.x(), 2.2); + QCOMPARE(v1.y(), 4.4); + QCOMPARE(v1.z(), 6.6); + + v1 *= v2; + QCOMPARE(v1.x(), -1.21); + QCOMPARE(v1.y(), -4.84); + QCOMPARE(v1.z(), -10.89); +} + +void tst_doubleVectors::binaryOperator3dTest() +{ + QDoubleVector3D v1(1.1, 2.2, 3.3); + QDoubleVector3D v2(3.4, 4.4, 5.5); + QDoubleVector3D v3 = v1 + v2; + QCOMPARE(v3.x(), 4.5); + QCOMPARE(v3.y(), 6.6); + QCOMPARE(v3.z(), 8.8); + + QDoubleVector3D v4 = v1 - v2; + QCOMPARE(v4.x(), -2.3); + QCOMPARE(v4.y(), -2.2); + QCOMPARE(v4.z(), -2.2); + + QDoubleVector3D v5 = v2 * 2; + QCOMPARE(v5.x(), 6.8); + QCOMPARE(v5.y(), 8.8); + QCOMPARE(v5.z(), 11.0); + + QDoubleVector3D v6 = 2 * v2; + QCOMPARE(v6.x(), 6.8); + QCOMPARE(v6.y(), 8.8); + QCOMPARE(v6.z(), 11.0); + + QDoubleVector3D v7 = v2 / 2; + QCOMPARE(v7.x(), 1.7); + QCOMPARE(v7.y(), 2.2); + QCOMPARE(v7.z(), 2.75); + + double d = QDoubleVector3D::dotProduct(v1, v2); + QCOMPARE(d, 31.57); + + QCOMPARE(v5 == v6, true); + QCOMPARE(v5 != v6, false); + QCOMPARE(v6 == v7, false); + QCOMPARE(v6 != v7, true); +} + +QTEST_APPLESS_MAIN(tst_doubleVectors) + +#include "tst_doublevectors.moc" diff --git a/tests/auto/geotestplugin/geotestplugin.json b/tests/auto/geotestplugin/geotestplugin.json new file mode 100644 index 0000000..5272171 --- /dev/null +++ b/tests/auto/geotestplugin/geotestplugin.json @@ -0,0 +1,19 @@ +{ + "Keys": ["qmlgeo.test.plugin"], + "Provider": "qmlgeo.test.plugin", + "Version": 100, + "Experimental": true, + "Features": [ + "OfflineMappingFeature", + "OfflineRoutingFeature", + "AlternativeRoutesFeature", + "ExcludeAreasRoutingFeature", + "RouteUpdatesFeature", + "OfflineGeocodingFeature", + "ReverseGeocodingFeature", + "OfflinePlacesFeature", + "SavePlaceFeature", + "SaveCategoryFeature", + "SearchSuggestionsFeature" + ] +} diff --git a/tests/auto/geotestplugin/geotestplugin.pro b/tests/auto/geotestplugin/geotestplugin.pro new file mode 100644 index 0000000..f4fe25b --- /dev/null +++ b/tests/auto/geotestplugin/geotestplugin.pro @@ -0,0 +1,23 @@ +TARGET = qtgeoservices_qmltestplugin +QT += location-private positioning-private testlib + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = TestGeoServicePlugin +PLUGIN_EXTENDS = - +load(qt_plugin) + +HEADERS += qgeocodingmanagerengine_test.h \ + qgeoserviceproviderplugin_test.h \ + qgeoroutingmanagerengine_test.h \ + qplacemanagerengine_test.h \ + qgeotiledmappingmanagerengine_test.h \ + qgeotiledmap_test.h \ + qgeotilefetcher_test.h + +SOURCES += qgeoserviceproviderplugin_test.cpp \ + qgeotiledmap_test.cpp + +OTHER_FILES += \ + geotestplugin.json \ + place_data.json +RESOURCES += testdata.qrc diff --git a/tests/auto/geotestplugin/place_data.json b/tests/auto/geotestplugin/place_data.json new file mode 100644 index 0000000..3aafcbe --- /dev/null +++ b/tests/auto/geotestplugin/place_data.json @@ -0,0 +1,146 @@ +{ + "categories": [ + { + "name": "Accommodation", + "id": "4b79794f-e146-4adc-9bdf-68c06e7209fd" + }, + { + "name": "Hotel", + "id": "70ab5807-26d2-46be-a860-dc48f17133f0", + "parentId": "4b79794f-e146-4adc-9bdf-68c06e7209fd" + }, + { + "name": "Motel", + "id": "e0478f8a-fe8f-4bf9-8392-c1910e49223f", + "parentId": "4b79794f-e146-4adc-9bdf-68c06e7209fd" + }, + { + "name": "Camping", + "id": "b0434495-9429-4c9f-96e5-75f52f0b8dc8", + "parentId": "4b79794f-e146-4adc-9bdf-68c06e7209fd" + }, + { + "name": "Park", + "id": "c2e1252c-b997-44fc-8165-e53dd00f66a7" + } + ], + + "places": [ + { + "name": "Park View Hotel", + "id": "4dcc74ce-fdeb-443e-827c-367438017cf1", + "categories": [ "70ab5807-26d2-46be-a860-dc48f17133f0" ], + "location": { + "latitude": 0.1001, + "longitude": 0.1001 + }, + "recommendations": [ + "8f72057a-54b2-4e95-a7bb-97b4d2b5721e", + "dacb2181-3f67-4e6a-bd4d-635e99ad5b03" + ], + "reviews": [ + { + "title": "Park View Review 1", + "text": "Park View Review 1 Text", + "dateTime": "13:01 22-09-2004", + "language": "en", + "rating": 3.5, + "reviewId": "0001" + }, + { + "title": "Park View Review 2", + "text": "Park View Review 2 Text", + "dateTime": "04:17 14-09-2005", + "language": "en", + "rating": 1, + "reviewId": "0002" + }, + { + "title": "Park View Review 3", + "text": "Park View Review 3 Text", + "dateTime": "04:12 14-10-2005", + "language": "en", + "rating": 5, + "reviewId": "0003" + }, + { + }, + { + "title": "Park View Review 5", + "text": "Park View Review 5 Text", + "dateTime": "14:53 20-11-2005", + "language": "en", + "rating": 2.3, + "reviewId": "0005" + } + ], + "images": [ + { + "url": "http://somewhere.com/image1.png", + "imageId": "0001", + "mimeType": "image/png" + }, + { + "url": "http://somewhere.com/image2.png", + "imageId": "0002", + "mimeType": "image/png" + }, + { + "url": "http://somewhere.com/image3.png", + "imageId": "0003", + "mimeType": "image/png" + }, + { + }, + { + "url": "http://somewhere.com/image5.png", + "imageId": "0005", + "mimeType": "image/png" + } + ], + "editorials": [ + { + "title": "Editorial 1", + "text": "Editorial 1 Text", + "language": "en" + }, + { + "title": "Editorial 2", + "text": "Editorial 2 Text", + "language": "en" + }, + { + "title": "Editorial 3", + "text": "Editorial 3 Text", + "language": "en" + }, + { + }, + { + "title": "Editorial 5", + "text": "Editorial 5 Text", + "language": "en" + } + ] + }, + { + "name": "Sea View Hotel", + "id": "8f72057a-54b2-4e95-a7bb-97b4d2b5721e", + "categories": [ "70ab5807-26d2-46be-a860-dc48f17133f0" ], + "location": { + "latitude": 0.1002, + "longitude": 0.1002 + }, + "alternateImplementation": true + }, + { + "name": "Country Gardens", + "id": "dacb2181-3f67-4e6a-bd4d-635e99ad5b03", + "categories": [ "c2e1252c-b997-44fc-8165-e53dd00f66a7" ], + "location": { + "latitude": 0.1001, + "longitude": 0.1002 + } + } + ] +} diff --git a/tests/auto/geotestplugin/qgeocodingmanagerengine_test.h b/tests/auto/geotestplugin/qgeocodingmanagerengine_test.h new file mode 100644 index 0000000..ecbb60d --- /dev/null +++ b/tests/auto/geotestplugin/qgeocodingmanagerengine_test.h @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGERENGINE_TEST_H +#define QGEOCODINGMANAGERENGINE_TEST_H + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_USE_NAMESPACE + + +class GeocodeReplyTest :public QGeoCodeReply +{ + Q_OBJECT +public: + GeocodeReplyTest(QObject *parent = 0) : QGeoCodeReply (parent) {} + + void callAddLocation ( const QGeoLocation & location ) {addLocation(location);} + void callSetError ( Error error, const QString & errorString ) {setError(error, errorString);} + void callSetFinished ( bool finished ) {setFinished(finished);} + void callSetLimit ( int limit ) {setLimit(limit);} + void callSetOffset ( int offset ) {setOffset(offset);} + void callSetLocations ( const QList & locations ) {setLocations(locations);} + void callSetViewport ( const QGeoShape &viewport ) {setViewport(viewport);} +}; + +class QGeoCodingManagerEngineTest: public QGeoCodingManagerEngine + +{ +Q_OBJECT +public: + QGeoCodingManagerEngineTest(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) : + QGeoCodingManagerEngine(parameters), + validateWellKnownValues_(false), + finishRequestImmediately_(true), + supported_(true), + geocodeReply_(0), + timerId_(0), + errorCode_(QGeoCodeReply::NoError) + { + Q_UNUSED(error) + Q_UNUSED(errorString) + if (parameters.contains("supported")) + supported_ = qvariant_cast(parameters.value("supported")); + if (parameters.contains("finishRequestImmediately")) + finishRequestImmediately_ = qvariant_cast(parameters.value("finishRequestImmediately")); + if (parameters.contains("validateWellKnownValues")) + validateWellKnownValues_ = qvariant_cast(parameters.value("validateWellKnownValues")); + + setLocale(QLocale (QLocale::German, QLocale::Germany)); + } + + QGeoCodeReply* geocode(const QString &searchString, + int limit = -1, + int offset = 0, + const QGeoShape &bounds = QGeoShape()) + { + geocodeReply_ = new GeocodeReplyTest(); + connect(geocodeReply_, SIGNAL(aborted()), this, SLOT(requestAborted())); + geocodeReply_->callSetViewport(bounds); + + if (searchString.length() == 1) { + errorString_ = searchString; + errorCode_ = (QGeoCodeReply::Error)searchString.toInt(); + } else { + errorString_ = ""; + errorCode_ = QGeoCodeReply::NoError; + } + + if (errorCode_ == QGeoCodeReply::NoError) + setLocations(geocodeReply_, searchString, limit, offset); + + if (finishRequestImmediately_) { + // check if we should finish with error + if (errorCode_) { + geocodeReply_->callSetError(errorCode_, errorString_); + } else { + geocodeReply_->callSetFinished(true); + } + } else { + // we only allow serialized requests in QML - previous must have been aborted + Q_ASSERT(timerId_ == 0); + timerId_ = startTimer(200); + } + return static_cast(geocodeReply_); + } + + QGeoCodeReply* geocode(const QGeoAddress & address, const QGeoShape &bounds) + { + geocodeReply_ = new GeocodeReplyTest(); + connect(geocodeReply_, SIGNAL(aborted()), this, SLOT(requestAborted())); + geocodeReply_->callSetViewport(bounds); + + if (address.street().startsWith("error")) { + errorString_ = address.street(); + errorCode_ = (QGeoCodeReply::Error)address.county().toInt(); + } else { + errorString_ = ""; + errorCode_ = QGeoCodeReply::NoError; + } + // 1. Check if we are to validate values + if (validateWellKnownValues_) { + if (address.street() != "wellknown street") { + geocodeReply_->callSetError(QGeoCodeReply::EngineNotSetError, address.street()); + } else { + geocodeReply_->callSetError(QGeoCodeReply::NoError,address.street()); + } + } + + // 2. Set the locations into the reply + setLocations(geocodeReply_, address); + + // 3. Finish the request + if (finishRequestImmediately_) { + // check if we should finish with error + if (errorCode_) { + geocodeReply_->callSetError(errorCode_, errorString_); + } else { + geocodeReply_->callSetFinished(true); + } + } else { + // we only allow serialized requests in QML - previous must have been aborted + Q_ASSERT(timerId_ == 0); + timerId_ = startTimer(200); + } + return static_cast(geocodeReply_); + } + +public Q_SLOTS: + void requestAborted() + { + if (timerId_) { + killTimer(timerId_); + timerId_ = 0; + } + errorString_ = ""; + errorCode_ = QGeoCodeReply::NoError; + } + +public: + void setLocations(GeocodeReplyTest* reply, const QString searchString, int limit, int offset) + { + if (limit < 0) + limit = 0; + for (int i = 0; i < limit; ++i) { + QGeoLocation location; + QGeoAddress address; + address.setStreet(searchString); + address.setCounty(QString::number(offset)); + location.setAddress(address); + reply->callAddLocation(location); + } + } + + void setLocations(GeocodeReplyTest* reply, const QGeoAddress& address) + { + int count = address.county().toInt(); + + for (int i = 0; i < count; ++i) { + QGeoLocation location; + location.setAddress(address); + reply->callAddLocation(location); + } + } + + void setLocations(GeocodeReplyTest* reply, const QGeoCoordinate & coordinate) + { + for (int i = 0; i < coordinate.longitude(); ++i) { + QGeoLocation location; + location.setCoordinate(coordinate); + reply->callAddLocation(location); + } + } + + QGeoCodeReply* reverseGeocode(const QGeoCoordinate &coordinate, const QGeoShape &bounds) + { + geocodeReply_ = new GeocodeReplyTest(); + connect(geocodeReply_, SIGNAL(aborted()), this, SLOT(requestAborted())); + + setLocations(geocodeReply_, coordinate); + geocodeReply_->callSetViewport(bounds); + + if (coordinate.latitude() > 70) { + errorString_ = "error"; + errorCode_ = (QGeoCodeReply::Error) (qRound(coordinate.latitude() - 70)); + } else { + errorString_ = ""; + errorCode_ = QGeoCodeReply::NoError; + } + if (finishRequestImmediately_) { + if (errorCode_) { + geocodeReply_->callSetError(errorCode_, errorString_); + } else { + geocodeReply_->callSetError(QGeoCodeReply::NoError,coordinate.toString()); + geocodeReply_->callSetFinished(true); + } + } else { + // we only allow serialized requests in QML - previous must have been aborted or finished + Q_ASSERT(timerId_ == 0); + timerId_ = startTimer(200); + } + return static_cast(geocodeReply_); + } + +protected: + void timerEvent(QTimerEvent *event) + { + Q_UNUSED(event); + Q_ASSERT(timerId_ == event->timerId()); + Q_ASSERT(geocodeReply_); + killTimer(timerId_); + timerId_ = 0; + if (errorCode_) { + geocodeReply_->callSetError(errorCode_, errorString_); + emit error(geocodeReply_, errorCode_, errorString_); + } else { + geocodeReply_->callSetError(QGeoCodeReply::NoError, "no error"); + geocodeReply_->callSetFinished(true); + } + emit finished(geocodeReply_); + } + +private: + bool validateWellKnownValues_; + bool finishRequestImmediately_; + bool supported_; + GeocodeReplyTest* geocodeReply_; + int timerId_; + QGeoCodeReply::Error errorCode_; + QString errorString_; +}; + +#endif diff --git a/tests/auto/geotestplugin/qgeomappingmanagerengine_test.h b/tests/auto/geotestplugin/qgeomappingmanagerengine_test.h new file mode 100644 index 0000000..07d832c --- /dev/null +++ b/tests/auto/geotestplugin/qgeomappingmanagerengine_test.h @@ -0,0 +1,175 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOMAPPINGMANAGERENGINE_TEST_H +#define QGEOMAPPINGMANAGERENGINE_TEST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include "qgeomaptype.h" +#include "qgeotilespec.h" +#include "qgeocameracapabilities_p.h" + +#include +#include +#include + +QT_USE_NAMESPACE + + +class TiledMapReplyTest :public QGeoTiledMapReply +{ + Q_OBJECT +public: + TiledMapReplyTest(const QGeoTileSpec &spec, QObject *parent=0): QGeoTiledMapReply (spec, parent) {} + void callSetError ( Error error, const QString & errorString ) {setError(error, errorString);} + void callSetFinished ( bool finished ) { setFinished(finished);} + void callSetCached(bool cached) { setFinished(cached);} + void callSetMapImageData(const QByteArray &data) { setMapImageData(data); } + void callSetMapImageFormat(const QString &format) { setMapImageFormat(format); } + void abort() { emit aborted(); } + +Q_SIGNALS: + void aborted(); +}; + +class QGeoMappingManagerEngineTest: public QGeoMappingManagerEngine + +{ +Q_OBJECT +public: + QGeoMappingManagerEngineTest(const QMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) : + QGeoMappingManagerEngine(parameters), + finishRequestImmediately_(true), + mappingReply_(0), + timerId_(0), + errorCode_(QGeoTiledMapReply::NoError) + { + Q_UNUSED(error) + Q_UNUSED(errorString) + if (parameters.contains("finishRequestImmediately")) + finishRequestImmediately_ = qvariant_cast(parameters.value("finishRequestImmediately")); + setLocale(QLocale (QLocale::German, QLocale::Germany)); + QGeoCameraCapabilities capabilities; + capabilities.setMinimumZoomLevel(0.0); + capabilities.setMaximumZoomLevel(20.0); + capabilities.setSupportsBearing(true); + setCameraCapabilities(capabilities); + } + + void init() + { + setTileSize(256); + QList types; + types << QGeoMapType(QGeoMapType::StreetMap,tr("Street Map"),tr("Test Street Map"), false, 1); + setSupportedMapTypes(types); + QGeoMappingManagerEngine::init(); + } + + QGeoTiledMapReply* getTileImage(const QGeoTileSpec &spec) + { + mappingReply_ = new TiledMapReplyTest(spec, this); + + QImage im(256, 256, QImage::Format_RGB888); + im.fill(QColor("lightgray")); + QRectF rect; + QString text("X: " + QString::number(spec.x()) + "\nY: " + QString::number(spec.y()) + "\nZ: " + QString::number(spec.zoom())); + rect.setWidth(250); + rect.setHeight(250); + rect.setLeft(3); + rect.setTop(3); + QPainter painter; + QPen pen(QColor("firebrick")); + painter.begin(&im); + painter.setPen(pen); + painter.setFont( QFont("Times", 35, 10, false)); + painter.drawText(rect, text); + // different border color for vertically and horizontally adjacent frames + if ((spec.x() + spec.y()) % 2 == 0) + pen.setColor(QColor("yellow")); + pen.setWidth(5); + painter.setPen(pen); + painter.drawRect(0,0,255,255); + painter.end(); + QPixmap pm = QPixmap::fromImage(im); + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::WriteOnly); + pm.save(&buffer, "PNG"); + + mappingReply_->callSetMapImageData(bytes); + mappingReply_->callSetMapImageFormat("png"); + mappingReply_->callSetFinished(true); + + return static_cast(mappingReply_); + } + +public Q_SLOTS: + void requestAborted() + { + if (timerId_) { + killTimer(timerId_); + timerId_ = 0; + } + errorString_ = ""; + errorCode_ = QGeoTiledMapReply::NoError; + } + +protected: + void timerEvent(QTimerEvent *event) + { + Q_ASSERT(timerId_ == event->timerId()); + Q_ASSERT(mappingReply_); + killTimer(timerId_); + timerId_ = 0; + if (errorCode_) { + mappingReply_->callSetError(errorCode_, errorString_); + emit tileError(mappingReply_->tileSpec(), errorString_); + } else { + mappingReply_->callSetError(QGeoTiledMapReply::NoError, "no error"); + mappingReply_->callSetFinished(true); + } + // emit finished(mappingReply_); todo tileFinished + } + +private: + bool finishRequestImmediately_; + TiledMapReplyTest* mappingReply_; + int timerId_; + QGeoTiledMapReply::Error errorCode_; + QString errorString_; +}; + +#endif diff --git a/tests/auto/geotestplugin/qgeoroutingmanagerengine_test.h b/tests/auto/geotestplugin/qgeoroutingmanagerengine_test.h new file mode 100644 index 0000000..303015b --- /dev/null +++ b/tests/auto/geotestplugin/qgeoroutingmanagerengine_test.h @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGERENGINE_TEST_H +#define QGEOROUTINGMANAGERENGINE_TEST_H + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +QT_USE_NAMESPACE + + +class QGeoRoutePrivateDefaultAlt : public QGeoRoutePrivateDefault +{ +public: + QGeoRoutePrivateDefaultAlt() : QGeoRoutePrivateDefault() + { + m_travelTime = 123456; // To identify this is actually a QGeoRoutePrivateDefaultAlt + } + QGeoRoutePrivateDefaultAlt(const QGeoRoutePrivateDefaultAlt &other) + : QGeoRoutePrivateDefault(other) {} + ~QGeoRoutePrivateDefaultAlt() {} + + void setTravelTime(int travelTime) override + { + Q_UNUSED(travelTime) + } +}; + +class QGeoRouteAlt : public QGeoRoute +{ +public: + QGeoRouteAlt() + : QGeoRoute(QExplicitlySharedDataPointer(new QGeoRoutePrivateDefaultAlt())) + { + } +}; + +class RouteReplyTest :public QGeoRouteReply +{ + Q_OBJECT + +public: + RouteReplyTest(QObject *parent=0) :QGeoRouteReply (QGeoRouteRequest(), parent) + {} + void callSetError ( Error error, const QString & errorString ) {setError(error, errorString);} + void callSetFinished ( bool finished ) {setFinished(finished);} + void callSetRoutes(const QList &routes) {setRoutes(routes);} +}; + +class QGeoRoutingManagerEngineTest: public QGeoRoutingManagerEngine +{ + Q_OBJECT + RouteReplyTest* routeReply_; + bool finishRequestImmediately_; + int timerId_; + QGeoRouteReply::Error errorCode_; + QString errorString_; + bool alternateGeoRouteImplementation_; + +public: + QGeoRoutingManagerEngineTest(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) : + QGeoRoutingManagerEngine(parameters), + routeReply_(0), + finishRequestImmediately_(true), + timerId_(0), + errorCode_(QGeoRouteReply::NoError), + alternateGeoRouteImplementation_(false) + { + Q_UNUSED(error) + Q_UNUSED(errorString) + + if (parameters.contains("gc_finishRequestImmediately")) { + finishRequestImmediately_ = qvariant_cast(parameters.value("gc_finishRequestImmediately")); + } + + if (parameters.contains("gc_alternateGeoRoute")) { + alternateGeoRouteImplementation_ = qvariant_cast(parameters.value("gc_alternateGeoRoute")); + } + + setLocale(QLocale (QLocale::German, QLocale::Germany)); + setSupportedFeatureTypes ( + QGeoRouteRequest::NoFeature | QGeoRouteRequest::TollFeature | + QGeoRouteRequest::HighwayFeature | QGeoRouteRequest::PublicTransitFeature | + QGeoRouteRequest::FerryFeature | QGeoRouteRequest::TunnelFeature | + QGeoRouteRequest::DirtRoadFeature | QGeoRouteRequest::ParksFeature | + QGeoRouteRequest::MotorPoolLaneFeature ); + setSupportedFeatureWeights ( + QGeoRouteRequest::NeutralFeatureWeight | QGeoRouteRequest::PreferFeatureWeight | + QGeoRouteRequest::RequireFeatureWeight | QGeoRouteRequest::AvoidFeatureWeight | + QGeoRouteRequest::DisallowFeatureWeight ); + setSupportedManeuverDetails ( + QGeoRouteRequest::NoManeuvers | QGeoRouteRequest::BasicManeuvers); + setSupportedRouteOptimizations ( + QGeoRouteRequest::ShortestRoute | QGeoRouteRequest::FastestRoute | + QGeoRouteRequest::MostEconomicRoute | QGeoRouteRequest::MostScenicRoute); + setSupportedSegmentDetails ( + QGeoRouteRequest::NoSegmentData | QGeoRouteRequest::BasicSegmentData ); + setSupportedTravelModes ( + QGeoRouteRequest::CarTravel | QGeoRouteRequest::PedestrianTravel | + QGeoRouteRequest::BicycleTravel | QGeoRouteRequest::PublicTransitTravel | + QGeoRouteRequest::TruckTravel ); + } + + virtual QGeoRouteReply* calculateRoute(const QGeoRouteRequest& request) + { + routeReply_ = new RouteReplyTest(); + connect(routeReply_, SIGNAL(aborted()), this, SLOT(requestAborted())); + + if (request.numberAlternativeRoutes() > 70) { + errorCode_ = (QGeoRouteReply::Error)(request.numberAlternativeRoutes() - 70); + errorString_ = "error"; + } else { + errorCode_ = QGeoRouteReply::NoError; + errorString_ = ""; + } + setRoutes(request, routeReply_); + if (finishRequestImmediately_) { + if (errorCode_) { + routeReply_->callSetError(errorCode_, errorString_); + } else { + routeReply_->callSetError(QGeoRouteReply::NoError, "no error"); + routeReply_->callSetFinished(true); + } + } else { + // we only allow serialized requests in QML - previous must have been aborted or finished + Q_ASSERT(timerId_ == 0); + timerId_ = startTimer(200); + } + return static_cast(routeReply_); + } + + void setRoutes(const QGeoRouteRequest& request, RouteReplyTest* reply) + { + QList routes; + int travelTime = 0; + if (request.extraParameters().contains("test-traveltime")) + travelTime = request.extraParameters().value("test-traveltime").toMap().value("requestedTime").toInt(); + + for (int i = 0; i < request.numberAlternativeRoutes(); ++i) { + QGeoRoute route; + if (alternateGeoRouteImplementation_) + route = QGeoRouteAlt(); + route.setPath(request.waypoints()); + route.setTravelTime(travelTime); + + const QList metadata = request.waypointsMetadata(); + for (const auto &meta: metadata) { + if (meta.contains("extra")) { + QVariantMap extra = meta.value("extra").toMap(); + if (extra.contains("user_distance")) + route.setDistance(meta.value("extra").toMap().value("user_distance").toMap().value("distance").toDouble()); + } + } + + routes.append(route); + } + reply->callSetRoutes(routes); + } + +public Q_SLOTS: + void requestAborted() + { + if (timerId_) { + killTimer(timerId_); + timerId_ = 0; + } + errorCode_ = QGeoRouteReply::NoError; + errorString_ = ""; + } + +protected: + void timerEvent(QTimerEvent *event) + { + Q_UNUSED(event); + Q_ASSERT(timerId_ == event->timerId()); + Q_ASSERT(routeReply_); + killTimer(timerId_); + timerId_ = 0; + if (errorCode_) { + routeReply_->callSetError(errorCode_, errorString_); + emit error(routeReply_, errorCode_, errorString_); + } else { + routeReply_->callSetError(QGeoRouteReply::NoError, "no error"); + routeReply_->callSetFinished(true); + emit finished(routeReply_); + } + } +}; + +#endif diff --git a/tests/auto/geotestplugin/qgeoserviceproviderplugin_test.cpp b/tests/auto/geotestplugin/qgeoserviceproviderplugin_test.cpp new file mode 100644 index 0000000..d3b0461 --- /dev/null +++ b/tests/auto/geotestplugin/qgeoserviceproviderplugin_test.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderplugin_test.h" +#include "qgeocodingmanagerengine_test.h" +#include "qgeoroutingmanagerengine_test.h" +#include "qgeotiledmappingmanagerengine_test.h" +#include "qplacemanagerengine_test.h" + +#include + +namespace +{ + template + EngineType * createEngine(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) + { + const QString failError = parameters.value(QStringLiteral("error")).toString(); + const QString failErrorString = parameters.value(QStringLiteral("errorString")).toString(); + + if (!failError.isEmpty()) { + *error = QGeoServiceProvider::Error(failError.toInt()); + *errorString = failErrorString; + return 0; + } else { + return new EngineType(parameters, error, errorString); + } + } +} + +QGeoServiceProviderFactoryTest::QGeoServiceProviderFactoryTest() +{ +} + +QGeoServiceProviderFactoryTest::~QGeoServiceProviderFactoryTest() +{ +} + +QGeoRoutingManagerEngine* QGeoServiceProviderFactoryTest::createRoutingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const +{ + return createEngine(parameters, error, errorString); +} + + +QGeoCodingManagerEngine* QGeoServiceProviderFactoryTest::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString) const +{ + return createEngine(parameters, error, errorString); +} + + +QGeoMappingManagerEngine* QGeoServiceProviderFactoryTest::createMappingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const +{ + return createEngine(parameters, error, errorString); +} + +QPlaceManagerEngine* QGeoServiceProviderFactoryTest::createPlaceManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(error); + Q_UNUSED(errorString); + return new QPlaceManagerEngineTest(parameters); +} diff --git a/tests/auto/geotestplugin/qgeoserviceproviderplugin_test.h b/tests/auto/geotestplugin/qgeoserviceproviderplugin_test.h new file mode 100644 index 0000000..c606fdb --- /dev/null +++ b/tests/auto/geotestplugin/qgeoserviceproviderplugin_test.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_TEST_H +#define QGEOSERVICEPROVIDER_TEST_H + +#include +#include + +QT_USE_NAMESPACE + +class QGeoServiceProviderFactoryTest: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "geotestplugin.json") + +public: + QGeoServiceProviderFactoryTest(); + ~QGeoServiceProviderFactoryTest(); + + QGeoMappingManagerEngine* createMappingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const; + QGeoRoutingManagerEngine* createRoutingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString ) const; + QGeoCodingManagerEngine* createGeocodingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const; + QPlaceManagerEngine* createPlaceManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const; +}; + +#endif + + diff --git a/tests/auto/geotestplugin/qgeotiledmap_test.cpp b/tests/auto/geotestplugin/qgeotiledmap_test.cpp new file mode 100644 index 0000000..ef2af7d --- /dev/null +++ b/tests/auto/geotestplugin/qgeotiledmap_test.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmap_test.h" +#include +#include + +QT_USE_NAMESPACE + +class QGeoTiledMapTestPrivate: public QGeoTiledMapPrivate +{ + Q_DECLARE_PUBLIC(QGeoTiledMapTest) +public: + QGeoTiledMapTestPrivate(QGeoTiledMappingManagerEngine *engine) + : QGeoTiledMapPrivate(engine) + { + + } + + ~QGeoTiledMapTestPrivate() + { + + } + + void addParameter(QGeoMapParameter *param) override + { + Q_Q(QGeoTiledMapTest); + if (param->type() == QStringLiteral("cameraCenter_test")) { + // We assume that cameraCenter_test parameters have a QGeoCoordinate property named "center" + // Handle the parameter + QGeoCameraData cameraData = m_cameraData; + QGeoCoordinate newCenter = param->property("center").value(); + cameraData.setCenter(newCenter); + q->setCameraData(cameraData); + // Connect for further changes handling + q->connect(param, SIGNAL(propertyUpdated(QGeoMapParameter *, const char *)), + q, SLOT(onCameraCenter_testChanged(QGeoMapParameter*, const char*))); + + } + } + void removeParameter(QGeoMapParameter *param) override + { + Q_Q(QGeoTiledMapTest); + param->disconnect(q); + } +}; + +QGeoTiledMapTest::QGeoTiledMapTest(QGeoTiledMappingManagerEngine *engine, QObject *parent) +: QGeoTiledMap(*new QGeoTiledMapTestPrivate(engine), engine, parent), m_engine(engine) +{ +} + +void QGeoTiledMapTest::onCameraCenter_testChanged(QGeoMapParameter *param, const char *propertyName) +{ + Q_D(QGeoTiledMapTest); + if (strcmp(propertyName, "center") == 0) { + QGeoCameraData cameraData = d->m_cameraData; + // Not testing for propertyName as this param has only one allowed property + QGeoCoordinate newCenter = param->property(propertyName).value(); + cameraData.setCenter(newCenter); + setCameraData(cameraData); + } +} diff --git a/tests/auto/geotestplugin/qgeotiledmap_test.h b/tests/auto/geotestplugin/qgeotiledmap_test.h new file mode 100644 index 0000000..19c7620 --- /dev/null +++ b/tests/auto/geotestplugin/qgeotiledmap_test.h @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAP_TEST_H +#define QGEOTILEDMAP_TEST_H + +#include +#include + +QT_USE_NAMESPACE + +class QGeoTiledMappingManagerEngineTest; +class QGeoTiledMapTestPrivate; + +class QGeoTiledMapTest: public QGeoTiledMap +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QGeoTiledMapTest) +public: + QGeoTiledMapTest(QGeoTiledMappingManagerEngine *engine, QObject *parent = 0); + +protected slots: + void onCameraCenter_testChanged(QGeoMapParameter *param, const char *propertyName); + +public: + using QGeoTiledMap::setCameraData; + QGeoTiledMappingManagerEngine *m_engine; +}; + +#endif + + diff --git a/tests/auto/geotestplugin/qgeotiledmappingmanagerengine_test.h b/tests/auto/geotestplugin/qgeotiledmappingmanagerengine_test.h new file mode 100644 index 0000000..297be0d --- /dev/null +++ b/tests/auto/geotestplugin/qgeotiledmappingmanagerengine_test.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEDMAPPINGMANAGERENGINE_TEST_H +#define QGEOTILEDMAPPINGMANAGERENGINE_TEST_H + +#include +#include +#include +#include +#include +#include + +#include "qgeotiledmap_test.h" +#include "qgeotilefetcher_test.h" + +QT_USE_NAMESPACE + +class QGeoTiledMappingManagerEngineTest: public QGeoTiledMappingManagerEngine +{ +Q_OBJECT +public: + QGeoTiledMappingManagerEngineTest(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) : + QGeoTiledMappingManagerEngine() + { + Q_UNUSED(error) + Q_UNUSED(errorString) + + setLocale(QLocale (QLocale::German, QLocale::Germany)); + QGeoCameraCapabilities capabilities; + capabilities.setMinimumZoomLevel(0.0); + capabilities.setMaximumZoomLevel(20.0); + capabilities.setSupportsBearing(true); + capabilities.setSupportsTilting(true); + capabilities.setMinimumTilt(0); + capabilities.setMaximumTilt(60); + setTileSize(QSize(256, 256)); + + const QByteArray pluginName = "qmlgeo.test.plugin"; + QList mapTypes; + + mapTypes << QGeoMapType(QGeoMapType::StreetMap, tr("StreetMap"), tr("StreetMap"), false, false, 1, pluginName, capabilities); + mapTypes << QGeoMapType(QGeoMapType::SatelliteMapDay, tr("SatelliteMapDay"), tr("SatelliteMapDay"), false, false, 2, pluginName, capabilities); + mapTypes << QGeoMapType(QGeoMapType::CycleMap, tr("CycleMap"), tr("CycleMap"), false, false, 3, pluginName, capabilities); + + QGeoCameraCapabilities capabilities4; + capabilities4.setMinimumZoomLevel(0.0); + capabilities4.setMaximumZoomLevel(19.0); + capabilities4.setSupportsBearing(true); + capabilities4.setSupportsTilting(true); + capabilities4.setMinimumTilt(0); + capabilities4.setMaximumTilt(80); + capabilities4.setMinimumFieldOfView(1); + capabilities4.setMaximumFieldOfView(179); + QVariantMap meta; + meta["foo"] = 42; + mapTypes << QGeoMapType(QGeoMapType::CustomMap, tr("AlternateCameraCapabilities"), tr("AlternateCameraCapabilities"), false, false, 4, pluginName, capabilities4, meta); + + if (parameters.contains(QStringLiteral("extraMapTypeName"))) { + QString extraMapTypeName = parameters.value(QStringLiteral("extraMapTypeName")).toString(); + mapTypes << QGeoMapType(QGeoMapType::CustomMap, extraMapTypeName, extraMapTypeName, false, false, 5, pluginName, capabilities); + } + + setSupportedMapTypes(mapTypes); + + QGeoTileFetcherTest *fetcher = new QGeoTileFetcherTest(this); + if (parameters.contains(QStringLiteral("finishRequestImmediately"))) + fetcher->setFinishRequestImmediately(parameters.value(QStringLiteral("finishRequestImmediately")).toBool()); + if (parameters.contains(QStringLiteral("tileSize"))) { + int tileSize = parameters.value(QStringLiteral("tileSize")).toInt(); + setTileSize(QSize(tileSize, tileSize)); + } + if (parameters.contains(QStringLiteral("maxZoomLevel"))) { + double maxZoomLevel = parameters.value(QStringLiteral("maxZoomLevel")).toDouble(); + capabilities.setMaximumZoomLevel(maxZoomLevel); + } + + setCameraCapabilities(capabilities); + fetcher->setTileSize(tileSize()); + setTileFetcher(fetcher); + } + + QGeoMap *createMap() override + { + return new QGeoTiledMapTest(this); + } +}; + +#endif diff --git a/tests/auto/geotestplugin/qgeotilefetcher_test.h b/tests/auto/geotestplugin/qgeotilefetcher_test.h new file mode 100644 index 0000000..e108f48 --- /dev/null +++ b/tests/auto/geotestplugin/qgeotilefetcher_test.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOTILEFETCHER_TEST_H +#define QGEOTILEFETCHER_TEST_H + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class TiledMapReplyTest :public QGeoTiledMapReply +{ + Q_OBJECT +public: + TiledMapReplyTest(const QGeoTileSpec &spec, QObject *parent=0): QGeoTiledMapReply (spec, parent) {} + void callSetError ( Error error, const QString & errorString ) {setError(error, errorString);} + void callSetFinished ( bool finished ) { setFinished(finished);} + void callSetCached(bool cached) { setFinished(cached);} + void callSetMapImageData(const QByteArray &data) { setMapImageData(data); } + void callSetMapImageFormat(const QString &format) { setMapImageFormat(format); } + void abort() { emit aborted(); } + +Q_SIGNALS: + void aborted(); +}; + +class QGeoTileFetcherTest: public QGeoTileFetcher +{ + Q_OBJECT +public: + QGeoTileFetcherTest(QGeoMappingManagerEngine *parent) + : QGeoTileFetcher(parent), finishRequestImmediately_(false), errorCode_(QGeoTiledMapReply::NoError) + { + } + + bool init() + { + return true; + } + + QGeoTiledMapReply* getTileImage(const QGeoTileSpec &spec) + { + TiledMapReplyTest* mappingReply = new TiledMapReplyTest(spec, this); + + QImage im(256, 256, QImage::Format_RGB888); + im.fill(QColor("lightgray")); + QRectF rect; + QString text("X: " + QString::number(spec.x()) + "\nY: " + QString::number(spec.y()) + "\nZ: " + QString::number(spec.zoom())); + rect.setWidth(250); + rect.setHeight(250); + rect.setLeft(3); + rect.setTop(3); + QPainter painter; + QPen pen(QColor("firebrick")); + painter.begin(&im); + painter.setPen(pen); + painter.setFont( QFont("Times", 35, 10, false)); + painter.drawText(rect, text); + // different border color for vertically and horizontally adjacent frames + if ((spec.x() + spec.y()) % 2 == 0) + pen.setColor(QColor("yellow")); + pen.setWidth(5); + painter.setPen(pen); + painter.drawRect(0,0,255,255); + painter.end(); + QPixmap pm = QPixmap::fromImage(im); + QByteArray bytes; + QBuffer buffer(&bytes); + buffer.open(QIODevice::WriteOnly); + pm.save(&buffer, "PNG"); + + mappingReply->callSetMapImageData(bytes); + mappingReply->callSetMapImageFormat("png"); + + if (finishRequestImmediately_) { + updateRequest(mappingReply); + return mappingReply; + } else { + if (m_queue.isEmpty()) + timer_.start(500, this); + m_queue.append(mappingReply); + } + + return mappingReply; + } + + void setFinishRequestImmediately(bool enabled) + { + finishRequestImmediately_ = enabled; + } + + void setTileSize(QSize tileSize) + { + tileSize_ = tileSize; + } + +public Q_SLOTS: + void requestAborted() + { + timer_.stop(); + errorString_.clear(); + errorCode_ = QGeoTiledMapReply::NoError; + } +Q_SIGNALS: + void tileFetched(const QGeoTileSpec&); + +protected: + void updateRequest(TiledMapReplyTest* mappingReply) + { + if (errorCode_) { + mappingReply->callSetError(errorCode_, errorString_); + emit tileError(mappingReply->tileSpec(), errorString_); + } else { + mappingReply->callSetError(QGeoTiledMapReply::NoError, "no error"); + mappingReply->callSetFinished(true); + emit tileFetched(mappingReply->tileSpec()); + } + } + + void timerEvent(QTimerEvent *event) + { + if (event->timerId() != timer_.timerId()) { + QGeoTileFetcher::timerEvent(event); + return; + } + updateRequest(m_queue.takeFirst()); + if (m_queue.isEmpty()) { + timer_.stop(); + } + } + +private: + bool finishRequestImmediately_; + QBasicTimer timer_; + QGeoTiledMapReply::Error errorCode_; + QString errorString_; + QSize tileSize_; + QList m_queue; +}; + +#endif diff --git a/tests/auto/geotestplugin/qplacemanagerengine_test.h b/tests/auto/geotestplugin/qplacemanagerengine_test.h new file mode 100644 index 0000000..7245ccf --- /dev/null +++ b/tests/auto/geotestplugin/qplacemanagerengine_test.h @@ -0,0 +1,732 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPLACEMANAGERENGINE_TEST_H +#define QPLACEMANAGERENGINE_TEST_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +inline uint qHash(const QPlaceCategory &category) +{ + return qHash(QUuid(category.categoryId().toLatin1())); +} + +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class QPlacePrivateDefaultAlt : public QPlacePrivateDefault +{ +public: + QPlacePrivateDefaultAlt() {} + QPlacePrivateDefaultAlt(const QPlacePrivateDefaultAlt &other) + : QPlacePrivateDefault(other) + { + } + ~QPlacePrivateDefaultAlt() {} + + QPlaceAttribute extendedAttribute(const QString &attributeType) const override + { + if (attributeType == QStringLiteral("x_provider")) { + QPlaceAttribute a; + a.setLabel(QStringLiteral("x_provider")); + a.setText(QStringLiteral("QPlacePrivateDefaultAlt")); + return a; + } else { + return QPlacePrivateDefault::extendedAttribute(attributeType); + } + } +}; + +class QPlaceAlt : public QPlace +{ +public: + QPlaceAlt() : QPlace(QSharedDataPointer(new QPlacePrivateDefaultAlt())) + { + } +}; + +class PlaceReply : public QPlaceReply +{ + Q_OBJECT + + friend class QPlaceManagerEngineTest; + +public: + PlaceReply(QObject *parent = 0) + : QPlaceReply(parent) + { } + + Q_INVOKABLE void emitFinished() + { + emit finished(); + } +}; + +class ContentReply : public QPlaceContentReply +{ + Q_OBJECT + + friend class QPlaceManagerEngineTest; + +public: + ContentReply(QObject *parent = 0) + : QPlaceContentReply(parent) + {} + + Q_INVOKABLE void emitError() + { + emit error(error(), errorString()); + } + + Q_INVOKABLE void emitFinished() + { + emit finished(); + } +}; + +class DetailsReply : public QPlaceDetailsReply +{ + Q_OBJECT + + friend class QPlaceManagerEngineTest; + +public: + DetailsReply(QObject *parent = 0) + : QPlaceDetailsReply(parent) + { } + + Q_INVOKABLE void emitError() + { + emit error(error(), errorString()); + } + + Q_INVOKABLE void emitFinished() + { + emit finished(); + } +}; + +class IdReply : public QPlaceIdReply +{ + Q_OBJECT + + friend class QPlaceManagerEngineTest; + +public: + IdReply(QPlaceIdReply::OperationType type, QObject *parent = 0) + : QPlaceIdReply(type, parent) + { } + + Q_INVOKABLE void emitError() + { + emit error(error(), errorString()); + } + + Q_INVOKABLE void emitFinished() + { + emit finished(); + } +}; + +class PlaceSearchReply : public QPlaceSearchReply +{ + Q_OBJECT + +public: + PlaceSearchReply(const QList &results, QObject *parent = 0) + : QPlaceSearchReply(parent) + { + setResults(results); + } + + Q_INVOKABLE void emitError() + { + emit error(error(), errorString()); + } + + Q_INVOKABLE void emitFinished() + { + emit finished(); + } +}; + +class SuggestionReply : public QPlaceSearchSuggestionReply +{ + Q_OBJECT + +public: + SuggestionReply(const QStringList &suggestions, QObject *parent = 0) + : QPlaceSearchSuggestionReply(parent) + { + setSuggestions(suggestions); + } + + Q_INVOKABLE void emitError() + { + emit error(error(), errorString()); + } + + Q_INVOKABLE void emitFinished() + { + emit finished(); + } +}; + +class QPlaceManagerEngineTest : public QPlaceManagerEngine +{ + Q_OBJECT +public: + QPlaceManagerEngineTest(const QVariantMap ¶meters) + : QPlaceManagerEngine(parameters) + { + m_locales << QLocale(); + if (parameters.value(QStringLiteral("initializePlaceData"), false).toBool()) { + QFile placeData(QFINDTESTDATA("place_data.json")); + QVERIFY(placeData.exists()); + if (placeData.open(QIODevice::ReadOnly)) { + QJsonDocument document = QJsonDocument::fromJson(placeData.readAll()); + + if (document.isObject()) { + QJsonObject o = document.object(); + + if (o.contains(QStringLiteral("categories"))) { + QJsonArray categories = o.value(QStringLiteral("categories")).toArray(); + + for (int i = 0; i < categories.count(); ++i) { + QJsonObject c = categories.at(i).toObject(); + + QPlaceCategory category; + + category.setName(c.value(QStringLiteral("name")).toString()); + category.setCategoryId(c.value(QStringLiteral("id")).toString()); + + m_categories.insert(category.categoryId(), category); + + const QString parentId = c.value(QStringLiteral("parentId")).toString(); + m_childCategories[parentId].append(category.categoryId()); + } + } + + if (o.contains(QStringLiteral("places"))) { + QJsonArray places = o.value(QStringLiteral("places")).toArray(); + + for (int i = 0; i < places.count(); ++i) { + QJsonObject p = places.at(i).toObject(); + + QPlace place; + if (p.value(QStringLiteral("alternateImplementation")).toBool(false)) { + place = QPlaceAlt(); + QPlaceAttribute att; + att.setLabel(QStringLiteral("x_provider")); + att.setText(QStringLiteral("42")); // Doesn't matter, wont be used. + place.setExtendedAttribute(QStringLiteral("x_provider"), att); + } + + place.setName(p.value(QStringLiteral("name")).toString()); + place.setPlaceId(p.value(QStringLiteral("id")).toString()); + + QList categories; + QJsonArray ca = p.value(QStringLiteral("categories")).toArray(); + for (int j = 0; j < ca.count(); ++j) { + QPlaceCategory c = m_categories.value(ca.at(j).toString()); + if (!c.isEmpty()) + categories.append(c); + } + place.setCategories(categories); + + QGeoCoordinate coordinate; + QJsonObject lo = p.value(QStringLiteral("location")).toObject(); + coordinate.setLatitude(lo.value(QStringLiteral("latitude")).toDouble()); + coordinate.setLongitude(lo.value(QStringLiteral("longitude")).toDouble()); + + QGeoLocation location; + location.setCoordinate(coordinate); + + place.setLocation(location); + + m_places.insert(place.placeId(), place); + + QStringList recommendations; + QJsonArray ra = p.value(QStringLiteral("recommendations")).toArray(); + for (int j = 0; j < ra.count(); ++j) + recommendations.append(ra.at(j).toString()); + m_placeRecommendations.insert(place.placeId(), recommendations); + + QJsonArray revArray = p.value(QStringLiteral("reviews")).toArray(); + QList reviews; + for (int j = 0; j < revArray.count(); ++j) { + QJsonObject ro = revArray.at(j).toObject(); + QPlaceReview review; + if (ro.contains(QStringLiteral("title"))) + review.setTitle(ro.value(QStringLiteral("title")).toString()); + if (ro.contains(QStringLiteral("text"))) + review.setText(ro.value(QStringLiteral("text")).toString()); + + if (ro.contains(QStringLiteral("language"))) + review.setLanguage(ro.value("language").toString()); + + if (ro.contains(QStringLiteral("rating"))) + review.setRating(ro.value("rating").toDouble()); + + if (ro.contains(QStringLiteral("dateTime"))) + review.setDateTime(QDateTime::fromString( + ro.value(QStringLiteral("dateTime")).toString(), + QStringLiteral("hh:mm dd-MM-yyyy"))); + if (ro.contains(QStringLiteral("reviewId"))) + review.setReviewId(ro.value("reviewId").toString()); + + reviews << review; + } + m_placeReviews.insert(place.placeId(), reviews); + + QJsonArray imgArray = p.value(QStringLiteral("images")).toArray(); + QList images; + for (int j = 0; j < imgArray.count(); ++j) { + QJsonObject imgo = imgArray.at(j).toObject(); + QPlaceImage image; + if (imgo.contains(QStringLiteral("url"))) + image.setUrl(imgo.value(QStringLiteral("url")).toString()); + + if (imgo.contains("imageId")) + image.setImageId(imgo.value(QStringLiteral("imageId")).toString()); + + if (imgo.contains("mimeType")) + image.setMimeType(imgo.value(QStringLiteral("mimeType")).toString()); + + images << image; + } + + m_placeImages.insert(place.placeId(), images); + + QJsonArray edArray = p.value(QStringLiteral("editorials")).toArray(); + QList editorials; + for (int j = 0; j < edArray.count(); ++j) { + QJsonObject edo = edArray.at(j).toObject(); + QPlaceEditorial editorial; + if (edo.contains(QStringLiteral("title"))) + editorial.setTitle(edo.value(QStringLiteral("title")).toString()); + + if (edo.contains(QStringLiteral("text"))) + editorial.setText(edo.value(QStringLiteral("text")).toString()); + + if (edo.contains(QStringLiteral("language"))) + editorial.setLanguage(edo.value(QStringLiteral("language")).toString()); + + editorials << editorial; + } + + m_placeEditorials.insert(place.placeId(), editorials); + } + } + } + } + } + } + + QPlaceDetailsReply *getPlaceDetails(const QString &placeId) override + { + DetailsReply *reply = new DetailsReply(this); + + if (placeId.isEmpty() || !m_places.contains(placeId)) { + reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); + QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); + } else { + reply->setPlace(m_places.value(placeId)); + } + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QPlaceContentReply *getPlaceContent(const QPlaceContentRequest &query) override + { + ContentReply *reply = new ContentReply(this); + if (query.placeId().isEmpty() || !m_places.contains(query.placeId())) { + reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); + QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); + + } else { + QPlaceContent::Collection collection; + int totalCount = 0; + switch (query.contentType()) { + case QPlaceContent::ReviewType: + totalCount = m_placeReviews.value(query.placeId()).count(); + break; + case QPlaceContent::ImageType: + totalCount = m_placeImages.value(query.placeId()).count(); + break; + case QPlaceContent::EditorialType: + totalCount = m_placeEditorials.value(query.placeId()).count(); + default: + //do nothing + break; + } + + QVariantMap context = query.contentContext().toMap(); + + int offset = context.value(QStringLiteral("offset"), 0).toInt(); + int max = (query.limit() == -1) ? totalCount + : qMin(offset + query.limit(), totalCount); + for (int i = offset; i < max; ++i) { + switch (query.contentType()) { + case QPlaceContent::ReviewType: + collection.insert(i, m_placeReviews.value(query.placeId()).at(i)); + break; + case QPlaceContent::ImageType: + collection.insert(i, m_placeImages.value(query.placeId()).at(i)); + break; + case QPlaceContent::EditorialType: + collection.insert(i, m_placeEditorials.value(query.placeId()).at(i)); + default: + //do nothing + break; + } + } + + reply->setContent(collection); + reply->setTotalCount(totalCount); + + if (max != totalCount) { + context.clear(); + context.insert(QStringLiteral("offset"), offset + query.limit()); + QPlaceContentRequest request = query; + request.setContentContext(context); + reply->setNextPageRequest(request); + } + if (offset > 0) { + context.clear(); + context.insert(QStringLiteral("offset"), qMin(0, offset - query.limit())); + QPlaceContentRequest request = query; + request.setContentContext(context); + reply->setPreviousPageRequest(request); + } + } + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + return reply; + } + + QPlaceSearchReply *search(const QPlaceSearchRequest &query) override + { + QList results; + + if (!query.searchTerm().isEmpty()) { + foreach (const QPlace &place, m_places) { + if (!place.name().contains(query.searchTerm(), Qt::CaseInsensitive)) + continue; + + QPlaceResult r; + r.setPlace(place); + r.setTitle(place.name()); + + results.append(r); + } + } else if (!query.categories().isEmpty()) { + QSet categories = query.categories().toSet(); + foreach (const QPlace &place, m_places) { + if (place.categories().toSet().intersect(categories).isEmpty()) + continue; + + QPlaceResult r; + r.setPlace(place); + r.setTitle(place.name()); + + results.append(r); + } + } else if (!query.recommendationId().isEmpty()) { + QStringList recommendations = m_placeRecommendations.value(query.recommendationId()); + foreach (const QString &id, recommendations) { + QPlaceResult r; + r.setPlace(m_places.value(id)); + r.setTitle(r.place().name()); + + results.append(r); + } + } + + PlaceSearchReply *reply = new PlaceSearchReply(results, this); + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QPlaceSearchSuggestionReply *searchSuggestions(const QPlaceSearchRequest &query) override + { + QStringList suggestions; + if (query.searchTerm() == QLatin1String("test")) { + suggestions << QStringLiteral("test1"); + suggestions << QStringLiteral("test2"); + suggestions << QStringLiteral("test3"); + } + + SuggestionReply *reply = new SuggestionReply(suggestions, this); + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QPlaceIdReply *savePlace(const QPlace &place) override + { + IdReply *reply = new IdReply(QPlaceIdReply::SavePlace, this); + + if (!place.placeId().isEmpty() && !m_places.contains(place.placeId())) { + reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); + QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); + } else if (!place.placeId().isEmpty()) { + m_places.insert(place.placeId(), place); + reply->setId(place.placeId()); + } else { + QPlace p = place; + p.setPlaceId(QUuid::createUuid().toString()); + m_places.insert(p.placeId(), p); + + reply->setId(p.placeId()); + } + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QPlaceIdReply *removePlace(const QString &placeId) override + { + IdReply *reply = new IdReply(QPlaceIdReply::RemovePlace, this); + reply->setId(placeId); + + if (!m_places.contains(placeId)) { + reply->setError(QPlaceReply::PlaceDoesNotExistError, tr("Place does not exist")); + QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); + } else { + m_places.remove(placeId); + } + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QPlaceIdReply *saveCategory(const QPlaceCategory &category, const QString &parentId) override + { + IdReply *reply = new IdReply(QPlaceIdReply::SaveCategory, this); + + if ((!category.categoryId().isEmpty() && !m_categories.contains(category.categoryId())) || + (!parentId.isEmpty() && !m_categories.contains(parentId))) { + reply->setError(QPlaceReply::CategoryDoesNotExistError, tr("Category does not exist")); + QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); + } else if (!category.categoryId().isEmpty()) { + m_categories.insert(category.categoryId(), category); + QStringList children = m_childCategories.value(parentId); + + QMutableHashIterator i(m_childCategories); + while (i.hasNext()) { + i.next(); + i.value().removeAll(category.categoryId()); + } + + if (!children.contains(category.categoryId())) { + children.append(category.categoryId()); + m_childCategories.insert(parentId, children); + } + reply->setId(category.categoryId()); + } else { + QPlaceCategory c = category; + c.setCategoryId(QUuid::createUuid().toString()); + m_categories.insert(c.categoryId(), c); + QStringList children = m_childCategories.value(parentId); + if (!children.contains(c.categoryId())) { + children.append(c.categoryId()); + m_childCategories.insert(parentId, children); + } + + reply->setId(c.categoryId()); + } + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QPlaceIdReply *removeCategory(const QString &categoryId) override + { + IdReply *reply = new IdReply(QPlaceIdReply::RemoveCategory, this); + reply->setId(categoryId); + + if (!m_categories.contains(categoryId)) { + reply->setError(QPlaceReply::CategoryDoesNotExistError, tr("Category does not exist")); + QMetaObject::invokeMethod(reply, "emitError", Qt::QueuedConnection); + } else { + m_categories.remove(categoryId); + + QMutableHashIterator i(m_childCategories); + while (i.hasNext()) { + i.next(); + i.value().removeAll(categoryId); + } + } + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QPlaceReply *initializeCategories() override + { + QPlaceReply *reply = new PlaceReply(this); + + QMetaObject::invokeMethod(reply, "emitFinished", Qt::QueuedConnection); + + return reply; + } + + QString parentCategoryId(const QString &categoryId) const override + { + QHashIterator i(m_childCategories); + while (i.hasNext()) { + i.next(); + if (i.value().contains(categoryId)) + return i.key(); + } + + return QString(); + } + + virtual QStringList childCategoryIds(const QString &categoryId) const override + { + return m_childCategories.value(categoryId); + } + + virtual QPlaceCategory category(const QString &categoryId) const override + { + return m_categories.value(categoryId); + } + + QList childCategories(const QString &parentId) const override + { + QList categories; + + foreach (const QString &id, m_childCategories.value(parentId)) + categories.append(m_categories.value(id)); + + return categories; + } + + QList locales() const override + { + return m_locales; + } + + void setLocales(const QList &locales) override + { + m_locales = locales; + } + + QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const override + { + QList > candidates; + + QMap sizeDictionary; + sizeDictionary.insert(QStringLiteral("s"), 20); + sizeDictionary.insert(QStringLiteral("m"), 30); + sizeDictionary.insert(QStringLiteral("l"), 50); + + QStringList sizeKeys; + sizeKeys << QStringLiteral("s") << QStringLiteral("m") << QStringLiteral("l"); + + foreach (const QString &sizeKey, sizeKeys) + { + if (icon.parameters().contains(sizeKey)) + candidates.append(QPair(sizeDictionary.value(sizeKey), + icon.parameters().value(sizeKey).toUrl())); + } + + if (candidates.isEmpty()) + return QUrl(); + else if (candidates.count() == 1) { + return candidates.first().second; + } else { + //we assume icons are squarish so we can use height to + //determine which particular icon to return + int requestedHeight = size.height(); + + for (int i = 0; i < candidates.count() - 1; ++i) { + int thresholdHeight = (candidates.at(i).first + candidates.at(i+1).first) / 2; + if (requestedHeight < thresholdHeight) + return candidates.at(i).second; + } + return candidates.last().second; + } + } + + QPlace compatiblePlace(const QPlace &original) const override + { + QPlace place; + place.setName(original.name()); + return place; + } + +private: + QList m_locales; + QHash m_places; + QHash m_categories; + QHash m_childCategories; + QHash m_placeRecommendations; + QHash > m_placeReviews; + QHash > m_placeImages; + QHash > m_placeEditorials; +}; + +#endif diff --git a/tests/auto/geotestplugin/testdata.qrc b/tests/auto/geotestplugin/testdata.qrc new file mode 100644 index 0000000..23eed23 --- /dev/null +++ b/tests/auto/geotestplugin/testdata.qrc @@ -0,0 +1,5 @@ + + + place_data.json + + diff --git a/tests/auto/maptype/maptype.pro b/tests/auto/maptype/maptype.pro new file mode 100644 index 0000000..1622dc5 --- /dev/null +++ b/tests/auto/maptype/maptype.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_maptype + +SOURCES += tst_maptype.cpp + +QT += location-private testlib diff --git a/tests/auto/maptype/tst_maptype.cpp b/tests/auto/maptype/tst_maptype.cpp new file mode 100644 index 0000000..f571158 --- /dev/null +++ b/tests/auto/maptype/tst_maptype.cpp @@ -0,0 +1,190 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include + +QT_USE_NAMESPACE + +Q_DECLARE_METATYPE(QGeoMapType) + +class tst_MapType : public QObject +{ + Q_OBJECT + +public: + tst_MapType(); + +private Q_SLOTS: + void constructorTest(); + void comparison(); + void comparison_data(); +}; + +tst_MapType::tst_MapType() {} + +void tst_MapType::constructorTest() +{ + QGeoCameraCapabilities capabilities; + capabilities.setMinimumZoomLevel(0.0); + capabilities.setMaximumZoomLevel(20.0); + capabilities.setSupportsBearing(true); + capabilities.setSupportsTilting(true); + capabilities.setMinimumTilt(0); + capabilities.setMaximumTilt(60); + capabilities.setMinimumFieldOfView(20); + capabilities.setMaximumFieldOfView(90); + const QByteArray pluginName = "tst_MapType"; + QGeoMapType *testObjPtr = new QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street map"), + QStringLiteral("map description"), true, true, 1, pluginName, capabilities); + QVERIFY(testObjPtr); + QCOMPARE(testObjPtr->style(), QGeoMapType::StreetMap); + QCOMPARE(testObjPtr->name(), QStringLiteral("street map")); + QCOMPARE(testObjPtr->description(), QStringLiteral("map description")); + QVERIFY(testObjPtr->mobile()); + QVERIFY(testObjPtr->night()); + QCOMPARE(testObjPtr->mapId(), 1); + QCOMPARE(testObjPtr->pluginName(), pluginName); + QCOMPARE(testObjPtr->cameraCapabilities(), capabilities); + QCOMPARE(testObjPtr->metadata(), QVariantMap()); + delete testObjPtr; + + testObjPtr = new QGeoMapType(); + QCOMPARE(testObjPtr->style(), QGeoMapType::NoMap); + QVERIFY2(testObjPtr->name().isEmpty(), "Wrong default value"); + QVERIFY2(testObjPtr->description().isEmpty(), "Wrong default value"); + QVERIFY2(!testObjPtr->mobile(), "Wrong default value"); + QVERIFY2(!testObjPtr->night(), "Wrong default value"); + QCOMPARE(testObjPtr->mapId(), 0); + QCOMPARE(testObjPtr->pluginName(), QByteArrayLiteral("")); + QCOMPARE(testObjPtr->cameraCapabilities(), QGeoCameraCapabilities()); + delete testObjPtr; +} + +void tst_MapType::comparison_data() +{ + QTest::addColumn("type1"); + QTest::addColumn("type2"); + QTest::addColumn("expected"); + + const QByteArray pluginName = "tst_MapType"; + QGeoCameraCapabilities capabilities; + capabilities.setMinimumZoomLevel(0.0); + capabilities.setMaximumZoomLevel(20.0); + capabilities.setSupportsBearing(true); + capabilities.setSupportsTilting(true); + capabilities.setMinimumTilt(0); + capabilities.setMaximumTilt(60); + capabilities.setMinimumFieldOfView(20); + capabilities.setMaximumFieldOfView(90); + QGeoCameraCapabilities capabilities2 = capabilities; + capabilities2.setMaximumFieldOfView(80); + + QTest::newRow("null") << QGeoMapType() << QGeoMapType() << true; + + QTest::newRow("equal") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << true; + + QTest::newRow("style") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::TerrainMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << false; + + QTest::newRow("name") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("different name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << false; + + QTest::newRow("description") << QGeoMapType(QGeoMapType::StreetMap, + QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, + QStringLiteral("street name"), + QStringLiteral("different desc"), false, false, 42, pluginName, capabilities) + << false; + + QTest::newRow("mobile") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), true, false, 42, pluginName, capabilities) + << false; + + QTest::newRow("night") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, true, 42, pluginName, capabilities) + << false; + + QTest::newRow("id") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 99, pluginName, capabilities) + << false; + + QTest::newRow("plugin_name") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, QByteArrayLiteral("abc"), capabilities) + << false; + + QTest::newRow("camera_capabilities") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities2) + << false; + + QVariantMap metadata; + metadata["foo"] = 42; + QTest::newRow("metadata") << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities) + << QGeoMapType(QGeoMapType::StreetMap, QStringLiteral("street name"), + QStringLiteral("street desc"), false, false, 42, pluginName, capabilities, metadata) + << false; +} + +void tst_MapType::comparison() +{ + QFETCH(QGeoMapType, type1); + QFETCH(QGeoMapType, type2); + QFETCH(bool, expected); + + QCOMPARE(type1 == type2, expected); + QCOMPARE(type1 != type2, !expected); +} + +QTEST_APPLESS_MAIN(tst_MapType) + +#include "tst_maptype.moc" diff --git a/tests/auto/nokia_services/nokia_services.pro b/tests/auto/nokia_services/nokia_services.pro new file mode 100644 index 0000000..4c56e9f --- /dev/null +++ b/tests/auto/nokia_services/nokia_services.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS += routing places_semiauto diff --git a/tests/auto/nokia_services/places_semiauto/places_semiauto.pro b/tests/auto/nokia_services/places_semiauto/places_semiauto.pro new file mode 100644 index 0000000..7953509 --- /dev/null +++ b/tests/auto/nokia_services/places_semiauto/places_semiauto.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaces + +HEADERS += ../../placemanager_utils/placemanager_utils.h + +SOURCES += tst_places.cpp \ + ../../placemanager_utils/placemanager_utils.cpp + +QT += location testlib diff --git a/tests/auto/nokia_services/places_semiauto/tst_places.cpp b/tests/auto/nokia_services/places_semiauto/tst_places.cpp new file mode 100644 index 0000000..2f68ab4 --- /dev/null +++ b/tests/auto/nokia_services/places_semiauto/tst_places.cpp @@ -0,0 +1,747 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#if QT_CONFIG(process) +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../placemanager_utils/placemanager_utils.h" + +QT_USE_NAMESPACE + +class tst_QPlaceManagerNokia : public PlaceManagerUtils +{ + Q_OBJECT +public: + enum ExpectedResults { + AnyResults, //zero or more results expected + SomeResults, //at least one result expected + NoResults // zero results expected + }; + + tst_QPlaceManagerNokia(); + +private Q_SLOTS: + void initTestCase(); + void search(); + void search_data(); + void searchResultFields(); + void recommendations(); + void recommendations_data(); + void details(); + void categories(); + void suggestions(); + void suggestions_data(); + void suggestionsMisc(); + void locale(); + void content(); + void content_data(); + void unsupportedFunctions(); + +private: + void commonAreas(QList *dataTags, QList *areas, + QList *errors, + QList *results); + + static const QLatin1String ValidKnownPlaceId; + static const QLatin1String ProxyEnv; + static const QLatin1String AppIdEnv; + static const QLatin1String TokenEnv; + + QGeoServiceProvider *provider; +}; + +Q_DECLARE_METATYPE(tst_QPlaceManagerNokia::ExpectedResults) + +// ValidKnownPlaceId is the id of a place with a full complement of place content. Editorials, +// reviews, images, recommendations. If it disappears these tests will fail. +// Currently it is set to an Eiffel Tower tourist office. +const QLatin1String tst_QPlaceManagerNokia::ValidKnownPlaceId("250u09tu-4561b8da952f4fd79c4e1998c3fcf032"); + +const QLatin1String tst_QPlaceManagerNokia::ProxyEnv("NOKIA_PLUGIN_PROXY"); +const QLatin1String tst_QPlaceManagerNokia::AppIdEnv("NOKIA_APPID"); +const QLatin1String tst_QPlaceManagerNokia::TokenEnv("NOKIA_TOKEN"); + +tst_QPlaceManagerNokia::tst_QPlaceManagerNokia() +{ +} + +void tst_QPlaceManagerNokia::initTestCase() +{ + QVariantMap params; + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + QVERIFY(providers.contains("here")); +#if QT_CONFIG(process) + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + + if (!(env.contains(AppIdEnv) && env.contains(TokenEnv))) + QSKIP("NOKIA_APPID and NOKIA_TOKEN environment variables not set");\ + + params.insert(QStringLiteral("here.app_id"), env.value(AppIdEnv)); + params.insert(QStringLiteral("here.token"), env.value(TokenEnv)); + + if (env.contains(ProxyEnv)) + params.insert(QStringLiteral("here.proxy"), env.value(ProxyEnv)); +#else + QSKIP("Cannot parse process environment, NOKIA_APPID and NOKIA_TOKEN not set"); +#endif + provider = new QGeoServiceProvider("here", params); + placeManager = provider->placeManager(); + QVERIFY(placeManager); +} + +void tst_QPlaceManagerNokia::search() +{ + QFETCH(QGeoShape, area); + QFETCH(QString, searchTerm); + QFETCH(QList, categories); + QFETCH(QPlaceReply::Error, error); + QFETCH(ExpectedResults, expectedResults); + + QPlaceSearchRequest searchRequest; + searchRequest.setSearchArea(area); + searchRequest.setSearchTerm(searchTerm); + if (categories.count() == 1) + searchRequest.setCategory(categories.first()); + else + searchRequest.setCategories(categories); + + QList results; + QVERIFY(doSearch(searchRequest, &results, error)); + + if (expectedResults == NoResults) + QVERIFY(results.count() == 0); + else if (expectedResults == SomeResults) + QVERIFY(results.count() > 0); +} + +void tst_QPlaceManagerNokia::search_data() +{ + QTest::addColumn("area"); + QTest::addColumn("searchTerm"); + QTest::addColumn >("categories"); + QTest::addColumn("hint"); + QTest::addColumn ("error"); + QTest::addColumn("expectedResults"); + + for (int i = 0; i < 3; i++) { + QByteArray suffix; + QGeoShape area; + if (i==0) { + area = QGeoCircle(QGeoCoordinate(-27.5, 153)); + suffix = " (Circle - center only)"; + } else if (i==1) { + area = QGeoCircle(QGeoCoordinate(-27.5, 153), 5000); + suffix = " (Circle - with radius specified)"; + } else { + area = QGeoRectangle(QGeoCoordinate(-26.5, 152), QGeoCoordinate(-28.5, 154)); + suffix = " (Rectangle)"; + } + + QByteArray dataTag = QByteArray("coordinate only") + suffix; + QTest::newRow(dataTag) << area + << QString() + << QList() + << QPlaceSearchRequest::UnspecifiedHint + << QPlaceReply::NoError + << SomeResults; + + dataTag = QByteArray("seach term") + suffix; + QTest::newRow(dataTag) << area + << "sushi" + << QList() + << QPlaceSearchRequest::UnspecifiedHint + << QPlaceReply::NoError + << SomeResults; + + QPlaceCategory eatDrinkCat; + eatDrinkCat.setCategoryId(QStringLiteral("eat-drink")); + dataTag = QByteArray("single valid category") + suffix; + QTest::newRow(dataTag) << area + << QString() + << (QList() + << eatDrinkCat) + << QPlaceSearchRequest::UnspecifiedHint + << QPlaceReply::NoError + << SomeResults; + + QPlaceCategory accommodationCat; + accommodationCat.setCategoryId(QStringLiteral("accommodation")); + dataTag = QByteArray("multiple valid categories") + suffix; + QTest::newRow(dataTag) << area + << QString() + << (QList() + << eatDrinkCat + << accommodationCat) + << QPlaceSearchRequest::UnspecifiedHint + << QPlaceReply::NoError + << SomeResults; + + QPlaceCategory nonExistentCat; + nonExistentCat.setCategoryId(QStringLiteral("klingon cuisine")); + dataTag = QByteArray("non-existent category") + suffix; + QTest::newRow(dataTag) << area + << QString() + << (QList() + << nonExistentCat) + << QPlaceSearchRequest::UnspecifiedHint + << QPlaceReply::NoError + << NoResults; + + dataTag = QByteArray("search term and category") + suffix; + QTest::newRow(dataTag) << area + << "sushi" + << (QList() + << eatDrinkCat) + << QPlaceSearchRequest::UnspecifiedHint + << QPlaceReply::BadArgumentError + << NoResults; + } + + + //invalid areas and boundary testing + QList dataTags; + QList areas; + QList errors; + QList results; + commonAreas(&dataTags, &areas, &errors, &results); + + for (int i = 0; i < dataTags.count(); ++i) { + QTest::newRow(dataTags.at(i)) << areas.at(i) + << "sushi" + << QList() + << QPlaceSearchRequest::UnspecifiedHint + << errors.at(i) + << results.at(i); + } + + //relevancy hints will be ignored by the backend, but should give a valid result + QTest::newRow("check using a distance relevancy hint") + << static_cast(QGeoCircle(QGeoCoordinate(-27.5, 153))) + << QStringLiteral("sushi") + << QList() + << QPlaceSearchRequest::DistanceHint + << QPlaceReply::NoError + << AnyResults; + + QTest::newRow("check using lexical place name hint") + << static_cast(QGeoCircle(QGeoCoordinate(-27.5, 153))) + << QStringLiteral("sushi") + << QList() + << QPlaceSearchRequest::LexicalPlaceNameHint + << QPlaceReply::NoError + << AnyResults; +} + +void tst_QPlaceManagerNokia::searchResultFields() +{ + //check that using a distance relevancy hint will give a valid result + //even though it will be ignored by the backend. + QPlaceSearchRequest searchRequest; + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(-27.5, 153))); + searchRequest.setSearchTerm(QStringLiteral("sushi")); + + QPlaceSearchReply *reply = placeManager->search(searchRequest); + QSignalSpy spy(reply, SIGNAL(finished())); + QTRY_VERIFY_WITH_TIMEOUT(spy.count() == 1, Timeout); + QVERIFY(reply->results().count() > 0); + + //check that search results have basic data filled in + QPlaceResult result= reply->results().at(0); + QVERIFY(!result.title().isEmpty()); + QVERIFY(!result.icon().url().isEmpty()); + QVERIFY(!qIsNaN(result.distance())); + QVERIFY(!result.place().name().isEmpty()); + QVERIFY(result.place().location().coordinate().isValid()); + QVERIFY(!result.place().location().address().text().isEmpty()); + QVERIFY(!result.place().location().address().isTextGenerated()); + QVERIFY(result.place().categories().count() == 1);//only primary category retrieved on + //search + + //sponsored and ratings fields are optional and thus have not been explicitly tested. +} + +void tst_QPlaceManagerNokia::recommendations() +{ + QFETCH(QString, recommendationId); + QFETCH(QString, searchTerm); + QFETCH(QGeoShape, searchArea); + QFETCH(QList, categories); + QFETCH(QPlaceReply::Error, error); + + QPlaceSearchRequest searchRequest; + searchRequest.setRecommendationId(recommendationId); + searchRequest.setSearchTerm(searchTerm); + searchRequest.setSearchArea(searchArea); + searchRequest.setCategories(categories); + + QList results; + QVERIFY(doSearch(searchRequest, &results, error)); + + if (error == QPlaceReply::NoError) + QVERIFY(results.count() > 0); +} + +void tst_QPlaceManagerNokia::recommendations_data() +{ + QTest::addColumn("recommendationId"); + QTest::addColumn("searchTerm"); + QTest::addColumn("searchArea"); + QTest::addColumn >("categories"); + QTest::addColumn("error"); + + QPlaceCategory eatDrinkCat; + eatDrinkCat.setCategoryId(QStringLiteral("eat-drink")); + + QTest::newRow("search recommendations with valid id") + << QString(ValidKnownPlaceId) + << QString() + << QGeoShape() + << QList() + << QPlaceReply::NoError; + + QTest::newRow("search for recommendations with invalid id") + << QStringLiteral("does_not_exist_id") + << QString() + << QGeoShape() + << QList() + << QPlaceReply::PlaceDoesNotExistError; + + QTest::newRow("search for recommendations with id and search term") + << QString(ValidKnownPlaceId) + << QStringLiteral("sushi") + << QGeoShape() + << QList() + << QPlaceReply::BadArgumentError; + + QTest::newRow("search for recommendations with an id and category") + << QString(ValidKnownPlaceId) + << QString() + << QGeoShape() + << (QList() << eatDrinkCat) + << QPlaceReply::BadArgumentError; + + QTest::newRow("search for recommendations with id, search term and category") + << QString(ValidKnownPlaceId) + << QStringLiteral("sushi") + << QGeoShape() + << (QList() << eatDrinkCat) + << QPlaceReply::BadArgumentError; + + QTest::newRow("search for recommendations with an id and search area") + << QString(ValidKnownPlaceId) + << QString() + << static_cast(QGeoCircle(QGeoCoordinate(-27.5, 153))) + << QList() + << QPlaceReply::BadArgumentError; +} + +void tst_QPlaceManagerNokia::details() +{ + QSKIP("Fetching details from HERE place server always fails - QTBUG-44837"); + //fetch the details of a valid place + QPlace place; + QVERIFY(doFetchDetails(ValidKnownPlaceId, &place)); + QVERIFY(!place.name().isEmpty()); + QVERIFY(!place.icon().url().isEmpty()); + QStringList contactTypes = place.contactTypes(); + QVERIFY(!contactTypes.isEmpty()); + foreach (const QString &contactType, contactTypes) { + QList details = place.contactDetails(contactType); + QVERIFY(details.count() > 0); + foreach (const QPlaceContactDetail &detail, details) { + QVERIFY(!detail.label().isEmpty()); + QVERIFY(!detail.value().isEmpty()); + } + } + + QVERIFY(place.location().coordinate().isValid()); + QVERIFY(!place.location().address().isEmpty()); + QVERIFY(!place.location().address().text().isEmpty()); + QVERIFY(!place.location().address().isTextGenerated()); + + QVERIFY(place.ratings().average() >= 1 && place.ratings().average() <= 5); + QVERIFY(place.ratings().maximum() == 5); + QVERIFY(place.ratings().count() > 0); + + QVERIFY(place.categories().count() > 0); + foreach (const QPlaceCategory &category, place.categories()) { + QVERIFY(!category.name().isEmpty()); + QVERIFY(!category.categoryId().isEmpty()); + QVERIFY(!category.icon().url().isEmpty()); + } + + QVERIFY(!place.extendedAttributeTypes().isEmpty()); + QVERIFY(place.visibility() == QLocation::PublicVisibility); + QVERIFY(place.detailsFetched()); + + //attributions are optional and thus have not been explicitly tested. + + //fetch the details of a non-existent place + QVERIFY(doFetchDetails(QStringLiteral("does_not_exist"), &place, + QPlaceReply::PlaceDoesNotExistError)); +} + +void tst_QPlaceManagerNokia::categories() +{ + QVERIFY(doInitializeCategories()); + + QList categories = placeManager->childCategories(); + QVERIFY(categories.count() > 0); + foreach (const QPlaceCategory &category, categories) { + //check that we have valid fields + QVERIFY(!category.categoryId().isEmpty()); + QVERIFY(!category.name().isEmpty()); + QVERIFY(!category.icon().url().isEmpty()); + + //check we can retrieve the very same category by id + QCOMPARE(placeManager->category(category.categoryId()), category); + + //the here plugin supports a two-level level category tree + QVERIFY(placeManager->parentCategoryId(category.categoryId()).isEmpty()); + const QList childCats = + placeManager->childCategories(category.categoryId()); + if (!childCats.isEmpty()) { + foreach (const QPlaceCategory &child, childCats) { + // only two levels of categories hence 2.nd level has no further children + QVERIFY(placeManager->childCategories(child.categoryId()).isEmpty()); + QVERIFY(placeManager->parentCategoryId(child.categoryId()) == category.categoryId()); + } + } + } +} + +void tst_QPlaceManagerNokia::suggestions() +{ + QFETCH(QGeoShape, area); + QFETCH(QString, searchTerm); + QFETCH(QPlaceReply::Error, error); + QFETCH(ExpectedResults, expectedResults); + + QPlaceSearchRequest searchRequest; + searchRequest.setSearchArea(area); + searchRequest.setSearchTerm(searchTerm); + + QStringList results; + QVERIFY(doSearchSuggestions(searchRequest, &results, error)); + + if (expectedResults == NoResults) + QVERIFY(results.count() == 0); + else if (expectedResults == SomeResults) + QVERIFY(results.count() > 0); +} + + +void tst_QPlaceManagerNokia::suggestions_data() +{ + QTest::addColumn("area"); + QTest::addColumn("searchTerm"); + QTest::addColumn ("error"); + QTest::addColumn("expectedResults"); + + for (int i = 0; i < 3; i++) { + QByteArray suffix; + QGeoShape area; + if (i == 0) { + area = QGeoCircle(QGeoCoordinate(-27.5, 153)); + suffix = " (Circle - center only)"; + } else if (i == 1) { + area = QGeoCircle(QGeoCoordinate(-27.5, 153), 5000); + suffix = " (Circle - with radius specified)"; + } else { + area = QGeoRectangle(QGeoCoordinate(-26.5, 152), QGeoCoordinate(-28.5, 154)); + suffix = " (Rectangle)"; + } + + QByteArray dataTag = QByteArray("valid usage") + suffix; + QTest::newRow(dataTag) << area + << "sus" + << QPlaceReply::NoError + << SomeResults; + } + + //invalid areas and boundary testing + QList dataTags; + QList areas; + QList errors; + QList results; + commonAreas(&dataTags, &areas, &errors, &results); + + for (int i = 0; i < dataTags.count(); ++i) { + QTest::newRow(dataTags.at(i)) << areas.at(i) + << "sus" + << errors.at(i) + << results.at(i); + } + + QTest::newRow("no text") << static_cast(QGeoCircle(QGeoCoordinate(-27.5, 153))) + << QString() + << QPlaceReply::NoError + << NoResults; +} + +void tst_QPlaceManagerNokia::suggestionsMisc() +{ + //check providing a distance relevancy hint (should be ignored) + QPlaceSearchRequest searchRequest; + QStringList results; + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(-27.5, 153))); + searchRequest.setSearchTerm(QStringLiteral("sus")); + searchRequest.setRelevanceHint(QPlaceSearchRequest::DistanceHint); + QVERIFY(doSearchSuggestions(searchRequest, &results, QPlaceReply::NoError)); + QVERIFY(results.count() > 0); + searchRequest.clear(); + + //check porviding a lexical place name relevancy hint (should be ignored) + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(-27.5, 153))); + searchRequest.setSearchTerm(QStringLiteral("sus")); + searchRequest.setRelevanceHint(QPlaceSearchRequest::LexicalPlaceNameHint); + QVERIFY(doSearchSuggestions(searchRequest, &results, QPlaceReply::NoError)); + QVERIFY(results.count() > 0); + searchRequest.clear(); + + //check providing a category + QPlaceCategory eatDrinkCat; + eatDrinkCat.setCategoryId(QStringLiteral("eat-drink")); + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(-27.5, 153))); + searchRequest.setSearchTerm(QStringLiteral("sus")); + searchRequest.setCategory(eatDrinkCat); + QVERIFY(doSearchSuggestions(searchRequest, &results, QPlaceReply::BadArgumentError)); + QCOMPARE(results.count(), 0); + searchRequest.clear(); + + //check providing a recommendation id + searchRequest.setSearchArea(QGeoCircle(QGeoCoordinate(-27.5, 153))); + searchRequest.setSearchTerm(QStringLiteral("sus")); + searchRequest.setRecommendationId("id"); + QVERIFY(doSearchSuggestions(searchRequest, &results, QPlaceReply::BadArgumentError)); +} + +void tst_QPlaceManagerNokia::locale() +{ + //check that the defualt locale is set + QCOMPARE(placeManager->locales().count(), 1); + QCOMPARE(placeManager->locales().at(0), QLocale()); + + //check that we can set different locales for the categories + placeManager->setLocale(QLocale("en")); + QVERIFY(doInitializeCategories()); + QList enCategories = placeManager->childCategories(); + QVERIFY(enCategories.count() > 0); + + placeManager->setLocale(QLocale("fi")); + QVERIFY(doInitializeCategories()); + QList fiCategories = placeManager->childCategories(); + + foreach (const QPlaceCategory enCat, enCategories) { + foreach (const QPlaceCategory fiCat, fiCategories) { + if (enCat.categoryId() == fiCat.categoryId()) { + QVERIFY(fiCat.name() != enCat.name()); + QVERIFY(fiCat == placeManager->category(fiCat.categoryId())); + } + } + } + + // we are skipping the check below because we are requesting + // details for a place without a search before. This implies + // URL templating must be possible which the HERE place + // server refuses. + + QSKIP("remainder of test skipped due to QTBUG-44837"); + + //check that setting a locale will affect place detail fetches. + QPlace place; + placeManager->setLocale(QLocale("en")); + QVERIFY(doFetchDetails(ValidKnownPlaceId, + &place)); + QString englishName = place.name(); + placeManager->setLocale(QLocale("fr")); + QVERIFY(doFetchDetails(ValidKnownPlaceId, + &place)); + QVERIFY(englishName != place.name()); +} + +void tst_QPlaceManagerNokia::content() +{ + QFETCH(QPlaceContent::Type, type); + + //check fetching of content + QPlaceContentRequest request; + request.setContentType(type); + request.setPlaceId(ValidKnownPlaceId); + QPlaceContent::Collection results; + QVERIFY(doFetchContent(request, &results)); + + QVERIFY(results.count() > 0); + + QMapIterator iter(results); + while (iter.hasNext()) { + iter.next(); + switch (type) { + case (QPlaceContent::ImageType): { + QPlaceImage image = iter.value(); + QVERIFY(!image.url().isEmpty()); + break; + } case (QPlaceContent::ReviewType) : { + QPlaceReview review = iter.value(); + QVERIFY(!review.dateTime().isValid()); + QVERIFY(!review.text().isEmpty()); + QVERIFY(review.rating() >= 1 && review.rating() <= 5); + + //title and language fields are optional and thus have not been + //explicitly tested + break; + } case (QPlaceContent::EditorialType): { + QPlaceEditorial editorial = iter.value(); + QVERIFY(!editorial.text().isEmpty()); + + //The language field is optional and thus has not been + //explicitly tested. + break; + } default: + QFAIL("Unknown content type"); + } + } + + //check total count + QPlaceContentReply *contentReply = placeManager->getPlaceContent(request); + QSignalSpy contentSpy(contentReply, SIGNAL(finished())); + QTRY_VERIFY_WITH_TIMEOUT(contentSpy.count() ==1, Timeout); + QVERIFY(contentReply->totalCount() > 0); + + if (contentReply->totalCount() >= 2) { + //try testing with a limit + request.setLimit(1); + QPlaceContent::Collection newResults; + QVERIFY(doFetchContent(request, &newResults)); + QCOMPARE(newResults.count(), 1); + QCOMPARE(newResults.values().first(), results.value(0)); + } +} + +void tst_QPlaceManagerNokia::content_data() +{ + QTest::addColumn("type"); + + QTest::newRow("images") << QPlaceContent::ImageType; + QTest::newRow("reviews") << QPlaceContent::ReviewType; + QTest::newRow("editorials") << QPlaceContent::EditorialType; +} + +void tst_QPlaceManagerNokia::unsupportedFunctions() +{ + QPlace place; + place.setName(QStringLiteral("Brisbane")); + + QVERIFY(doSavePlace(place, QPlaceReply::UnsupportedError)); + QVERIFY(doRemovePlace(place, QPlaceReply::UnsupportedError)); + + QPlaceCategory category; + category.setName(QStringLiteral("Accommodation")); + QVERIFY(doSaveCategory(category, QPlaceReply::UnsupportedError)); + QVERIFY(doRemoveCategory(category, QPlaceReply::UnsupportedError)); +} + +void tst_QPlaceManagerNokia::commonAreas(QList *dataTags, + QList *areas, + QList *errors, + QList *results) +{ + Q_ASSERT(dataTags); + Q_ASSERT(areas); + dataTags->append("Unknown shape for search area"); + areas->append(QGeoShape()); + errors->append(QPlaceReply::BadArgumentError); + results->append(NoResults); + + dataTags->append("NaN coordinate"); + areas->append(QGeoCircle(QGeoCoordinate())); + errors->append(QPlaceReply::BadArgumentError); + results->append(NoResults); + + dataTags->append("Valid latitude (upper boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(90.0, 45))); + errors->append(QPlaceReply::NoError); + results->append(AnyResults); + + dataTags->append("Invalid latitude (upper boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(90.1, 45))); + errors->append(QPlaceReply::BadArgumentError); + results->append(NoResults); + + dataTags->append("Valid latitude (lower boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(-90.0, 45))); + errors->append(QPlaceReply::NoError); + results->append(AnyResults); + + dataTags->append("Invalid latitude (lower boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(-90.1, 45))); + errors->append(QPlaceReply::BadArgumentError); + results->append(NoResults); + + dataTags->append("Valid longitude (lower boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(-45, -180.0))); + errors->append(QPlaceReply::NoError); + results->append(AnyResults); + + dataTags->append("Invalid longitude (lower boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(-45, -180.1))); + errors->append(QPlaceReply::BadArgumentError); + results->append(NoResults); + + dataTags->append("Valid longitude (upper boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(-45, 180.0))); + errors->append(QPlaceReply::NoError); + results->append(AnyResults); + + dataTags->append("Invalid longitude (upper boundary)"); + areas->append(QGeoCircle(QGeoCoordinate(-45, 180.1))); + errors->append(QPlaceReply::BadArgumentError); + results->append(NoResults); + + dataTags->append("Invalid rectangular area"); + areas->append(QGeoRectangle(QGeoCoordinate(20,20), + QGeoCoordinate(30,10))); + errors->append(QPlaceReply::BadArgumentError); + results->append(NoResults); +} + +QTEST_GUILESS_MAIN(tst_QPlaceManagerNokia) + +#include "tst_places.moc" diff --git a/tests/auto/nokia_services/routing/error-no-route.xml b/tests/auto/nokia_services/routing/error-no-route.xml new file mode 100644 index 0000000..1e1560a --- /dev/null +++ b/tests/auto/nokia_services/routing/error-no-route.xml @@ -0,0 +1 @@ +

    NOROUTE: Request failed
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/invalid-response-half-way-through.xml b/tests/auto/nokia_services/routing/invalid-response-half-way-through.xml new file mode 100644 index 0000000..545954a --- /dev/null +++ b/tests/auto/nokia_services/routing/invalid-response-half-way-through.xml @@ -0,0 +1,150 @@ + + + + + 2012-04-26T14:49:24.451Z + 2012-04-26T14:47:00.025+0000 + 5094886 + 2012-04-26T14:47:02.481+0000 + 12015 + 2012-04-26T14:47:02.481+0000 + 857 + 2011Q3 + routeserver,9.2-2012.02.20-hotfix6.2.13.1 + 22 + routing-route-service,6.2.13.1 + + + REMvaQUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H9pqM_8V_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-82dwsAoDCQ + + -53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + + fastestNow + car + enabled + + 52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 52.5308685,13.3871498 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 52.5289383,13.3851404 52.5288315,13.3852901 52.5287399,13.3853998 52.5285416,13.3856297 52.5283089,13.38591 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 52.5271416,13.3869896 52.5270386,13.3870201 52.5262985,13.3871603 52.5262985,13.38727 52.5262489,13.3874302 52.5261917,13.3877001 52.5260506,13.3882999 52.5259895,13.38873 52.5257187,13.3898802 52.5249786,13.3928604 52.5246773,13.3941345 + + + 52.5315361 + 13.3846502 + + + 52.5246773 + 13.3941345 + + + + + -53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + 1271.0 + 273.9 + + + 52.5315361 + 13.3875332 + + Head toward Eichendorffstraße on Invalidenstraße. Go for 150 feet. + 5.2 + 48.0 + -53499799 + forward + + + + 52.5314484 + 13.3868303 + + Turn left onto Eichendorffstraße. Go for 400 feet. + 47.1 + 119.0 + -780236888 + left + + + + 52.5304298 + 13.3873901 + + Turn right onto Schlegelstraße. Go for 0.1 miles. + 55.9 + 223.0 + -53499914 + right + + + + 52.5293198 + 13.3846502 + + Turn left onto Chausseestraße. Go for 0.2 miles. + 68.0 + 286.0 + -749446557 + left + + + + 52.5271416 + 13.3869896 + + Continue on Friedrichstraße, Oranienburger Tor. Go for 300 feet. + 20.1 + 93.0 + -572708773 + forward + + + + 52.5262985 + 13.3871603 + + Turn left onto Oranienburger Straße. Go for 0.3 miles. + 77.6 + 502.0 + +812293299 + left diff --git a/tests/auto/nokia_services/routing/invalid-response-no-route-tag.xml b/tests/auto/nokia_services/routing/invalid-response-no-route-tag.xml new file mode 100644 index 0000000..322f1a1 --- /dev/null +++ b/tests/auto/nokia_services/routing/invalid-response-no-route-tag.xml @@ -0,0 +1,18 @@ + + + + + 2012-04-26T14:49:24.451Z + 2012-04-26T14:47:00.025+0000 + 5094886 + 2012-04-26T14:47:02.481+0000 + 12015 + 2012-04-26T14:47:02.481+0000 + 857 + 2011Q3 + routeserver,9.2-2012.02.20-hotfix6.2.13.1 + 22 + routing-route-service,6.2.13.1 + + + \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/invalid-response-trash.xml b/tests/auto/nokia_services/routing/invalid-response-trash.xml new file mode 100644 index 0000000000000000000000000000000000000000..ec6a3ae4c656164f56de5a2481e3625305f71a7f GIT binary patch literal 785 zcmZvay-Nc@5XI;2@@+7F7sg_(l&E-jBE|?B(O?ddh_*ICFM1jxTtcw0N}-l9l?Xxz z+E`flCs6N7MWtEfE66+7p3V+0;MXwGMz(NX5dSQd*`G5u*-q9yW0qy1qx+-* zXrQj{I)~X}78~k@k-*Q?nX!;CmlBI{+ArC;oLyjMaw&&PIn`jyBO)O_gO~UO6 zF)4La>Xy`qRQ_(fZbtTPWO; + + + + + + + 2012-04-26T14:49:24.451Z + + 2012-04-26T14:47:00.025+0000 + 5094886 + 2012-04-26T14:47:02.481+0000 + 12015 + 2012-04-26T14:47:02.481+0000 + 857 + 2011Q3 + routeserver,9.2-2012.02.20-hotfix6.2.13.1 + 22 + routing-route-service,6.2.13.1 + + + + + + + + + REMvaQUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H9pqM_8V_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-82dwsAoDCQ + + + -53499799 + + 52.5315361 + 13.3875332 + 32 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + + fastestNow + car + enabled + + 52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 52.5308685,13.3871498 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 52.5289383,13.3851404 52.5288315,13.3852901 52.5287399,13.3853998 52.5285416,13.3856297 52.5283089,13.38591 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 52.5271416,13.3869896 52.5270386,13.3870201 52.5262985,13.3871603 52.5262985,13.38727 52.5262489,13.3874302 52.5261917,13.3877001 52.5260506,13.3882999 52.5259895,13.38873 52.5257187,13.3898802 52.5249786,13.3928604 52.5246773,13.3941345 + + + 52.5315361 + 13.3846502 + + + 52.5246773 + 13.3941345 + + + + + + + + + + -53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + 1271.0 + 273.9 + + 232 + + 52.5315361 + 13.3875332 + + Head toward Eichendorffstraße on Invalidenstraße. Go for 150 feet. + 5.2 + 48.0 + -53499799 + forward + + + + 52.5314484 + 13.3868303 + + Turn left onto Eichendorffstraße. Go for 400 feet. + 47.1 + 119.0 + -780236888 + left + + + + 52.5304298 + 13.3873901 + + Turn right onto Schlegelstraße. Go for 0.1 miles. + 55.9 + 223.0 + -53499914 + right + + + + 52.5293198 + 13.3846502 + + Turn left onto Chausseestraße. Go for 0.2 miles. + 68.0 + 286.0 + -749446557 + left + + + + 52.5271416 + 13.3869896 + + Continue on Friedrichstraße, Oranienburger Tor. Go for 300 feet. + 20.1 + 93.0 + -572708773 + forward + + + + 52.5262985 + 13.3871603 + + Turn left onto Oranienburger Straße. Go for 0.3 miles. + 77.6 + 502.0 + +812293299 + left + + + + 52.5246773 + 13.3941345 + + Your destination on Oranienburger Straße is on the right. The trip takes 0.8 miles and 5 mins. + 0.0 + 0.0 + forward + + + -53499799 + 52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 + 48.0 + 13.89 + + 9.17 + 5.2 + 9.72 + 4.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Invalidenstraße +
    + + + -780236888 + 52.5314484,13.3868303 52.5308685,13.3871498 + 68.0 + + 5.0 + 13.6 + 6.94 + 9.8 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Eichendorffstraße +
    + + + -780236887 + 52.5308685,13.3871498 52.5304298,13.3873901 + 51.0 + + 5.28 + 9.7 + 6.94 + 7.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Eichendorffstraße +
    + + + -53499914 + 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 + 223.0 + + 5.28 + 42.3 + 6.94 + 32.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Schlegelstraße +
    + + + -749446557 + 52.5293198,13.3846502 52.5289383,13.3851404 + 53.0 + 13.89 + + 10.83 + 4.9 + 9.72 + 5.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763170 + 52.5289383,13.3851404 52.5288315,13.3852901 + 15.0 + 13.89 + + 10.83 + 1.4 + 9.72 + 1.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763169 + 52.5288315,13.3852901 52.5287399,13.3853998 + 12.0 + 13.89 + + 10.83 + 1.1 + 9.72 + 1.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763164 + 52.5287399,13.3853998 52.5285416,13.3856297 + 26.0 + 13.89 + + 10.83 + 2.4 + 9.72 + 2.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763163 + 52.5285416,13.3856297 52.5283089,13.38591 + 32.0 + 13.89 + + 10.83 + 3.0 + 9.72 + 3.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -749446551 + 52.5283089,13.38591 52.5281982,13.3860397 + 15.0 + 13.89 + + 10.83 + 1.4 + 9.72 + 1.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -572708771 + 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 + 93.0 + 13.89 + + 10.83 + 8.6 + 9.72 + 9.6 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -53500039 + 52.5274811,13.3867598 52.5271416,13.3869896 + 40.0 + 13.89 + + 10.83 + 3.7 + 9.72 + 4.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -572708773 + 52.5271416,13.3869896 52.5270386,13.3870201 + 11.0 + 13.89 + + 10.28 + 1.1 + 9.72 + 1.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Friedrichstraße, Oranienburger Tor +
    + + + -572708772 + 52.5270386,13.3870201 52.5262985,13.3871603 + 82.0 + 13.89 + + 10.28 + 8.0 + 9.72 + 8.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Friedrichstraße +
    + + + +812293299 + 52.5262985,13.3871603 52.5262985,13.38727 + 7.0 + 13.89 + + 3.61 + 1.9 + 9.72 + 0.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -733054082 + 52.5262985,13.38727 52.5262489,13.3874302 + 12.0 + 13.89 + + 11.39 + 1.1 + 9.72 + 1.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -733054081 + 52.5262489,13.3874302 52.5261917,13.3877001 + 19.0 + 13.89 + + 11.39 + 1.7 + 9.72 + 2.0 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680644 + 52.5261917,13.3877001 52.5260506,13.3882999 + 43.0 + 13.89 + + 10.0 + 4.3 + 9.72 + 4.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680642 + 52.5260506,13.3882999 52.5259895,13.38873 + 29.0 + 13.89 + + 11.39 + 2.5 + 9.72 + 3.0 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680640 + 52.5259895,13.38873 52.5257187,13.3898802 + 83.0 + 13.89 + + 11.11 + 7.5 + 9.72 + 8.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501098 + 52.5257187,13.3898802 52.5249786,13.3928604 + 217.0 + 13.89 + + 8.33 + 26.0 + 9.72 + 22.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501113 + 52.5249786,13.3928604 52.5246773,13.3941345 + 92.0 + 13.89 + + 8.61 + 10.7 + 9.72 + 9.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + +
    + + 1271.0 + 1271.0 + 243.0 + + +
    +
    +
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/multiple-routes-in-response.xml b/tests/auto/nokia_services/routing/multiple-routes-in-response.xml new file mode 100644 index 0000000..12a4d21 --- /dev/null +++ b/tests/auto/nokia_services/routing/multiple-routes-in-response.xml @@ -0,0 +1 @@ +2012-04-26T15:01:07.170Z2012-04-26T14:59:00.073+000050949822012-04-26T14:59:02.647+0000122232012-04-26T14:59:02.647+00007032011Q3routeserver,9.2-2012.02.20-hotfix6.2.13.120routing-route-service,6.2.13.1REMvFQUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H-XVzADV_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-83dwYQCu+5349979952.531536113.387533252.53154313.387532stopOver-5350111352.524677313.394134552.52464613.394128stopOversceniccar52.5315361,13.3875332 52.5315819,13.3880997 52.5316582,13.3889303 52.5317612,13.3896999 52.5316315,13.3899002 52.5302505,13.3916502 52.5300598,13.3919001 52.5286217,13.3937197 52.5285416,13.3938799 52.5282288,13.3924398 52.5276604,13.3927898 52.5274887,13.39293 52.5264091,13.3935404 52.5262985,13.3935604 52.5254402,13.3931103 52.5249786,13.3928604 52.5246773,13.394134552.531761213.387533252.524677313.3941345+5349979952.531536113.387533252.53154313.387532stopOver-5350111352.524677313.394134552.52464613.394128stopOver1177.0237.752.531536113.3875332Head toward Borsigstraße on Invalidenstraße. Go for 500 feet.15.2148.0+53499799forward52.531761213.3896999Turn right onto Gartenstraße. Go for 0.3 miles.90.8454.0-811854188right52.528541613.3938799Turn right onto Torstraße. Go for 350 feet.28.4103.0-53499981right52.528228813.3924398Turn left onto Tucholskystraße. Go for 0.2 miles.72.4380.0-53500024left52.524978613.3928604Turn left onto Oranienburger Straße. Go for 300 feet.30.992.0-53501113left52.524677313.3941345Your destination on Oranienburger Straße is on the right. The trip takes 0.7 miles and 4 mins.0.00.0forward+5349979952.5315361,13.3875332 52.5315819,13.388099738.013.897.774.99.723.9
    DEBerlinBerlinBerlinMitteInvalidenstraße
    +5349978852.5315819,13.3880997 52.5316582,13.3889303 52.5317612,13.3896999110.013.897.7714.19.7211.3
    DEBerlinBerlinBerlinMitteInvalidenstraße
    -81185418852.5317612,13.3896999 52.5316315,13.389900219.05.03.86.942.7
    DEBerlinBerlinBerlinMitteGartenstraße
    -81185418752.5316315,13.3899002 52.5302505,13.3916502193.05.2836.66.9427.8
    DEBerlinBerlinBerlinMitteGartenstraße
    -5349987852.5302505,13.3916502 52.5300598,13.391900127.05.285.16.943.9
    DEBerlinBerlinBerlinMitteGartenstraße
    -5349996052.5300598,13.3919001 52.5286217,13.3937197 52.5285416,13.3938799215.05.2840.76.9431.0
    DEBerlinBerlinBerlinMitteGartenstraße
    -5349998152.5285416,13.3938799 52.5282288,13.3924398103.013.898.3312.46.9414.8
    DEBerlinBerlinBerlinMitteTorstraße
    -5350002452.5282288,13.3924398 52.5276604,13.3927898 52.5274887,13.3929388.04.7218.69.729.1
    DEBerlinBerlinBerlinMitteTucholskystraße
    -5350009152.5274887,13.39293 52.5264091,13.3935404126.04.7226.79.7213.0
    DEBerlinBerlinBerlinMitteTucholskystraße
    -84490623952.5264091,13.3935404 52.5262985,13.3935604 52.5254402,13.3931103112.04.1726.99.7211.5
    DEBerlinBerlinBerlinMitteTucholskystraße
    -84490623852.5254402,13.3931103 52.5249786,13.392860454.03.8913.99.725.6
    DEBerlinBerlinBerlinMitteTucholskystraße
    -5350111352.5249786,13.3928604 52.5246773,13.394134592.013.898.6110.79.729.5
    DEBerlinBerlinBerlinMitteOranienburger Straße
    1177.0309.0237.0
    REMvaQUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H9pqM_8V_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-82dwsAoDCQ-5349979952.531536113.387533252.53154313.387532stopOver-5350111352.524677313.394134552.52464613.394128stopOverfastestNowcarenabled52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 52.5308685,13.3871498 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 52.5289383,13.3851404 52.5288315,13.3852901 52.5287399,13.3853998 52.5285416,13.3856297 52.5283089,13.38591 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 52.5271416,13.3869896 52.5270386,13.3870201 52.5262985,13.3871603 52.5262985,13.38727 52.5262489,13.3874302 52.5261917,13.3877001 52.5260506,13.3882999 52.5259895,13.38873 52.5257187,13.3898802 52.5249786,13.3928604 52.5246773,13.394134552.531536113.384650252.524677313.3941345-5349979952.531536113.387533252.53154313.387532stopOver-5350111352.524677313.394134552.52464613.394128stopOver1271.0270.052.531536113.3875332Head toward Eichendorffstraße on Invalidenstraße. Go for 150 feet.5.248.0-53499799forward52.531448413.3868303Turn left onto Eichendorffstraße. Go for 400 feet.47.1119.0-780236888left52.530429813.3873901Turn right onto Schlegelstraße. Go for 0.1 miles.55.9223.0-53499914right52.529319813.3846502Turn left onto Chausseestraße. Go for 0.2 miles.64.9286.0-749446557left52.527141613.3869896Continue on Friedrichstraße, Oranienburger Tor. Go for 300 feet.19.393.0-572708773forward52.526298513.3871603Turn left onto Oranienburger Straße. Go for 0.3 miles.77.6502.0+812293299left52.524677313.3941345Your destination on Oranienburger Straße is on the right. The trip takes 0.8 miles and 5 mins.0.00.0forward-5349979952.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.386830348.013.899.175.29.724.9
    DEBerlinBerlinBerlinMitteInvalidenstraße
    -78023688852.5314484,13.3868303 52.5308685,13.387149868.05.013.66.949.8
    DEBerlinBerlinBerlinMitteEichendorffstraße
    -78023688752.5308685,13.3871498 52.5304298,13.387390151.05.289.76.947.3
    DEBerlinBerlinBerlinMitteEichendorffstraße
    -5349991452.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502223.05.2842.36.9432.1
    DEBerlinBerlinBerlinMitteSchlegelstraße
    -74944655752.5293198,13.3846502 52.5289383,13.385140453.013.8910.834.99.725.5
    DEBerlinBerlinBerlinMitteChausseestraße
    -78176317052.5289383,13.3851404 52.5288315,13.385290115.013.8910.831.49.721.5
    DEBerlinBerlinBerlinMitteChausseestraße
    -78176316952.5288315,13.3852901 52.5287399,13.385399812.013.8910.831.19.721.2
    DEBerlinBerlinBerlinMitteChausseestraße
    -78176316452.5287399,13.3853998 52.5285416,13.385629726.013.8910.832.49.722.7
    DEBerlinBerlinBerlinMitteChausseestraße
    -78176316352.5285416,13.3856297 52.5283089,13.3859132.013.8910.833.09.723.3
    DEBerlinBerlinBerlinMitteChausseestraße
    -74944655152.5283089,13.38591 52.5281982,13.386039715.013.8910.831.49.721.5
    DEBerlinBerlinBerlinMitteChausseestraße
    -57270877152.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.386759893.013.8910.838.69.729.6
    DEBerlinBerlinBerlinMitteChausseestraße
    -5350003952.5274811,13.3867598 52.5271416,13.386989640.013.8910.833.79.724.1
    DEBerlinBerlinBerlinMitteChausseestraße
    -57270877352.5271416,13.3869896 52.5270386,13.387020111.013.8910.281.19.721.1
    DEBerlinBerlinBerlinMitteFriedrichstraße, Oranienburger Tor
    -57270877252.5270386,13.3870201 52.5262985,13.387160382.013.8910.288.09.728.4
    DEBerlinBerlinBerlinMitteFriedrichstraße
    +81229329952.5262985,13.3871603 52.5262985,13.387277.013.893.611.99.720.7
    DEBerlinBerlinBerlinMitteOranienburger Straße
    -73305408252.5262985,13.38727 52.5262489,13.387430212.013.8911.391.19.721.2
    DEBerlinBerlinBerlinMitteOranienburger Straße
    -73305408152.5262489,13.3874302 52.5261917,13.387700119.013.8911.391.79.722.0
    DEBerlinBerlinBerlinMitteOranienburger Straße
    -57268064452.5261917,13.3877001 52.5260506,13.388299943.013.8910.04.39.724.4
    DEBerlinBerlinBerlinMitteOranienburger Straße
    -57268064252.5260506,13.3882999 52.5259895,13.3887329.013.8911.392.59.723.0
    DEBerlinBerlinBerlinMitteOranienburger Straße
    -57268064052.5259895,13.38873 52.5257187,13.389880283.013.8911.117.59.728.5
    DEBerlinBerlinBerlinMitteOranienburger Straße
    -5350109852.5257187,13.3898802 52.5249786,13.3928604217.013.898.3326.09.7222.3
    DEBerlinBerlinBerlinMitteOranienburger Straße
    -5350111352.5249786,13.3928604 52.5246773,13.394134592.013.898.6110.79.729.5
    DEBerlinBerlinBerlinMitteOranienburger Straße
    1271.0243.0
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/optim-fastest.xml b/tests/auto/nokia_services/routing/optim-fastest.xml new file mode 100644 index 0000000..43a7a77 --- /dev/null +++ b/tests/auto/nokia_services/routing/optim-fastest.xml @@ -0,0 +1,628 @@ + + + + + 2012-04-26T14:49:24.451Z + 2012-04-26T14:47:00.025+0000 + 5094886 + 2012-04-26T14:47:02.481+0000 + 12015 + 2012-04-26T14:47:02.481+0000 + 857 + 2011Q3 + routeserver,9.2-2012.02.20-hotfix6.2.13.1 + 22 + routing-route-service,6.2.13.1 + + + REMvaQUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H9pqM_8V_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-82dwsAoDCQ + + -53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + + fastestNow + car + enabled + + 52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 52.5308685,13.3871498 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 52.5289383,13.3851404 52.5288315,13.3852901 52.5287399,13.3853998 52.5285416,13.3856297 52.5283089,13.38591 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 52.5271416,13.3869896 52.5270386,13.3870201 52.5262985,13.3871603 52.5262985,13.38727 52.5262489,13.3874302 52.5261917,13.3877001 52.5260506,13.3882999 52.5259895,13.38873 52.5257187,13.3898802 52.5249786,13.3928604 52.5246773,13.3941345 + + + 52.5315361 + 13.3846502 + + + 52.5246773 + 13.3941345 + + + + + -53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + 1271.0 + 273.9 + + + 52.5315361 + 13.3875332 + + Head toward Eichendorffstraße on Invalidenstraße. Go for 150 feet. + 5.2 + 48.0 + -53499799 + forward + + + + 52.5314484 + 13.3868303 + + Turn left onto Eichendorffstraße. Go for 400 feet. + 47.1 + 119.0 + -780236888 + left + + + + 52.5304298 + 13.3873901 + + Turn right onto Schlegelstraße. Go for 0.1 miles. + 55.9 + 223.0 + -53499914 + right + + + + 52.5293198 + 13.3846502 + + Turn left onto Chausseestraße. Go for 0.2 miles. + 68.0 + 286.0 + -749446557 + left + + + + 52.5271416 + 13.3869896 + + Continue on Friedrichstraße, Oranienburger Tor. Go for 300 feet. + 20.1 + 93.0 + -572708773 + forward + + + + 52.5262985 + 13.3871603 + + Turn left onto Oranienburger Straße. Go for 0.3 miles. + 77.6 + 502.0 + +812293299 + left + + + + 52.5246773 + 13.3941345 + + Your destination on Oranienburger Straße is on the right. The trip takes 0.8 miles and 5 mins. + 0.0 + 0.0 + forward + + + -53499799 + 52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 + 48.0 + 13.89 + + 9.17 + 5.2 + 9.72 + 4.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Invalidenstraße +
    + + + -780236888 + 52.5314484,13.3868303 52.5308685,13.3871498 + 68.0 + + 5.0 + 13.6 + 6.94 + 9.8 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Eichendorffstraße +
    + + + -780236887 + 52.5308685,13.3871498 52.5304298,13.3873901 + 51.0 + + 5.28 + 9.7 + 6.94 + 7.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Eichendorffstraße +
    + + + -53499914 + 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 + 223.0 + + 5.28 + 42.3 + 6.94 + 32.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Schlegelstraße +
    + + + -749446557 + 52.5293198,13.3846502 52.5289383,13.3851404 + 53.0 + 13.89 + + 10.83 + 4.9 + 9.72 + 5.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763170 + 52.5289383,13.3851404 52.5288315,13.3852901 + 15.0 + 13.89 + + 10.83 + 1.4 + 9.72 + 1.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763169 + 52.5288315,13.3852901 52.5287399,13.3853998 + 12.0 + 13.89 + + 10.83 + 1.1 + 9.72 + 1.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763164 + 52.5287399,13.3853998 52.5285416,13.3856297 + 26.0 + 13.89 + + 10.83 + 2.4 + 9.72 + 2.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763163 + 52.5285416,13.3856297 52.5283089,13.38591 + 32.0 + 13.89 + + 10.83 + 3.0 + 9.72 + 3.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -749446551 + 52.5283089,13.38591 52.5281982,13.3860397 + 15.0 + 13.89 + + 10.83 + 1.4 + 9.72 + 1.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -572708771 + 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 + 93.0 + 13.89 + + 10.83 + 8.6 + 9.72 + 9.6 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -53500039 + 52.5274811,13.3867598 52.5271416,13.3869896 + 40.0 + 13.89 + + 10.83 + 3.7 + 9.72 + 4.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -572708773 + 52.5271416,13.3869896 52.5270386,13.3870201 + 11.0 + 13.89 + + 10.28 + 1.1 + 9.72 + 1.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Friedrichstraße, Oranienburger Tor +
    + + + -572708772 + 52.5270386,13.3870201 52.5262985,13.3871603 + 82.0 + 13.89 + + 10.28 + 8.0 + 9.72 + 8.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Friedrichstraße +
    + + + +812293299 + 52.5262985,13.3871603 52.5262985,13.38727 + 7.0 + 13.89 + + 3.61 + 1.9 + 9.72 + 0.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -733054082 + 52.5262985,13.38727 52.5262489,13.3874302 + 12.0 + 13.89 + + 11.39 + 1.1 + 9.72 + 1.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -733054081 + 52.5262489,13.3874302 52.5261917,13.3877001 + 19.0 + 13.89 + + 11.39 + 1.7 + 9.72 + 2.0 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680644 + 52.5261917,13.3877001 52.5260506,13.3882999 + 43.0 + 13.89 + + 10.0 + 4.3 + 9.72 + 4.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680642 + 52.5260506,13.3882999 52.5259895,13.38873 + 29.0 + 13.89 + + 11.39 + 2.5 + 9.72 + 3.0 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680640 + 52.5259895,13.38873 52.5257187,13.3898802 + 83.0 + 13.89 + + 11.11 + 7.5 + 9.72 + 8.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501098 + 52.5257187,13.3898802 52.5249786,13.3928604 + 217.0 + 13.89 + + 8.33 + 26.0 + 9.72 + 22.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501113 + 52.5249786,13.3928604 52.5246773,13.3941345 + 92.0 + 13.89 + + 8.61 + 10.7 + 9.72 + 9.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + +
    + + 1271.0 + 243.0 + +
    +
    +
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/optim-shortest.xml b/tests/auto/nokia_services/routing/optim-shortest.xml new file mode 100644 index 0000000..5aa8679 --- /dev/null +++ b/tests/auto/nokia_services/routing/optim-shortest.xml @@ -0,0 +1 @@ +2012-04-26T14:57:30.304Z2012-04-26T14:56:00.027+000050949832012-04-26T14:56:02.406+0000122232012-04-26T14:56:02.406+00005062011Q3routeserver,9.2-2012.02.20-hotfix6.2.13.141routing-route-service,6.2.13.1REMvFQUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H-XVzADV_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-83dwYQCu+5349979952.531536113.387533252.53154313.387532stopOver-5350111352.524677313.394134552.52464613.394128stopOvershortestcar52.5315361,13.3875332 52.5315819,13.3880997 52.5316582,13.3889303 52.5317612,13.3896999 52.5316315,13.3899002 52.5302505,13.3916502 52.5300598,13.3919001 52.5286217,13.3937197 52.5285416,13.3938799 52.5282288,13.3924398 52.5276604,13.3927898 52.5274887,13.39293 52.5264091,13.3935404 52.5262985,13.3935604 52.5254402,13.3931103 52.5249786,13.3928604 52.5246773,13.394134552.531761213.387533252.524677313.3941345+5349979952.531536113.387533252.53154313.387532stopOver-5350111352.524677313.394134552.52464613.394128stopOver1177.0237.752.531536113.3875332Head toward Borsigstraße on Invalidenstraße. Go for 500 feet.15.2148.0+53499799forward52.531761213.3896999Turn right onto Gartenstraße. Go for 0.3 miles.90.8454.0-811854188right52.528541613.3938799Turn right onto Torstraße. Go for 350 feet.28.4103.0-53499981right52.528228813.3924398Turn left onto Tucholskystraße. Go for 0.2 miles.72.4380.0-53500024left52.524978613.3928604Turn left onto Oranienburger Straße. Go for 300 feet.30.992.0-53501113left52.524677313.3941345Your destination on Oranienburger Straße is on the right. The trip takes 0.7 miles and 4 mins.0.00.0forward+5349979952.5315361,13.3875332 52.5315819,13.388099738.013.897.774.99.723.9
    DEBerlinBerlinBerlinMitteInvalidenstraße
    +5349978852.5315819,13.3880997 52.5316582,13.3889303 52.5317612,13.3896999110.013.897.7714.19.7211.3
    DEBerlinBerlinBerlinMitteInvalidenstraße
    -81185418852.5317612,13.3896999 52.5316315,13.389900219.05.03.86.942.7
    DEBerlinBerlinBerlinMitteGartenstraße
    -81185418752.5316315,13.3899002 52.5302505,13.3916502193.05.2836.66.9427.8
    DEBerlinBerlinBerlinMitteGartenstraße
    -5349987852.5302505,13.3916502 52.5300598,13.391900127.05.285.16.943.9
    DEBerlinBerlinBerlinMitteGartenstraße
    -5349996052.5300598,13.3919001 52.5286217,13.3937197 52.5285416,13.3938799215.05.2840.76.9431.0
    DEBerlinBerlinBerlinMitteGartenstraße
    -5349998152.5285416,13.3938799 52.5282288,13.3924398103.013.898.3312.46.9414.8
    DEBerlinBerlinBerlinMitteTorstraße
    -5350002452.5282288,13.3924398 52.5276604,13.3927898 52.5274887,13.3929388.04.7218.69.729.1
    DEBerlinBerlinBerlinMitteTucholskystraße
    -5350009152.5274887,13.39293 52.5264091,13.3935404126.04.7226.79.7213.0
    DEBerlinBerlinBerlinMitteTucholskystraße
    -84490623952.5264091,13.3935404 52.5262985,13.3935604 52.5254402,13.3931103112.04.1726.99.7211.5
    DEBerlinBerlinBerlinMitteTucholskystraße
    -84490623852.5254402,13.3931103 52.5249786,13.392860454.03.8913.99.725.6
    DEBerlinBerlinBerlinMitteTucholskystraße
    -5350111352.5249786,13.3928604 52.5246773,13.394134592.013.898.6110.79.729.5
    DEBerlinBerlinBerlinMitteOranienburger Straße
    1177.0309.0237.0
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/routing.pro b/tests/auto/nokia_services/routing/routing.pro new file mode 100644 index 0000000..f0ec3d6 --- /dev/null +++ b/tests/auto/nokia_services/routing/routing.pro @@ -0,0 +1,13 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_nokia_routing + +QT += network location testlib +INCLUDEPATH += $$PWD/../../../../src/plugins/geoservices/nokia + +HEADERS += $$PWD/../../../../src/plugins/geoservices/nokia/qgeonetworkaccessmanager.h +SOURCES += tst_routing.cpp + +OTHER_FILES += *.xml + +TESTDATA = $$OTHER_FILES diff --git a/tests/auto/nokia_services/routing/travelmode-car.xml b/tests/auto/nokia_services/routing/travelmode-car.xml new file mode 100644 index 0000000..43a7a77 --- /dev/null +++ b/tests/auto/nokia_services/routing/travelmode-car.xml @@ -0,0 +1,628 @@ + + + + + 2012-04-26T14:49:24.451Z + 2012-04-26T14:47:00.025+0000 + 5094886 + 2012-04-26T14:47:02.481+0000 + 12015 + 2012-04-26T14:47:02.481+0000 + 857 + 2011Q3 + routeserver,9.2-2012.02.20-hotfix6.2.13.1 + 22 + routing-route-service,6.2.13.1 + + + REMvaQUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H9pqM_8V_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-82dwsAoDCQ + + -53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + + fastestNow + car + enabled + + 52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 52.5308685,13.3871498 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 52.5289383,13.3851404 52.5288315,13.3852901 52.5287399,13.3853998 52.5285416,13.3856297 52.5283089,13.38591 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 52.5271416,13.3869896 52.5270386,13.3870201 52.5262985,13.3871603 52.5262985,13.38727 52.5262489,13.3874302 52.5261917,13.3877001 52.5260506,13.3882999 52.5259895,13.38873 52.5257187,13.3898802 52.5249786,13.3928604 52.5246773,13.3941345 + + + 52.5315361 + 13.3846502 + + + 52.5246773 + 13.3941345 + + + + + -53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + 1271.0 + 273.9 + + + 52.5315361 + 13.3875332 + + Head toward Eichendorffstraße on Invalidenstraße. Go for 150 feet. + 5.2 + 48.0 + -53499799 + forward + + + + 52.5314484 + 13.3868303 + + Turn left onto Eichendorffstraße. Go for 400 feet. + 47.1 + 119.0 + -780236888 + left + + + + 52.5304298 + 13.3873901 + + Turn right onto Schlegelstraße. Go for 0.1 miles. + 55.9 + 223.0 + -53499914 + right + + + + 52.5293198 + 13.3846502 + + Turn left onto Chausseestraße. Go for 0.2 miles. + 68.0 + 286.0 + -749446557 + left + + + + 52.5271416 + 13.3869896 + + Continue on Friedrichstraße, Oranienburger Tor. Go for 300 feet. + 20.1 + 93.0 + -572708773 + forward + + + + 52.5262985 + 13.3871603 + + Turn left onto Oranienburger Straße. Go for 0.3 miles. + 77.6 + 502.0 + +812293299 + left + + + + 52.5246773 + 13.3941345 + + Your destination on Oranienburger Straße is on the right. The trip takes 0.8 miles and 5 mins. + 0.0 + 0.0 + forward + + + -53499799 + 52.5315361,13.3875332 52.5315094,13.3872204 52.5314484,13.3868303 + 48.0 + 13.89 + + 9.17 + 5.2 + 9.72 + 4.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Invalidenstraße +
    + + + -780236888 + 52.5314484,13.3868303 52.5308685,13.3871498 + 68.0 + + 5.0 + 13.6 + 6.94 + 9.8 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Eichendorffstraße +
    + + + -780236887 + 52.5308685,13.3871498 52.5304298,13.3873901 + 51.0 + + 5.28 + 9.7 + 6.94 + 7.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Eichendorffstraße +
    + + + -53499914 + 52.5304298,13.3873901 52.5303993,13.3872299 52.5303612,13.3871202 52.5293198,13.3846502 + 223.0 + + 5.28 + 42.3 + 6.94 + 32.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Schlegelstraße +
    + + + -749446557 + 52.5293198,13.3846502 52.5289383,13.3851404 + 53.0 + 13.89 + + 10.83 + 4.9 + 9.72 + 5.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763170 + 52.5289383,13.3851404 52.5288315,13.3852901 + 15.0 + 13.89 + + 10.83 + 1.4 + 9.72 + 1.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763169 + 52.5288315,13.3852901 52.5287399,13.3853998 + 12.0 + 13.89 + + 10.83 + 1.1 + 9.72 + 1.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763164 + 52.5287399,13.3853998 52.5285416,13.3856297 + 26.0 + 13.89 + + 10.83 + 2.4 + 9.72 + 2.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -781763163 + 52.5285416,13.3856297 52.5283089,13.38591 + 32.0 + 13.89 + + 10.83 + 3.0 + 9.72 + 3.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -749446551 + 52.5283089,13.38591 52.5281982,13.3860397 + 15.0 + 13.89 + + 10.83 + 1.4 + 9.72 + 1.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -572708771 + 52.5281982,13.3860397 52.5280991,13.3861103 52.5274811,13.3867598 + 93.0 + 13.89 + + 10.83 + 8.6 + 9.72 + 9.6 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -53500039 + 52.5274811,13.3867598 52.5271416,13.3869896 + 40.0 + 13.89 + + 10.83 + 3.7 + 9.72 + 4.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Chausseestraße +
    + + + -572708773 + 52.5271416,13.3869896 52.5270386,13.3870201 + 11.0 + 13.89 + + 10.28 + 1.1 + 9.72 + 1.1 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Friedrichstraße, Oranienburger Tor +
    + + + -572708772 + 52.5270386,13.3870201 52.5262985,13.3871603 + 82.0 + 13.89 + + 10.28 + 8.0 + 9.72 + 8.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Friedrichstraße +
    + + + +812293299 + 52.5262985,13.3871603 52.5262985,13.38727 + 7.0 + 13.89 + + 3.61 + 1.9 + 9.72 + 0.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -733054082 + 52.5262985,13.38727 52.5262489,13.3874302 + 12.0 + 13.89 + + 11.39 + 1.1 + 9.72 + 1.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -733054081 + 52.5262489,13.3874302 52.5261917,13.3877001 + 19.0 + 13.89 + + 11.39 + 1.7 + 9.72 + 2.0 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680644 + 52.5261917,13.3877001 52.5260506,13.3882999 + 43.0 + 13.89 + + 10.0 + 4.3 + 9.72 + 4.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680642 + 52.5260506,13.3882999 52.5259895,13.38873 + 29.0 + 13.89 + + 11.39 + 2.5 + 9.72 + 3.0 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -572680640 + 52.5259895,13.38873 52.5257187,13.3898802 + 83.0 + 13.89 + + 11.11 + 7.5 + 9.72 + 8.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501098 + 52.5257187,13.3898802 52.5249786,13.3928604 + 217.0 + 13.89 + + 8.33 + 26.0 + 9.72 + 22.3 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501113 + 52.5249786,13.3928604 52.5246773,13.3941345 + 92.0 + 13.89 + + 8.61 + 10.7 + 9.72 + 9.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + +
    + + 1271.0 + 243.0 + +
    +
    +
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/travelmode-pedestrian.xml b/tests/auto/nokia_services/routing/travelmode-pedestrian.xml new file mode 100644 index 0000000..56a79e3 --- /dev/null +++ b/tests/auto/nokia_services/routing/travelmode-pedestrian.xml @@ -0,0 +1,798 @@ + + + + + 2012-04-26T14:48:17.998Z + 2012-04-26T14:47:00.026+0000 + 5094886 + 2012-04-26T14:47:02.413+0000 + 12015 + 2012-04-26T14:47:02.413+0000 + 857 + 2011Q3 + routeserver,9.2-2012.02.20-hotfix6.2.13.1 + 96 + routing-route-service,6.2.13.1 + + + REMvSAkAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H-XVzADV_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-84fTC85nlF5wPmO1JsEZpheczwc8AOrpMz9xhQUzcABtrGd-FJ0-86Po9JnfAAHBo80GTA + + +53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + + fastestNow + pedestrian + enabled + + 52.5314903,13.3875389 52.5315857,13.3881445 52.5315475,13.3880892 52.5310287,13.388505 52.5309753,13.3885479 52.5307274,13.3887367 52.530674,13.3887777 52.5297775,13.3894777 52.5297241,13.3895216 52.5280571,13.3913355 52.5279846,13.3914194 52.5281792,13.3924217 52.5276413,13.392765 52.527504,13.3928776 52.5274544,13.3929157 52.5274773,13.3929968 52.5264435,13.3935556 52.5263863,13.3935757 52.5262947,13.3935919 52.525425,13.393137 52.5249977,13.3929052 52.5249405,13.3928967 52.5246468,13.3941231 + + + 52.5315857 + 13.3875389 + + + 52.5246468 + 13.3941231 + + + + + +53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + 1107.0 + 798.2 + + + 52.5315361 + 13.3875332 + + Head toward Borsigstraße on Invalidenstraße. Go for 100 feet. + 27.4 + 38.0 + +53499799 + forward + + + + 52.5315819 + 13.3880997 + + Turn right and use the crosswalk. + 6.0 + 8.0 + -811853913 + right + + + + 52.5315819 + 13.3880997 + + Continue on Borsigstraße. Go for 0.3 miles. + 343.4 + 476.0 + -811853915 + forward + + + + 52.5280304 + 13.3914099 + + Cross Torstraße. + 5.0 + 8.0 + -833290988 + forward + + + + 52.5280304 + 13.3914099 + + Turn left onto Torstraße. Go for 250 feet. + 52.6 + 73.0 + +53499991 + left + + + + 52.5282288 + 13.3924398 + + Turn right onto Tucholskystraße. Go for 300 feet. + 63.4 + 88.0 + -53500024 + right + + + + 52.5274887 + 13.39293 + + Cross Linienstraße, Tucholskystraße. + 12.0 + 16.0 + -53520166 + forward + + + + 52.5274887 + 13.39293 + + Turn right onto Tucholskystraße. Go for 0.2 miles. + 216.2 + 300.0 + -53500091 + right + + + + 52.5249786 + 13.3928604 + + Cross Oranienburger Straße. + 6.0 + 8.0 + -53501113 + forward + + + + 52.5249786 + 13.3928604 + + Turn left onto Oranienburger Straße. Go for 300 feet. + 66.2 + 92.0 + -53501113 + left + + + + 52.5246773 + 13.3941345 + + Your destination on Oranienburger Straße is on the right. The trip takes 0.7 miles and 13 mins. + 0.0 + 0.0 + forward + + + +53499799 + 52.5314903,13.3875389 52.5315857,13.3881445 + 38.0 + 13.89 + + 7.77 + 4.9 + 1.39 + 27.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Invalidenstraße +
    + + + -811853913 + 52.5315857,13.3881445 52.5315475,13.3880892 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte +
    + + + +811853913 + 52.5315475,13.3880892 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte +
    + + + -811853915 + 52.5315475,13.3880892 52.5310287,13.388505 + 70.0 + + 1.39 + 50.4 + 1.39 + 50.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Borsigstraße +
    + + + -838257237 + 52.5310287,13.388505 52.5309753,13.3885479 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte +
    + + + +838257237 + 52.5309753,13.3885479 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte +
    + + + -811853914 + 52.5309753,13.3885479 52.5307274,13.3887367 + 36.0 + + 1.39 + 25.9 + 1.39 + 25.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Borsigstraße +
    + + + -53499858 + 52.5307274,13.3887367 52.530674,13.3887777 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Schlegelstraße +
    + + + +53499858 + 52.530674,13.3887777 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Schlegelstraße +
    + + + -53499890 + 52.530674,13.3887777 52.5297775,13.3894777 + 116.0 + + 1.39 + 83.5 + 1.39 + 83.5 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Borsigstraße +
    + + + -53499910 + 52.5297775,13.3894777 52.5297241,13.3895216 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tieckstraße +
    + + + +53499910 + 52.5297241,13.3895216 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tieckstraße +
    + + + -53499992 + 52.5297241,13.3895216 52.5280571,13.3913355 + 230.0 + + 1.39 + 165.6 + 1.39 + 165.6 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Borsigstraße +
    + + + -833290988 + 52.5280571,13.3913355 52.5279846,13.3914194 + 4.0 + 13.89 + + 8.33 + 0.5 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Torstraße +
    + + + +833290988 + 52.5279846,13.3914194 + 4.0 + 13.89 + + 7.5 + 0.5 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Torstraße +
    + + + +53499991 + 52.5279846,13.3914194 52.5281792,13.3924217 + 73.0 + 13.89 + + 7.5 + 9.7 + 1.39 + 52.6 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Torstraße +
    + + + -53500024 + 52.5281792,13.3924217 52.5276413,13.392765 52.527504,13.3928776 + 88.0 + + 1.39 + 63.4 + 1.39 + 63.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + -53520166 + 52.527504,13.3928776 52.5274544,13.3929157 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Linienstraße +
    + + + +53520166 + 52.5274544,13.3929157 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Linienstraße +
    + + + -53500091 + 52.5274544,13.3929157 52.5274773,13.3929968 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + +53500091 + 52.5274773,13.3929968 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + -53500091 + 52.5274773,13.3929968 52.5264435,13.3935556 + 126.0 + + 1.39 + 90.7 + 1.39 + 90.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + +53500090 + 52.5264435,13.3935556 52.5263863,13.3935757 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Auguststraße +
    + + + -53500090 + 52.5263863,13.3935757 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Auguststraße +
    + + + -844906239 + 52.5263863,13.3935757 52.5262947,13.3935919 52.525425,13.393137 + 112.0 + + 1.39 + 80.6 + 1.39 + 80.6 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + -844906238 + 52.525425,13.393137 52.5249977,13.3929052 + 54.0 + + 1.39 + 38.9 + 1.39 + 38.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + -53501113 + 52.5249977,13.3929052 52.5249405,13.3928967 + 4.0 + 13.89 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + +53501113 + 52.5249405,13.3928967 + 4.0 + 13.89 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501113 + 52.5249405,13.3928967 52.5246468,13.3941231 + 92.0 + 13.89 + + 1.39 + 66.2 + 1.39 + 66.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + +
    + + 1107.0 + 798.0 + 798.0 + noThroughRoad + +
    +
    +
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/travelmode-public-transport.xml b/tests/auto/nokia_services/routing/travelmode-public-transport.xml new file mode 100644 index 0000000..cf146ea --- /dev/null +++ b/tests/auto/nokia_services/routing/travelmode-public-transport.xml @@ -0,0 +1,343 @@ + + + + + 2012-04-26T14:49:40.902Z + 2012-04-26T14:47:00.026+0000 + 5094886 + 2012-04-26T14:47:02.494+0000 + 12015 + 2012-04-26T14:47:02.494+0000 + 857 + 2011Q3 + routeserver,9.2-2012.02.20-hotfix6.2.13.1 + 142 + routing-route-service,6.2.13.1 + + + REMvTwUAAAB4tdyZCURKQJROJJhqxipAAAAAYAlESkAAAADAasYqQAAAAAAAAPB_AAAAAAAA8H-XVzADV_SHZp4MKQHNgLOULCerAAEAAICiDCkBAQAAADAnqwABAAAAAADA_wEAAAAAAMD_HY0-84f6AgAAvBcAABjgZf7_Hyzz__8Hkw + + +53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + + fastestNow + publicTransport + enabled + + 52.5315819,13.3875275 52.5315819,13.3880997 52.5316086,13.3882599 52.5323792,13.3873796 52.5251007,13.3922596 52.5251007,13.3922596 52.5249786,13.3928604 52.5249443,13.3928757 52.5246468,13.3941231 + + + 52.5323792 + 13.3873796 + + + 52.5246468 + 13.3941231 + + + + + +53499799 + + 52.5315361 + 13.3875332 + + + 52.531543 + 13.387532 + + stopOver + + + -53501113 + + 52.5246773 + 13.3941345 + + + 52.524646 + 13.394128 + + stopOver + + 1388.0 + 641.3 + + + 52.5315361 + 13.3875332 + + Head toward Borsigstraße on Invalidenstraße. Go for 100 feet. + 27.4 + 38.0 + +53499799 + forward + + + + 52.5315819 + 13.3880997 + + Leave Invalidenstraße Go for 400 feet. + 115.7 + 115.0 + +1525 + forward + + + + 52.5323792 + 13.3873796 + + Take REGIONALMETRO "?es "?ains (Deutsche Bahn), departure 18:55. + 378.7 + 1092.0 + + + + + 52.5251007 + 13.3922596 + + Go through the virtual connection. Go for 150 feet. + 47.4 + 43.0 + -3281 + forward + + + + 52.5249786 + 13.3928604 + + Turn right and cross Tucholskystraße. + 6.0 + 8.0 + -53501259 + right + + + + 52.5249786 + 13.3928604 + + Continue on Oranienburger Straße. Go for 300 feet. + 66.2 + 92.0 + -53501113 + forward + + + + 52.5246773 + 13.3941345 + + Your destination on Oranienburger Straße is on the right. The trip takes 0.9 miles and 11 mins. + 0.0 + 0.0 + forward + + + +53499799 + 52.5315819,13.3875275 52.5315819,13.3880997 + 38.0 + 13.89 + + 7.77 + 4.9 + 1.39 + 27.4 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Invalidenstraße +
    + + + +1525 + 52.5315819,13.3880997 52.5316086,13.3882599 + 11.0 + 1.39 + + 1.39 + 7.9 + 1.39 + 7.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Nordbahnhof +
    + + + +1519 + 52.5316086,13.3882599 52.5323792,13.3873796 + 104.0 + 1.39 + + 1.39 + 74.9 + 1.39 + 74.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Nordbahnhof +
    + + + -8016 + 52.5323792,13.3873796 52.5251007,13.3922596 + 1092.0 + + + -3281 + 52.5251007,13.3922596 52.5251007,13.3922596 + 1.0 + 1.39 + + 1.39 + 0.7 + 1.39 + 0.7 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -3284 + 52.5251007,13.3922596 52.5249786,13.3928604 + 42.0 + 1.39 + + 1.39 + 30.2 + 1.39 + 30.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + + + -53501259 + 52.5249786,13.3928604 52.5249443,13.3928757 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + +53501259 + 52.5249443,13.3928757 + 4.0 + + 1.39 + 2.9 + 1.39 + 2.9 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Tucholskystraße +
    + + + -53501113 + 52.5249443,13.3928757 52.5246468,13.3941231 + 92.0 + 13.89 + + 1.39 + 66.2 + 1.39 + 66.2 + +
    + + DE + Berlin + Berlin + Berlin + Mitte + Oranienburger Straße +
    + +
    + + 1388.0 + 641.0 + 641.0 + noThroughRoad + unpaved + publicTransport + +
    +
    +
    \ No newline at end of file diff --git a/tests/auto/nokia_services/routing/tst_routing.cpp b/tests/auto/nokia_services/routing/tst_routing.cpp new file mode 100644 index 0000000..833c95a --- /dev/null +++ b/tests/auto/nokia_services/routing/tst_routing.cpp @@ -0,0 +1,518 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +#define CHECK_CLOSE_E(expected, actual, e) QVERIFY((qAbs(actual - expected) <= e)) +#define CHECK_CLOSE(expected, actual) CHECK_CLOSE_E(expected, actual, qreal(1e-6)) + +class MockGeoNetworkReply : public QNetworkReply +{ +public: + MockGeoNetworkReply( QObject* parent = 0); + virtual void abort(); + + void setFile(QFile* file); + void complete(); + using QNetworkReply::setRequest; + using QNetworkReply::setOperation; + using QNetworkReply::setError; + +protected: + virtual qint64 readData(char *data, qint64 maxlen); + virtual qint64 writeData(const char *data, qint64 len); + +private: + QFile* m_file; +}; + +MockGeoNetworkReply::MockGeoNetworkReply(QObject* parent) +: QNetworkReply(parent) +, m_file(0) +{ + setOpenMode(QIODevice::ReadOnly); +} + +void MockGeoNetworkReply::abort() +{} + +qint64 MockGeoNetworkReply::readData(char *data, qint64 maxlen) +{ + if (m_file) { + const qint64 read = m_file->read(data, maxlen); + if (read <= 0) + return -1; + return read; + } + return -1; +} + +qint64 MockGeoNetworkReply::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data); + Q_UNUSED(len); + return -1; +} + +void MockGeoNetworkReply::setFile(QFile* file) +{ + delete m_file; + m_file = file; + if (m_file) + m_file->setParent(this); +} + +void MockGeoNetworkReply::complete() +{ + if (error() != QNetworkReply::NoError) + emit error(error()); + setFinished(true); + emit finished(); +} + +class MockGeoNetworkAccessManager : public QGeoNetworkAccessManager +{ +public: + MockGeoNetworkAccessManager(QObject* parent = 0); + QNetworkReply* get(const QNetworkRequest& request); + QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data); + + void setReply(MockGeoNetworkReply* reply); + +private: + MockGeoNetworkReply* m_reply; +}; + +MockGeoNetworkAccessManager::MockGeoNetworkAccessManager(QObject* parent) +: QGeoNetworkAccessManager(parent) +, m_reply(0) +{} + +QNetworkReply* MockGeoNetworkAccessManager::get(const QNetworkRequest& request) +{ + MockGeoNetworkReply* r = m_reply; + m_reply = 0; + if (r) { + r->setRequest(request); + r->setOperation(QNetworkAccessManager::GetOperation); + r->setParent(0); + } + + return r; +} + +QNetworkReply* MockGeoNetworkAccessManager::post(const QNetworkRequest &request, const QByteArray &data) +{ + Q_UNUSED(request); + Q_UNUSED(data); + QTest::qFail("Not implemented", __FILE__, __LINE__); + return new MockGeoNetworkReply(); +} + +void MockGeoNetworkAccessManager::setReply(MockGeoNetworkReply* reply) +{ + delete m_reply; + m_reply = reply; + if (m_reply) + m_reply->setParent(this); +} + +class tst_nokia_routing : public QObject +{ + Q_OBJECT + +public: + tst_nokia_routing(); + +private: + void calculateRoute(); + void loadReply(const QString& filename); + void onReply(QGeoRouteReply* reply); + void verifySaneRoute(const QGeoRoute& route); + + // Infrastructure slots +private Q_SLOTS: + void routingFinished(QGeoRouteReply* reply); + void routingError(QGeoRouteReply* reply, QGeoRouteReply::Error error, QString errorString); + + // Test slots +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void cleanup(); + void can_compute_route_for_all_supported_travel_modes(); + void can_compute_route_for_all_supported_travel_modes_data(); + void can_compute_route_for_all_supported_optimizations(); + void can_compute_route_for_all_supported_optimizations_data(); + void can_handle_multiple_routes_in_response(); + void can_handle_no_route_exists_case(); + void can_handle_invalid_server_responses(); + void can_handle_invalid_server_responses_data(); + void can_handle_additions_to_routing_xml(); + void foobar(); + void foobar_data(); + +private: + QGeoServiceProvider* m_geoServiceProvider; + MockGeoNetworkAccessManager* m_networkManager; + QGeoRoutingManager* m_routingManager; + QGeoRouteReply* m_reply; + MockGeoNetworkReply* m_replyUnowned; + QGeoRouteRequest m_dummyRequest; + bool m_calculationDone; + bool m_expectError; +}; + +tst_nokia_routing::tst_nokia_routing() +: m_geoServiceProvider(0) +, m_networkManager(0) +, m_routingManager(0) +, m_reply(0) +, m_replyUnowned() +, m_calculationDone(true) +, m_expectError(false) +{ +} + +void tst_nokia_routing::loadReply(const QString& filename) +{ + QFile* file = new QFile(QFINDTESTDATA(filename)); + if (!file->open(QIODevice::ReadOnly)) { + delete file; + file = 0; + qDebug() << filename; + QTest::qFail("Failed to open file", __FILE__, __LINE__); + } + + m_replyUnowned = new MockGeoNetworkReply(); + m_replyUnowned->setFile(file); + m_networkManager->setReply(m_replyUnowned); +} + +void tst_nokia_routing::calculateRoute() +{ + QVERIFY2(m_replyUnowned, "No reply set"); + m_calculationDone = false; + m_routingManager->calculateRoute(m_dummyRequest); + m_replyUnowned->complete(); + m_replyUnowned = 0; + // Timeout of 200ms is required for slow targets (e.g. Qemu) + QTRY_VERIFY_WITH_TIMEOUT(m_calculationDone, 200); +} + +void tst_nokia_routing::onReply(QGeoRouteReply* reply) +{ + QVERIFY(reply); + //QVERIFY(0 == m_reply); + m_reply = reply; + if (m_reply) + m_reply->setParent(0); + m_calculationDone = true; +} + +void tst_nokia_routing::verifySaneRoute(const QGeoRoute& route) +{ + QVERIFY(route.distance() > 0); + QVERIFY(route.travelTime() > 0); + QVERIFY(route.travelMode() != 0); + + const QGeoRectangle bounds = route.bounds(); + QVERIFY(bounds.width() > 0); + QVERIFY(bounds.height() > 0); + + const QList path = route.path(); + QVERIFY(path.size() >= 2); + + foreach (const QGeoCoordinate& coord, path) { + QVERIFY(coord.isValid()); + QVERIFY(bounds.contains(coord)); + } + + QGeoRouteSegment segment = route.firstRouteSegment(); + bool first = true, last = false; + + do { + const QGeoRouteSegment next = segment.nextRouteSegment(); + last = next.isValid(); + + QVERIFY(segment.isValid()); + QVERIFY(segment.distance() >= 0); + QVERIFY(segment.travelTime() >= 0); // times are rounded and thus may end up being zero + + const QList path = segment.path(); + foreach (const QGeoCoordinate& coord, path) { + QVERIFY(coord.isValid()); + if (!first && !last) { + QVERIFY(bounds.contains(coord)); // on pt and pedestrian + } + } + + const QGeoManeuver maneuver = segment.maneuver(); + + if (maneuver.isValid()) { + QVERIFY(!maneuver.instructionText().isEmpty()); + QVERIFY(maneuver.position().isValid()); + if (!first && !last) { + QVERIFY(bounds.contains(maneuver.position())); // on pt and pedestrian + } + } + + segment = next; + first = false; + } while (!last); +} + +void tst_nokia_routing::routingFinished(QGeoRouteReply* reply) +{ + onReply(reply); +} + +void tst_nokia_routing::routingError(QGeoRouteReply* reply, QGeoRouteReply::Error error, QString errorString) +{ + Q_UNUSED(error); + + if (!m_expectError) { + QFAIL(qPrintable(errorString)); + } else { + onReply(reply); + } +} + +void tst_nokia_routing::initTestCase() +{ + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + QVERIFY(providers.contains(QStringLiteral("here"))); + + m_networkManager = new MockGeoNetworkAccessManager(); + + QVariantMap parameters; + parameters.insert(QStringLiteral("nam"), QVariant::fromValue(m_networkManager)); + parameters.insert(QStringLiteral("here.app_id"), "stub"); + parameters.insert(QStringLiteral("here.token"), "stub"); + + m_geoServiceProvider = new QGeoServiceProvider(QStringLiteral("here"), parameters); + QVERIFY(m_geoServiceProvider); + + m_routingManager = m_geoServiceProvider->routingManager(); + QVERIFY(m_routingManager); + + connect(m_routingManager, SIGNAL(finished(QGeoRouteReply*)), + this, SLOT(routingFinished(QGeoRouteReply*))); + connect(m_routingManager, SIGNAL(error(QGeoRouteReply*,QGeoRouteReply::Error,QString)), + this, SLOT(routingError(QGeoRouteReply*,QGeoRouteReply::Error,QString))); + + QList waypoints; + waypoints.push_back(QGeoCoordinate(1, 1)); + waypoints.push_back(QGeoCoordinate(2, 2)); + m_dummyRequest.setWaypoints(waypoints); +} + +void tst_nokia_routing::cleanupTestCase() +{ + delete m_geoServiceProvider; + + // network access manager will be deleted by plugin + + m_geoServiceProvider = 0; + m_networkManager = 0; + m_routingManager = 0; +} + +void tst_nokia_routing::cleanup() +{ + delete m_reply; + m_reply = 0; + m_replyUnowned = 0; + m_expectError = false; +} + +void tst_nokia_routing::can_compute_route_for_all_supported_travel_modes() +{ + QFETCH(int, travelMode); + QFETCH(QString, file); + QFETCH(qreal, distance); + QFETCH(int, duration); + + loadReply(file); + calculateRoute(); + + QList routes = m_reply->routes(); + QCOMPARE(1, routes.size()); + QGeoRoute& route = routes[0]; + QCOMPARE(travelMode, (int)route.travelMode()); + CHECK_CLOSE(distance, route.distance()); + QCOMPARE(duration, route.travelTime()); + verifySaneRoute(route); +} + +void tst_nokia_routing::can_compute_route_for_all_supported_travel_modes_data() +{ + QTest::addColumn("travelMode"); + QTest::addColumn("file"); + QTest::addColumn("distance"); + QTest::addColumn("duration"); + + QTest::newRow("Car") << (int)QGeoRouteRequest::CarTravel << QString("travelmode-car.xml") << (qreal)1271.0 << 243; + QTest::newRow("Pedestrian") << (int)QGeoRouteRequest::PedestrianTravel << QString("travelmode-pedestrian.xml") << (qreal)1107.0 << 798; + QTest::newRow("Public Transport") << (int)QGeoRouteRequest::PublicTransitTravel << QString("travelmode-public-transport.xml") << (qreal)1388.0 << 641; +} + +void tst_nokia_routing::can_compute_route_for_all_supported_optimizations() +{ + QFETCH(int, optimization); + QFETCH(QString, file); + QFETCH(qreal, distance); + QFETCH(int, duration); + m_dummyRequest.setRouteOptimization((QGeoRouteRequest::RouteOptimization)optimization); + loadReply(file); + calculateRoute(); + QList routes = m_reply->routes(); + QCOMPARE(1, routes.size()); + QGeoRoute& route = routes[0]; + CHECK_CLOSE(distance, route.distance()); + QCOMPARE(duration, route.travelTime()); + verifySaneRoute(route); +} + +void tst_nokia_routing::can_compute_route_for_all_supported_optimizations_data() +{ + QTest::addColumn("optimization"); + QTest::addColumn("file"); + QTest::addColumn("distance"); + QTest::addColumn("duration"); + + QTest::newRow("Shortest") << (int)QGeoRouteRequest::ShortestRoute << QString("optim-shortest.xml") << qreal(1177.0) << 309; + QTest::newRow("Fastest") << (int)QGeoRouteRequest::FastestRoute << QString("optim-fastest.xml") << qreal(1271.0) << 243; +} + +void tst_nokia_routing::can_handle_multiple_routes_in_response() +{ + loadReply(QStringLiteral("multiple-routes-in-response.xml")); + calculateRoute(); + QList routes = m_reply->routes(); + QCOMPARE(2, routes.size()); + + verifySaneRoute(routes[0]); + verifySaneRoute(routes[1]); +} + +void tst_nokia_routing::can_handle_no_route_exists_case() +{ + loadReply(QStringLiteral("error-no-route.xml")); + calculateRoute(); + QCOMPARE(QGeoRouteReply::NoError, m_reply->error()); + QList routes = m_reply->routes(); + QCOMPARE(0, routes.size()); +} + +void tst_nokia_routing::can_handle_additions_to_routing_xml() +{ + loadReply(QStringLiteral("littered-with-new-tags.xml")); + calculateRoute(); + QCOMPARE(QGeoRouteReply::NoError, m_reply->error()); + QList routes = m_reply->routes(); + QVERIFY(routes.size() > 0); +} + +void tst_nokia_routing::can_handle_invalid_server_responses() +{ + QFETCH(QString, file); + + m_expectError = true; + + loadReply(file); + calculateRoute(); + QCOMPARE(QGeoRouteReply::ParseError, m_reply->error()); +} + +void tst_nokia_routing::can_handle_invalid_server_responses_data() +{ + QTest::addColumn("file"); + + QTest::newRow("Trash") << QString("invalid-response-trash.xml"); + QTest::newRow("Half way through") << QString("invalid-response-half-way-through.xml"); + QTest::newRow("No tag") << QString("invalid-response-no-calculateroute-tag.xml"); +} + +void tst_nokia_routing::foobar() +{ + QFETCH(int, code); + + m_expectError = true; + m_replyUnowned = new MockGeoNetworkReply(); + m_replyUnowned->setError(static_cast(code), QStringLiteral("Test error")); + m_networkManager->setReply(m_replyUnowned); + calculateRoute(); + QCOMPARE(QGeoRouteReply::CommunicationError, m_reply->error()); +} + +void tst_nokia_routing::foobar_data() +{ + QTest::addColumn("code"); + + QTest::newRow("QNetworkReply::ConnectionRefusedError") << int(QNetworkReply::ConnectionRefusedError); + QTest::newRow("QNetworkReply::RemoteHostClosedError") << int(QNetworkReply::RemoteHostClosedError); + QTest::newRow("QNetworkReply::HostNotFoundError") << int(QNetworkReply::HostNotFoundError); + QTest::newRow("QNetworkReply::TimeoutError") << int(QNetworkReply::TimeoutError); + QTest::newRow("QNetworkReply::OperationCanceledError") << int(QNetworkReply::OperationCanceledError); + QTest::newRow("QNetworkReply::SslHandshakeFailedError") << int(QNetworkReply::SslHandshakeFailedError); + QTest::newRow("QNetworkReply::TemporaryNetworkFailureError") << int(QNetworkReply::TemporaryNetworkFailureError); + QTest::newRow("QNetworkReply::ProxyConnectionRefusedError") << int(QNetworkReply::ProxyConnectionRefusedError); + QTest::newRow("QNetworkReply::ProxyConnectionClosedError") << int(QNetworkReply::ProxyConnectionClosedError); + QTest::newRow("QNetworkReply::ProxyNotFoundError") << int(QNetworkReply::ProxyNotFoundError); + QTest::newRow("QNetworkReply::ProxyTimeoutError") << int(QNetworkReply::ProxyTimeoutError); + QTest::newRow("QNetworkReply::ProxyAuthenticationRequiredError") << int(QNetworkReply::ProxyAuthenticationRequiredError); + QTest::newRow("QNetworkReply::ContentAccessDenied") << int(QNetworkReply::ContentAccessDenied); + QTest::newRow("QNetworkReply::ContentOperationNotPermittedError") << int(QNetworkReply::ContentOperationNotPermittedError); + QTest::newRow("QNetworkReply::ContentNotFoundError") << int(QNetworkReply::ContentNotFoundError); + QTest::newRow("QNetworkReply::ContentReSendError") << int(QNetworkReply::ContentReSendError); + QTest::newRow("QNetworkReply::ProtocolUnknownError") << int(QNetworkReply::ProtocolUnknownError); + QTest::newRow("QNetworkReply::ProtocolInvalidOperationError") << int(QNetworkReply::ProtocolInvalidOperationError); + QTest::newRow("QNetworkReply::UnknownNetworkError") << int(QNetworkReply::UnknownNetworkError); + QTest::newRow("QNetworkReply::UnknownProxyError") << int(QNetworkReply::UnknownProxyError); + QTest::newRow("QNetworkReply::ProxyAuthenticationRequiredError") << int(QNetworkReply::ProxyAuthenticationRequiredError); + QTest::newRow("QNetworkReply::ProtocolFailure") << int(QNetworkReply::ProtocolFailure); +} + + +QTEST_MAIN(tst_nokia_routing) + +#include "tst_routing.moc" diff --git a/tests/auto/placemanager_utils/placemanager_utils.cpp b/tests/auto/placemanager_utils/placemanager_utils.cpp new file mode 100644 index 0000000..d5ba61f --- /dev/null +++ b/tests/auto/placemanager_utils/placemanager_utils.cpp @@ -0,0 +1,376 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "placemanager_utils.h" + +#include +#include +#include +#include +#include +#include +#include + +//constant for timeout to verify signals +const int PlaceManagerUtils::Timeout(10000); + +PlaceManagerUtils::PlaceManagerUtils(QObject *parent) + : QObject(parent), placeManager(0) +{ +} + +bool PlaceManagerUtils::doSavePlace(QPlaceManager *manager, + const QPlace &place, + QPlaceReply::Error expectedError, + QString *placeId) +{ + Q_ASSERT(manager); + QPlaceIdReply *saveReply = manager->savePlace(place); + bool isSuccessful = checkSignals(saveReply, expectedError, manager); + if (placeId != 0) { + *placeId = saveReply->id(); + } + + if (saveReply->id().isEmpty() && expectedError == QPlaceReply::NoError) { + qWarning("ID is empty in reply for save operation"); + qWarning() << "Error string = " << saveReply->errorString(); + isSuccessful = false; + } + + if (!isSuccessful) + qWarning() << "Error string = " << saveReply->errorString(); + + return isSuccessful; +} + +void PlaceManagerUtils::doSavePlaces(QPlaceManager *manager, QList &places) +{ + QPlaceIdReply *saveReply; + + foreach (QPlace place, places) { + saveReply = manager->savePlace(place); + QSignalSpy saveSpy(saveReply, SIGNAL(finished())); + QTRY_VERIFY_WITH_TIMEOUT(saveSpy.count() == 1, Timeout); + QCOMPARE(saveReply->error(), QPlaceReply::NoError); + saveSpy.clear(); + } +} + +void PlaceManagerUtils::doSavePlaces(QPlaceManager *manager, const QList &places) +{ + QPlaceIdReply *saveReply; + + static int count= 0; + foreach (QPlace *place, places) { + count++; + saveReply = manager->savePlace(*place); + QSignalSpy saveSpy(saveReply, SIGNAL(finished())); + QTRY_VERIFY_WITH_TIMEOUT(saveSpy.count() == 1, Timeout); + QCOMPARE(saveReply->error(), QPlaceReply::NoError); + place->setPlaceId(saveReply->id()); + saveSpy.clear(); + } +} + +bool PlaceManagerUtils::doSearch(QPlaceManager *manager, + const QPlaceSearchRequest &request, + QList *results, + QPlaceReply::Error expectedError) +{ + QPlaceSearchReply *searchReply= manager->search(request); + bool success = checkSignals(searchReply, expectedError, manager); + *results = searchReply->results(); + return success; +} + +bool PlaceManagerUtils::doSearch(QPlaceManager *manager, + const QPlaceSearchRequest &request, + QList *results, QPlaceReply::Error expectedError) +{ + bool success = false; + results->clear(); + QList searchResults; + success = doSearch(manager, request, &searchResults, expectedError); + foreach (const QPlaceSearchResult &searchResult, searchResults) { + if (searchResult.type() == QPlaceSearchResult::PlaceResult) { + QPlaceResult placeResult = searchResult; + results->append(placeResult.place()); + } + } + return success; +} + +bool PlaceManagerUtils::doSearchSuggestions(QPlaceManager *manager, + const QPlaceSearchRequest &request, + QStringList *results, + QPlaceReply::Error expectedError) +{ + QPlaceSearchSuggestionReply *reply = manager->searchSuggestions(request); + bool success = checkSignals(reply, expectedError, manager); + *results = reply->suggestions(); + + if (!success) + qDebug() << "Error string = " << reply->errorString(); + + return success; +} + +bool PlaceManagerUtils::doRemovePlace(QPlaceManager *manager, + const QPlace &place, + QPlaceReply::Error expectedError) +{ + QPlaceIdReply *removeReply = manager->removePlace(place.placeId()); + bool isSuccessful = false; + isSuccessful = checkSignals(removeReply, expectedError, manager) + && (removeReply->id() == place.placeId()); + + if (!isSuccessful) + qWarning() << "Place removal unsuccessful errorString = " << removeReply->errorString(); + + return isSuccessful; +} + +bool PlaceManagerUtils::doFetchDetails(QPlaceManager *manager, + QString placeId, QPlace *place, + QPlaceReply::Error expectedError) +{ + QPlaceDetailsReply *detailsReply = manager->getPlaceDetails(placeId); + bool success = checkSignals(detailsReply, expectedError, manager); + *place = detailsReply->place(); + + if (!success) + qDebug() << "Error string = " << detailsReply->errorString(); + + return success; +} + +bool PlaceManagerUtils::doInitializeCategories(QPlaceManager *manager, + QPlaceReply::Error expectedError) +{ + QPlaceReply *reply = manager->initializeCategories(); + bool success = checkSignals(reply, expectedError, manager); + + if (!success) + qDebug() << "Error string = " << reply->errorString(); + + delete reply; + return success; +} + +bool PlaceManagerUtils::doSaveCategory(QPlaceManager *manager, + const QPlaceCategory &category, + const QString &parentId, + QPlaceReply::Error expectedError, + QString *categoryId) +{ + QPlaceIdReply *idReply = manager->saveCategory(category, parentId); + bool isSuccessful = checkSignals(idReply, expectedError, manager) + && (idReply->error() == expectedError); + + if (categoryId != 0) + *categoryId = idReply->id(); + + if (!isSuccessful) + qDebug() << "Error string =" << idReply->errorString(); + return isSuccessful; +} + +bool PlaceManagerUtils::doRemoveCategory(QPlaceManager *manager, + const QPlaceCategory &category, + QPlaceReply::Error expectedError) +{ + QPlaceIdReply *idReply = manager->removeCategory(category.categoryId()); + + bool isSuccessful = checkSignals(idReply, expectedError, manager) && + (idReply->error() == expectedError); + return isSuccessful; +} + +bool PlaceManagerUtils::doFetchCategory(QPlaceManager *manager, + const QString &categoryId, + QPlaceCategory *category, + QPlaceReply::Error expectedError) +{ + Q_ASSERT(category); + QPlaceReply * catInitReply = manager->initializeCategories(); + bool isSuccessful = checkSignals(catInitReply, expectedError, manager); + *category = manager->category(categoryId); + + if (!isSuccessful) + qDebug() << "Error initializing categories, error string = " + << catInitReply->errorString(); + + if (category->categoryId() != categoryId) + isSuccessful = false; + return isSuccessful; +} + +bool PlaceManagerUtils::doFetchContent(QPlaceManager *manager, + const QPlaceContentRequest &request, + QPlaceContent::Collection *results, + QPlaceReply::Error expectedError) +{ + Q_ASSERT(results); + QPlaceContentReply *reply = manager->getPlaceContent(request); + bool isSuccessful = checkSignals(reply, expectedError, manager); + *results = reply->content(); + + if (!isSuccessful) + qDebug() << "Error during content fetch, error string = " + << reply->errorString(); + + return isSuccessful; +} + +bool PlaceManagerUtils::doMatch(QPlaceManager *manager, + const QPlaceMatchRequest &request, + QList *places, + QPlaceReply::Error expectedError) +{ + QPlaceMatchReply *reply = manager->matchingPlaces(request); + bool isSuccessful = checkSignals(reply, expectedError, manager) && + (reply->error() == expectedError); + *places = reply->places(); + if (!isSuccessful) + qDebug() << "Error for matching operation, error string = " + << reply->errorString(); + return isSuccessful; +} + +bool PlaceManagerUtils::checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError, + QPlaceManager *manager) +{ + Q_ASSERT(reply); + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + QSignalSpy errorSpy(reply, SIGNAL(error(QPlaceReply::Error,QString))); + QSignalSpy managerFinishedSpy(manager, SIGNAL(finished(QPlaceReply*))); + QSignalSpy managerErrorSpy(manager,SIGNAL(error(QPlaceReply*,QPlaceReply::Error,QString))); + + if (expectedError != QPlaceReply::NoError) { + //check that we get an error signal from the reply + WAIT_UNTIL(errorSpy.count() == 1); + if (errorSpy.count() != 1) { + qWarning() << "Error signal for search operation not received"; + return false; + } + + //check that we get the correct error from the reply's signal + QPlaceReply::Error actualError = qvariant_cast(errorSpy.at(0).at(0)); + if (actualError != expectedError) { + qWarning() << "Actual error code in reply signal does not match expected error code"; + qWarning() << "Actual error code = " << actualError; + qWarning() << "Expected error coe =" << expectedError; + return false; + } + + //check that we get an error signal from the manager + WAIT_UNTIL(managerErrorSpy.count() == 1); + if (managerErrorSpy.count() !=1) { + qWarning() << "Error signal from manager for search operation not received"; + return false; + } + + //check that we get the correct reply instance in the error signal from the manager + if (qvariant_cast(managerErrorSpy.at(0).at(0)) != reply) { + qWarning() << "Reply instance in error signal from manager is incorrect"; + return false; + } + + //check that we get the correct error from the signal of the manager + actualError = qvariant_cast(managerErrorSpy.at(0).at(1)); + if (actualError != expectedError) { + qWarning() << "Actual error code from manager signal does not match expected error code"; + qWarning() << "Actual error code =" << actualError; + qWarning() << "Expected error code = " << expectedError; + return false; + } + } + + //check that we get a finished signal + WAIT_UNTIL(finishedSpy.count() == 1); + if (finishedSpy.count() !=1) { + qWarning() << "Finished signal from reply not received"; + return false; + } + + if (reply->error() != expectedError) { + qWarning() << "Actual error code does not match expected error code"; + qWarning() << "Actual error code: " << reply->error(); + qWarning() << "Expected error code" << expectedError; + return false; + } + + if (expectedError == QPlaceReply::NoError && !reply->errorString().isEmpty()) { + qWarning() << "Expected error was no error but error string was not empty"; + qWarning() << "Error string=" << reply->errorString(); + return false; + } + + //check that we get the finished signal from the manager + WAIT_UNTIL(managerFinishedSpy.count() == 1); + if (managerFinishedSpy.count() != 1) { + qWarning() << "Finished signal from manager not received"; + return false; + } + + //check that the reply instance in the finished signal from the manager is correct + if (qvariant_cast(managerFinishedSpy.at(0).at(0)) != reply) { + qWarning() << "Reply instance in finished signal from manager is incorrect"; + return false; + } + + return true; +} + +bool PlaceManagerUtils::compare(const QList &actualResults, + const QList &expectedResults) +{ + QSet actualIds; + foreach (const QPlace &place, actualResults) + actualIds.insert(place.placeId()); + + QSet expectedIds; + foreach (const QPlace &place, expectedResults) + expectedIds.insert(place.placeId()); + + bool isMatch = (actualIds == expectedIds); + if (actualResults.count() != expectedResults.count() || !isMatch) { + qWarning() << "comparison of results by name does not match"; + qWarning() << "actual result ids: " << actualIds; + qWarning() << "expected result ids : " << expectedIds; + return false; + } + + return isMatch; +} + +void PlaceManagerUtils::setVisibility(QList places, QLocation::Visibility visibility) +{ + foreach (QPlace *place, places) + place->setVisibility(visibility); +} diff --git a/tests/auto/placemanager_utils/placemanager_utils.h b/tests/auto/placemanager_utils/placemanager_utils.h new file mode 100644 index 0000000..982bd85 --- /dev/null +++ b/tests/auto/placemanager_utils/placemanager_utils.h @@ -0,0 +1,230 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLACEMANAGER_UTILS_H +#define PLACEMANAGER_UTILS_H + +#include +#include +#include +#include + +#ifndef WAIT_UNTIL +#define WAIT_UNTIL(__expr) \ + do { \ + const int __step = 50; \ + const int __timeout = 25000; \ + if (!(__expr)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + } while (0) +#endif + +QT_BEGIN_NAMESPACE + +class QPlaceManager; +class QPlace; +class QPlaceSearchResult; +class QPlaceSearchRequest; +class QPlaceCategory; +class QPlaceContentRequest; +class QPlaceMatchRequest; + +QT_END_NAMESPACE + +class PlaceManagerUtils : public QObject +{ + Q_OBJECT +public: + PlaceManagerUtils(QObject *parent = 0); + + static bool doSavePlace(QPlaceManager *manager, + const QPlace &place, + QPlaceReply::Error expectedError = QPlaceReply::NoError, + QString *placeId = 0); + + static void doSavePlaces(QPlaceManager *manager, QList &places); + + //sets the id for saved places + static void doSavePlaces(QPlaceManager *manager, const QList &places); + + static bool doSearch(QPlaceManager *manager, const QPlaceSearchRequest &request, + QList *results, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doSearch(QPlaceManager *manager, const QPlaceSearchRequest &request, + QList *results, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doSearchSuggestions(QPlaceManager *manager, + const QPlaceSearchRequest &request, + QStringList *results, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doRemovePlace(QPlaceManager *manager, const QPlace &place, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doFetchDetails(QPlaceManager *manager, + QString placeId, + QPlace *place, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doInitializeCategories(QPlaceManager *manager, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doSaveCategory(QPlaceManager *manager, + const QPlaceCategory &category, + const QString &parentId, + QPlaceReply::Error expectedError = QPlaceReply::NoError, + QString *categoryId = 0); + + static bool doRemoveCategory(QPlaceManager *manager, const QPlaceCategory &category, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doFetchCategory(QPlaceManager *manager, + const QString &categoryId, + QPlaceCategory *category, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doFetchContent(QPlaceManager *manager, + const QPlaceContentRequest &request, + QPlaceContent::Collection *results, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool doMatch(QPlaceManager *manager, + const QPlaceMatchRequest &request, + QList *places, + QPlaceReply::Error expectedError = QPlaceReply::NoError); + + static bool checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError, + QPlaceManager *manager); + + static bool compare(const QList &actualResults, + const QList &expectedResults); + + static void setVisibility(QListplaces, QLocation::Visibility visibility); + + static const int Timeout; + +protected: + bool doSavePlace(const QPlace &place, + QPlaceReply::Error expectedError = QPlaceReply::NoError, + QString *placeId = 0) { + return doSavePlace(placeManager, place, expectedError, placeId); + } + + void doSavePlaces(QList &places) { + return doSavePlaces(placeManager, places); + } + + void doSavePlaces(const QList &places) { + return doSavePlaces(placeManager, places); + } + + bool doRemovePlace(const QPlace &place, + QPlaceReply::Error expectedError = QPlaceReply::NoError) + { + return doRemovePlace(placeManager, place, expectedError); + } + + bool doSearch(const QPlaceSearchRequest &request, + QList *results, + QPlaceReply::Error expectedError = QPlaceReply::NoError) { + return doSearch(placeManager, request, results,expectedError); + } + + bool doSearchSuggestions(const QPlaceSearchRequest &request, + QStringList *results, + QPlaceReply::Error expectedError) { + return doSearchSuggestions(placeManager, request, results, expectedError); + } + + bool doFetchDetails(QString placeId, + QPlace *place, + QPlaceReply::Error expectedError = QPlaceReply::NoError) { + return doFetchDetails(placeManager, placeId, place, expectedError); + } + + bool doInitializeCategories(QPlaceReply::Error expectedError = QPlaceReply::NoError) { + return doInitializeCategories(placeManager, expectedError); + } + + bool doSaveCategory(const QPlaceCategory &category, + QPlaceReply::Error expectedError = QPlaceReply::NoError, + QString *categoryId = 0) { + return doSaveCategory(placeManager, category, QString(), + expectedError,categoryId); + } + + bool doSaveCategory(const QPlaceCategory &category, + const QString &parentId, + QPlaceReply::Error expectedError = QPlaceReply::NoError, + QString *categoryId = 0) { + return doSaveCategory(placeManager, category, parentId, + expectedError, categoryId); + } + + bool doRemoveCategory(const QPlaceCategory &category, + QPlaceReply::Error expectedError = QPlaceReply::NoError) + { + return doRemoveCategory(placeManager, category, expectedError); + } + + bool doFetchCategory(const QString &categoryId, + QPlaceCategory *category, + QPlaceReply::Error expectedError = QPlaceReply::NoError) { + return doFetchCategory(placeManager, categoryId, + category, expectedError); + } + + bool doFetchContent(const QPlaceContentRequest &request, + QPlaceContent::Collection *results, + QPlaceReply::Error expectedError = QPlaceReply::NoError) + { + return doFetchContent(placeManager, request, results, expectedError); + } + + bool doMatch(const QPlaceMatchRequest &request, + QList *places, + QPlaceReply::Error expectedError = QPlaceReply::NoError) { + return doMatch(placeManager, request, + places, expectedError); + } + + bool checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError) { + return checkSignals(reply, expectedError, placeManager); + } + + QPlaceManager *placeManager; +}; + +#endif + diff --git a/tests/auto/placesplugin_unsupported/placesplugin.json b/tests/auto/placesplugin_unsupported/placesplugin.json new file mode 100644 index 0000000..6749962 --- /dev/null +++ b/tests/auto/placesplugin_unsupported/placesplugin.json @@ -0,0 +1,8 @@ +{ + "Keys": ["test.places.unsupported"], + "Provider": "test.places.unsupported", + "Version": 1, + "Experimental": true, + "Features": [ + ] +} diff --git a/tests/auto/placesplugin_unsupported/placesplugin_unsupported.pro b/tests/auto/placesplugin_unsupported/placesplugin_unsupported.pro new file mode 100644 index 0000000..65c6a39 --- /dev/null +++ b/tests/auto/placesplugin_unsupported/placesplugin_unsupported.pro @@ -0,0 +1,14 @@ +TARGET = qtgeoservices_placesplugin_unsupported +QT += location + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = UnsupportedPlacesGeoServicePlugin +PLUGIN_EXTENDS = - +load(qt_plugin) + +HEADERS += qgeoserviceproviderplugin_test.h + +SOURCES += qgeoserviceproviderplugin_test.cpp + +OTHER_FILES += \ + placesplugin.json diff --git a/tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.cpp b/tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.cpp new file mode 100644 index 0000000..f94e52c --- /dev/null +++ b/tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.cpp @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderplugin_test.h" + +#include +#include + +QGeoServiceProviderFactoryTest::QGeoServiceProviderFactoryTest() +{ +} + +QGeoServiceProviderFactoryTest::~QGeoServiceProviderFactoryTest() +{ +} + +QPlaceManagerEngine *QGeoServiceProviderFactoryTest::createPlaceManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const +{ + Q_UNUSED(error); + Q_UNUSED(errorString); + + return new QPlaceManagerEngine(parameters); +} diff --git a/tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.h b/tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.h new file mode 100644 index 0000000..11c30d2 --- /dev/null +++ b/tests/auto/placesplugin_unsupported/qgeoserviceproviderplugin_test.h @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_TEST_H +#define QGEOSERVICEPROVIDER_TEST_H + +#include + +QT_USE_NAMESPACE + +class QGeoServiceProviderFactoryTest : public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "placesplugin.json") + +public: + QGeoServiceProviderFactoryTest(); + ~QGeoServiceProviderFactoryTest(); + + QPlaceManagerEngine *createPlaceManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, + QString *errorString) const; +}; + +#endif + + diff --git a/tests/auto/positionplugin/plugin.cpp b/tests/auto/positionplugin/plugin.cpp new file mode 100644 index 0000000..919549d --- /dev/null +++ b/tests/auto/positionplugin/plugin.cpp @@ -0,0 +1,213 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class DummySource : public QGeoPositionInfoSource +{ + Q_OBJECT + +public: + DummySource(QObject *parent=0); + ~DummySource(); + + void startUpdates(); + void stopUpdates(); + void requestUpdate(int timeout=5000); + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const; + PositioningMethods supportedPositioningMethods() const; + + void setUpdateInterval(int msec); + int minimumUpdateInterval() const; + Error error() const; + +private: + QTimer *timer; + QTimer *timeoutTimer; + QTimer *singleTimer; + QGeoPositionInfo lastPosition; + QDateTime lastUpdateTime; + +private slots: + void updatePosition(); + void doTimeout(); +}; + +DummySource::DummySource(QObject *parent) : + QGeoPositionInfoSource(parent), + timer(new QTimer(this)), + timeoutTimer(new QTimer(this)), + singleTimer(new QTimer(this)), + lastPosition(QGeoCoordinate(0,0), QDateTime::currentDateTime()) +{ + timer->setInterval(1000); + connect(timer, SIGNAL(timeout()), + this, SLOT(updatePosition())); + connect(singleTimer, SIGNAL(timeout()), + this, SLOT(updatePosition())); + connect(timeoutTimer, SIGNAL(timeout()), + this, SLOT(doTimeout())); +} + +QGeoPositionInfoSource::Error DummySource::error() const +{ + return QGeoPositionInfoSource::NoError; +} + + +void DummySource::setUpdateInterval(int msec) +{ + if (msec == 0) { + timer->setInterval(1000); + } else if (msec < 1000) { + msec = 1000; + timer->setInterval(msec); + } else { + timer->setInterval(msec); + } + + QGeoPositionInfoSource::setUpdateInterval(msec); +} + +int DummySource::minimumUpdateInterval() const +{ + return 1000; +} + +QGeoPositionInfo DummySource::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const +{ + Q_UNUSED(fromSatellitePositioningMethodsOnly); + return lastPosition; +} + +QGeoPositionInfoSource::PositioningMethods DummySource::supportedPositioningMethods() const +{ + return QGeoPositionInfoSource::AllPositioningMethods; +} + +void DummySource::startUpdates() +{ + timer->start(); +} + +void DummySource::stopUpdates() +{ + timer->stop(); +} + +void DummySource::requestUpdate(int timeout) +{ + if (timeout == 0) + timeout = 5000; + if (timeout < 0) + timeout = 0; + + timeoutTimer->setInterval(timeout); + timeoutTimer->start(); + + if (timer->isActive()) { + timer->stop(); + timer->start(); + } + + singleTimer->setInterval(1000); + singleTimer->start(); +} + +DummySource::~DummySource() +{} + +void DummySource::updatePosition() +{ + timeoutTimer->stop(); + singleTimer->stop(); + + const QDateTime now = QDateTime::currentDateTime(); + + QGeoCoordinate coord(lastPosition.coordinate().latitude() + 0.1, + lastPosition.coordinate().longitude() + 0.1); + + QGeoPositionInfo info(coord, now); + info.setAttribute(QGeoPositionInfo::Direction, lastPosition.coordinate().azimuthTo(coord)); + if (lastUpdateTime.isValid()) { + double speed = lastPosition.coordinate().distanceTo(coord) / lastUpdateTime.msecsTo(now); + info.setAttribute(QGeoPositionInfo::GroundSpeed, 1000 * speed); + } + + lastUpdateTime = now; + lastPosition = info; + emit positionUpdated(info); +} + +void DummySource::doTimeout() +{ + timeoutTimer->stop(); + singleTimer->stop(); + emit updateTimeout(); +} + + +class QGeoPositionInfoSourceFactoryTest : public QObject, public QGeoPositionInfoSourceFactory +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.qt.position.sourcefactory/5.0" + FILE "plugin.json") + Q_INTERFACES(QGeoPositionInfoSourceFactory) + +public: + QGeoPositionInfoSource *positionInfoSource(QObject *parent); + QGeoSatelliteInfoSource *satelliteInfoSource(QObject *parent); + QGeoAreaMonitorSource *areaMonitor(QObject *parent); +}; + +QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryTest::positionInfoSource(QObject *parent) +{ + return new DummySource(parent); +} + +QGeoSatelliteInfoSource *QGeoPositionInfoSourceFactoryTest::satelliteInfoSource(QObject *parent) +{ + Q_UNUSED(parent) + // not implemented + return 0; +} + +QGeoAreaMonitorSource *QGeoPositionInfoSourceFactoryTest::areaMonitor(QObject* parent) +{ + Q_UNUSED(parent) + return 0; +} + +#include "plugin.moc" diff --git a/tests/auto/positionplugin/plugin.json b/tests/auto/positionplugin/plugin.json new file mode 100644 index 0000000..68acade --- /dev/null +++ b/tests/auto/positionplugin/plugin.json @@ -0,0 +1,9 @@ +{ + "Keys": ["test.source"], + "Provider": "test.source", + "Position": true, + "Satellite": false, + "Monitor": false, + "Priority": 0, + "Testable": true +} diff --git a/tests/auto/positionplugin/positionplugin.pro b/tests/auto/positionplugin/positionplugin.pro new file mode 100644 index 0000000..dd04e7f --- /dev/null +++ b/tests/auto/positionplugin/positionplugin.pro @@ -0,0 +1,12 @@ +TARGET = qtposition_testplugin +QT += positioning + +PLUGIN_TYPE = position +PLUGIN_CLASS_NAME = TestPositionPlugin +PLUGIN_EXTENDS = - +load(qt_plugin) + +SOURCES += plugin.cpp + +OTHER_FILES += \ + plugin.json diff --git a/tests/auto/positionplugintest/positionplugintest.pro b/tests/auto/positionplugintest/positionplugintest.pro new file mode 100644 index 0000000..4602be7 --- /dev/null +++ b/tests/auto/positionplugintest/positionplugintest.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_positionplugin + +SOURCES += tst_positionplugin.cpp + +CONFIG -= app_bundle + +QT += positioning testlib diff --git a/tests/auto/positionplugintest/tst_positionplugin.cpp b/tests/auto/positionplugintest/tst_positionplugin.cpp new file mode 100644 index 0000000..032b11f --- /dev/null +++ b/tests/auto/positionplugintest/tst_positionplugin.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_PositionPlugin : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void availableSources(); + void create(); + void getUpdates(); +}; + +void tst_PositionPlugin::initTestCase() +{ +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif +} + +void tst_PositionPlugin::availableSources() +{ + QVERIFY(QGeoPositionInfoSource::availableSources().contains("test.source")); + QVERIFY(!QGeoSatelliteInfoSource::availableSources().contains("test.source")); + QVERIFY(!QGeoAreaMonitorSource::availableSources().contains("test.source")); +} + +void tst_PositionPlugin::create() +{ + QGeoPositionInfoSource *src = 0; + src = QGeoPositionInfoSource::createSource("test.source", 0); + QVERIFY(src != 0); + + QVERIFY(src->minimumUpdateInterval() == 1000); + + src = QGeoPositionInfoSource::createSource("invalid source that will never exist", 0); + QVERIFY(src == 0); + + QGeoSatelliteInfoSource *ssrc = 0; + ssrc = QGeoSatelliteInfoSource::createSource("test.source", 0); + QVERIFY(ssrc == 0); +} + +void tst_PositionPlugin::getUpdates() +{ + QGeoPositionInfoSource *src = QGeoPositionInfoSource::createSource("test.source", 0); + src->setUpdateInterval(1000); + + QSignalSpy spy(src, SIGNAL(positionUpdated(QGeoPositionInfo))); + src->startUpdates(); + QTest::qWait(1500); + QCOMPARE(spy.count(), 1); + QCOMPARE(spy[0].size(), 1); + + QGeoPositionInfo info = qvariant_cast(spy[0][0]); + QCOMPARE(info.coordinate().latitude(), 0.1); + QCOMPARE(info.coordinate().longitude(), 0.1); +} + + + +QTEST_GUILESS_MAIN(tst_PositionPlugin) +#include "tst_positionplugin.moc" diff --git a/tests/auto/qgeoaddress/qgeoaddress.pro b/tests/auto/qgeoaddress/qgeoaddress.pro new file mode 100644 index 0000000..e12b9e1 --- /dev/null +++ b/tests/auto/qgeoaddress/qgeoaddress.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeoaddress + +SOURCES += tst_qgeoaddress.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeoaddress/tst_qgeoaddress.cpp b/tests/auto/qgeoaddress/tst_qgeoaddress.cpp new file mode 100644 index 0000000..7b012f1 --- /dev/null +++ b/tests/auto/qgeoaddress/tst_qgeoaddress.cpp @@ -0,0 +1,559 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QGeoAddress : public QObject +{ + Q_OBJECT + +public: + tst_QGeoAddress(); + +private Q_SLOTS: + void constructorTest(); + void textTest(); +//TODO: there are various field we don't have yet in QGeoAddress +// will need to either remove or enable these tests +// void additionalDataTest(); +// void alternativeAttributesTest(); + void cityTest(); + void countryCodeTest(); + void countryTest(); + void countyTest(); + void districtTest(); +// void floorTest(); +// void houseNumberTest(); +// void labelTest(); + void postalCodeTest(); + void stateTest(); + void streetTest(); +// void suiteTest(); + void generatedText(); + void generatedText_data(); + void operatorsTest(); + void emptyClearTest(); +}; + +tst_QGeoAddress::tst_QGeoAddress() +{ +} + +void tst_QGeoAddress::constructorTest() +{ + QGeoAddress testObj; + + testObj.setStreet("testId"); + QGeoAddress *testObjPtr = new QGeoAddress(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QGeoAddress::textTest() +{ + QGeoAddress address; + QVERIFY(address.text().isEmpty()); + address.setText(QStringLiteral("123 Fake Street\nSpringfield")); + QCOMPARE(address.text(), QStringLiteral("123 Fake Street\nSpringfield")); +} + +void tst_QGeoAddress::cityTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.city() == QString(), "Wrong default value"); + testObj.setCity("testText"); + QVERIFY2(testObj.city() == "testText", "Wrong value returned"); +} + +void tst_QGeoAddress::countryCodeTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.countryCode() == QString(), "Wrong default value"); + testObj.setCountryCode("testText"); + QVERIFY2(testObj.countryCode() == "testText", "Wrong value returned"); +} + +void tst_QGeoAddress::countryTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.country() == QString(), "Wrong default value"); + testObj.setCountry("testText"); + QVERIFY2(testObj.country() == "testText", "Wrong value returned"); +} + +void tst_QGeoAddress::countyTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.county() == QString(), "Wrong default value"); + testObj.setCounty("testText"); + QVERIFY2(testObj.county() == "testText", "Wrong value returned"); +} + +void tst_QGeoAddress::districtTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.district() == QString(), "Wrong default value"); + testObj.setDistrict("testText"); + QVERIFY2(testObj.district() == "testText", "Wrong value returned"); +} + +// TODO: currently don't have floor in QGeoAddress +//void tst_QGeoAddress::floorTest() +//{ +// QGeoAddress testObj; +// QVERIFY2(testObj.floor() == QString(), "Wrong default value"); +// testObj.setFloor("testText"); +// QVERIFY2(testObj.floor() == "testText", "Wrong value returned"); +//} + +//TODO: Atm not sure if we will have house number in API. +//void tst_QGeoAddress::houseNumberTest() +//{ +// QGeoAddress testObj; +// QVERIFY2(testObj.houseNumber() == QString(), "Wrong default value"); +// testObj.setHouseNumber("testText"); +// QVERIFY2(testObj.houseNumber() == "testText", "Wrong value returned"); +//} + +//void tst_QGeoAddress::labelTest() +//{ +// QGeoAddress testObj; +// QVERIFY2(testObj.label() == QString(), "Wrong default value"); +// testObj.setLabel("testText"); +// QVERIFY2(testObj.label() == "testText", "Wrong value returned"); +//} + +void tst_QGeoAddress::postalCodeTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.postalCode() == QString(), "Wrong default value"); + testObj.setPostalCode("testText"); + QVERIFY2(testObj.postalCode() == "testText", "Wrong value returned"); +} + +void tst_QGeoAddress::stateTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.state() == QString(), "Wrong default value"); + testObj.setState("testText"); + QVERIFY2(testObj.state() == "testText", "Wrong value returned"); +} + +void tst_QGeoAddress::streetTest() +{ + QGeoAddress testObj; + QVERIFY2(testObj.street() == QString(), "Wrong default value"); + testObj.setStreet("testText"); + QVERIFY2(testObj.street() == "testText", "Wrong value returned"); +} + +void tst_QGeoAddress::generatedText() +{ + QFETCH(QString, countryCode); + QFETCH(QString, expectedPostalCodeOnly); + QFETCH(QString, expectedFullAddress); + + QGeoAddress streetOnly; + streetOnly.setStreet("street"); + streetOnly.setCountryCode(countryCode); + + QCOMPARE(streetOnly.text(), QStringLiteral("street")); + + QGeoAddress cityOnly; + cityOnly.setCity("city"); + cityOnly.setCountryCode(countryCode); + if (countryCode == QLatin1String("CYM") || countryCode == QLatin1String("IRL")) + QCOMPARE(cityOnly.text(), QString()); + else + QCOMPARE(cityOnly.text(), QStringLiteral("city")); + + QGeoAddress postalCodeOnly; + postalCodeOnly.setPostalCode("postcode"); + postalCodeOnly.setCountryCode(countryCode); + QCOMPARE(postalCodeOnly.text(), expectedPostalCodeOnly); + + QGeoAddress fullAddress; + fullAddress.setStreet("street"); + fullAddress.setDistrict("district"); + fullAddress.setPostalCode("postcode"); + fullAddress.setCity("city"); + fullAddress.setState("state"); + fullAddress.setCountry("country"); + fullAddress.setCountryCode(countryCode); + + QCOMPARE(fullAddress.text(), expectedFullAddress); +} + +void tst_QGeoAddress::generatedText_data() +{ + QTest::addColumn("countryCode"); + QTest::addColumn("expectedPostalCodeOnly"); + QTest::addColumn("expectedFullAddress"); + + QTest::newRow("Albania") << QString::fromLatin1("ALB") + << QString::fromLatin1("postcode") /* postal code only */ + << QString::fromLatin1("street
    " /* full address */ + "postcode, city
    " + "country"); + + QTest::newRow("Andorra") << QString::fromLatin1("AND") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("United Arab Emirates") << QString::fromLatin1("ARE") + << QString() + << QString::fromLatin1("street
    " + "district city
    " + "country"); + QTest::newRow("Australia") << QString::fromLatin1("AUS") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district state postcode
    " + "country"); + QTest::newRow("Austria") << QString::fromLatin1("AUT") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Bahamas") << QString::fromLatin1("BHS") + << QString() + << QString::fromLatin1("street
    " + "district city
    " + "country"); + QTest::newRow("Bahrain") << QString::fromLatin1("BHR") + << QString() + << QString::fromLatin1("street
    " + "district, city, state
    " + "country"); + QTest::newRow("Brazil") << QString::fromLatin1("BRA") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district city-state postcode
    " + "country"); + QTest::newRow("Brunei Darussalam") << QString::fromLatin1("BRN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district city postcode
    " + "country"); + QTest::newRow("Canada") << QString::fromLatin1("CAN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city, state postcode
    " + "country"); + QTest::newRow("China") << QString::fromLatin1("CHN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street, city
    " + "postcode state
    " + "country"); + QTest::newRow("Chile") << QString::fromLatin1("CHL") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode district, city, state
    " + "country"); + QTest::newRow("Cayman Islands") << QString::fromLatin1("CYM") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "state postcode
    " + "country"); + QTest::newRow("France") << QString::fromLatin1("FRA") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + + QTest::newRow("United Kingdom") << QString::fromLatin1("GBR") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district, city, postcode
    " + "country"); + QTest::newRow("Gibraltar") << QString::fromLatin1("GIB") + << QString() + << QString::fromLatin1("street
    " + "city
    " + "country"); + QTest::newRow("Guadeloupe") << QString::fromLatin1("GLP") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("French Guiana") << QString::fromLatin1("GUF") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Hong Kong") << QString::fromLatin1("HKG") + << QString() + << QString::fromLatin1("street
    " + "district
    " + "city"); + QTest::newRow("India") << QString::fromLatin1("IND") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city postcode state
    " + "country"); + QTest::newRow("Indonesia") << QString::fromLatin1("IDN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city, postcode
    " + "country"); + QTest::newRow("Ireland") << QString::fromLatin1("IRL") + << QString() + << QString::fromLatin1("street
    " + "district, state
    " + "country"); + QTest::newRow("Italy") << QString::fromLatin1("ITA") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Jersey") << QString::fromLatin1("JEY") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city, postcode
    " + "country"); + QTest::newRow("Jordan") << QString::fromLatin1("JOR") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district city postcode
    " + "country"); + QTest::newRow("Kuwait") << QString::fromLatin1("KWT") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode, district, city
    " + "country"); + QTest::newRow("Latvia") << QString::fromLatin1("LVA") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city, postcode
    " + "country"); + QTest::newRow("Lebanon") << QString::fromLatin1("LBN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district city postcode
    " + "country"); + QTest::newRow("Luxembourg") << QString::fromLatin1("LUX") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Malta") << QString::fromLatin1("MLT") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city postcode
    " + "country"); + QTest::newRow("Monaco") << QString::fromLatin1("MCO") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Mexico") << QString::fromLatin1("MEX") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district
    " + "postcode city, state
    " + "country"); + QTest::newRow("Martinique") << QString::fromLatin1("MTQ") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode, city
    " + "country"); + QTest::newRow("Malaysia") << QString::fromLatin1("MYS") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "state
    " + "country"); + QTest::newRow("New Zealand") << QString::fromLatin1("NZL") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district city postcode
    " + "country"); + QTest::newRow("Oman") << QString::fromLatin1("OMN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district, postcode, city, country"); + QTest::newRow("Puerto Rico") << QString::fromLatin1("PRI") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "district, city, state, postcode
    " + "country"); + QTest::newRow("Qatar") << QString::fromLatin1("QAT") + << QString() + << QString::fromLatin1("street
    " + "district city, country"); + QTest::newRow("Reunion") << QString::fromLatin1("REU") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Russian Federation") << QString::fromLatin1("RUS") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Saudi Arabia") << QString::fromLatin1("SAU") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street district
    " + "city postcode
    " + "country"); + QTest::newRow("Singapore") << QString::fromLatin1("SGP") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city postcode
    " + "country"); + QTest::newRow("Marino") << QString::fromLatin1("SMR") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Taiwan") << QString::fromLatin1("TWN") + << QString() + << QString::fromLatin1("street, district, city
    " + "country"); + QTest::newRow("Thailand") << QString::fromLatin1("THA") + << QString("postcode") + << QString::fromLatin1("street
    " + "district, city postcode
    " + "country"); + QTest::newRow("Turkey") << QString::fromLatin1("TUR") + << QString("postcode") + << QString::fromLatin1("street
    " + "postcode district, city
    " + "country"); + QTest::newRow("Ukraine") << QString::fromLatin1("UKR") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city postcode
    " + "country"); + QTest::newRow("United States") << QString::fromLatin1("USA") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city, state postcode
    " + "country"); + QTest::newRow("Virgin Islands, US") << QString::fromLatin1("VIR") + << QString("postcode") + << QString::fromLatin1("street
    " + "city, state postcode
    " + "country"); + QTest::newRow("Vatican City State") << QString::fromLatin1("VAT") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); + QTest::newRow("Venezuela") << QString::fromLatin1("VEN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "city postcode, state
    " + "country"); + QTest::newRow("South Africa") << QString::fromLatin1("ZAF") + << QString() + << QString::fromLatin1("street
    " + "district, city
    " + "country"); + QTest::newRow("Finland") << QString::fromLatin1("FIN") + << QString::fromLatin1("postcode") + << QString::fromLatin1("street
    " + "postcode city
    " + "country"); +} + +// TODO: curenlty we don't have suite in QGeoAddress +// will need to either remove or enable +//void tst_QGeoAddress::suiteTest() +//{ +// QGeoAddress testObj; +// QVERIFY2(testObj.suite() == QString(), "Wrong default value"); +// testObj.setSuite("testText"); +// QVERIFY2(testObj.suite() == "testText", "Wrong value returned"); +//} + +void tst_QGeoAddress::operatorsTest() +{ + QGeoAddress testObj; + testObj.setStreet("testValue"); + QGeoAddress testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setCountry("testValue2"); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QGeoAddress::emptyClearTest() +{ + QGeoAddress testObj; + QVERIFY(testObj.isEmpty()); + + testObj.setCountry(QStringLiteral("country")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setCountryCode(QStringLiteral("countryCode")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setState(QStringLiteral("state")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setCounty(QStringLiteral("county")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setCity(QStringLiteral("city")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setDistrict(QStringLiteral("district")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setPostalCode(QStringLiteral("postalCode")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setStreet(QStringLiteral("street")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + testObj.setText(QStringLiteral("formatted address")); + QVERIFY(!testObj.isEmpty()); + testObj.clear(); + + QVERIFY(testObj.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QGeoAddress) + +#include "tst_qgeoaddress.moc" diff --git a/tests/auto/qgeoareamonitor/logfilepositionsource.cpp b/tests/auto/qgeoareamonitor/logfilepositionsource.cpp new file mode 100644 index 0000000..a613d6e --- /dev/null +++ b/tests/auto/qgeoareamonitor/logfilepositionsource.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "logfilepositionsource.h" + +LogFilePositionSource::LogFilePositionSource(QObject *parent) + : QGeoPositionInfoSource(parent), + logFile(new QFile(this)), + timer(new QTimer(this)) +{ + connect(timer, SIGNAL(timeout()), this, SLOT(readNextPosition())); + + logFile->setFileName(QFINDTESTDATA("simplelog.txt")); + if (!logFile->open(QIODevice::ReadOnly)) + qWarning() << "Error: cannot open source file" << logFile->fileName(); +} + +QGeoPositionInfo LogFilePositionSource::lastKnownPosition(bool /*fromSatellitePositioningMethodsOnly*/) const +{ + return lastPosition; +} + +LogFilePositionSource::PositioningMethods LogFilePositionSource::supportedPositioningMethods() const +{ + return AllPositioningMethods; +} + +int LogFilePositionSource::minimumUpdateInterval() const +{ + return 200; +} + +void LogFilePositionSource::startUpdates() +{ + int interval = updateInterval(); + if (interval < minimumUpdateInterval()) + interval = minimumUpdateInterval(); + + timer->start(interval); +} + +void LogFilePositionSource::stopUpdates() +{ + timer->stop(); +} + +void LogFilePositionSource::requestUpdate(int /*timeout*/) +{ + // For simplicity, ignore timeout - assume that if data is not available + // now, no data will be added to the file later + if (logFile->canReadLine()) + readNextPosition(); + else + emit updateTimeout(); +} + +void LogFilePositionSource::readNextPosition() +{ + QByteArray line = logFile->readLine().trimmed(); + if (!line.isEmpty()) { + QList data = line.split(' '); + double latitude; + double longitude; + bool hasLatitude = false; + bool hasLongitude = false; + QDateTime timestamp = QDateTime::fromString(QString(data.value(0)), Qt::ISODate); + latitude = data.value(1).toDouble(&hasLatitude); + longitude = data.value(2).toDouble(&hasLongitude); + + if (hasLatitude && hasLongitude && timestamp.isValid()) { + QGeoCoordinate coordinate(latitude, longitude); + QGeoPositionInfo info(coordinate, timestamp); + if (info.isValid()) { + lastPosition = info; + emit positionUpdated(info); + } + } + } +} + +QGeoPositionInfoSource::Error LogFilePositionSource::error() const +{ + return QGeoPositionInfoSource::NoError; +} diff --git a/tests/auto/qgeoareamonitor/logfilepositionsource.h b/tests/auto/qgeoareamonitor/logfilepositionsource.h new file mode 100644 index 0000000..3768d75 --- /dev/null +++ b/tests/auto/qgeoareamonitor/logfilepositionsource.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef LOGFILEPOSITIONSOURCE_H +#define LOGFILEPOSITIONSOURCE_H + +#include + +QT_BEGIN_NAMESPACE +class QFile; +class QTimer; +QT_END_NAMESPACE + +class LogFilePositionSource : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + LogFilePositionSource(QObject *parent = 0); + + QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly = false) const; + + PositioningMethods supportedPositioningMethods() const; + int minimumUpdateInterval() const; + Error error() const; + +public slots: + virtual void startUpdates(); + virtual void stopUpdates(); + + virtual void requestUpdate(int timeout = 5000); + +private slots: + void readNextPosition(); + +private: + QFile *logFile; + QTimer *timer; + QGeoPositionInfo lastPosition; +}; + +#endif diff --git a/tests/auto/qgeoareamonitor/qgeoareamonitor.pro b/tests/auto/qgeoareamonitor/qgeoareamonitor.pro new file mode 100644 index 0000000..d084df6 --- /dev/null +++ b/tests/auto/qgeoareamonitor/qgeoareamonitor.pro @@ -0,0 +1,14 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeoareamonitor + +SOURCES += tst_qgeoareamonitor.cpp \ + logfilepositionsource.cpp + +HEADERS += logfilepositionsource.h + +OTHER_FILES += *.txt + +CONFIG -= app_bundle + +QT += positioning testlib diff --git a/tests/auto/qgeoareamonitor/simplelog.txt b/tests/auto/qgeoareamonitor/simplelog.txt new file mode 100644 index 0000000..5a14fb8 --- /dev/null +++ b/tests/auto/qgeoareamonitor/simplelog.txt @@ -0,0 +1,87 @@ +2009-08-24T22:24:34 -27.54 153.090718 +2009-08-24T22:24:35 -27.55 153.090718 +2009-08-24T22:24:36 -27.56 153.090718 +2009-08-24T22:24:37 -27.57 153.090718 +2009-08-24T22:24:38 -27.58 153.090783 +2009-08-24T22:24:39 -27.59 153.090845 +2009-08-24T22:24:40 -27.60 153.090908 +2009-08-24T22:24:41 -27.61 153.090971 +2009-08-24T22:24:42 -27.62 153.091036 +2009-08-24T22:24:43 -27.63 153.091102 +2009-08-24T22:24:44 -27.64 153.091167 +2009-08-24T22:24:45 -27.65 153.091232 +2009-08-24T22:24:46 -27.65 153.091298 +2009-08-24T22:24:47 -27.65 153.091366 +2009-08-24T22:24:48 -27.65 153.091435 +2009-08-24T22:24:49 -27.66 153.091507 +2009-08-24T22:24:50 -27.67 153.091581 +2009-08-24T22:24:51 -27.68 153.091654 +2009-08-24T22:24:52 -27.69 153.091729 +2009-08-24T22:24:53 -27.70 153.091800 +2009-08-24T22:24:54 -27.71 153.091870 +2009-08-24T22:24:55 -27.72 153.091940 +2009-08-24T22:24:56 -27.73 153.092010 +2009-08-24T22:24:57 -27.74 153.092078 +2009-08-24T22:24:58 -27.75 153.092144 +2009-08-24T22:24:59 -27.78 153.092218 +2009-08-24T22:25:00 -27.79 153.092308 +2009-08-24T22:25:01 -27.80 153.092415 +2009-08-24T22:25:02 -27.81 153.092530 +2009-08-24T22:25:03 -27.82 153.092648 +2009-08-24T22:25:04 -27.83 153.092763 +2009-08-24T22:25:05 -27.84 153.092879 +2009-08-24T22:25:06 -27.85 153.092990 +2009-08-24T22:25:07 -27.84 153.093099 +2009-08-24T22:25:08 -27.83 153.093204 +2009-08-24T22:25:09 -27.82 153.093303 +2009-08-24T22:25:10 -27.81 153.093396 +2009-08-24T22:25:11 -27.80 153.093484 +2009-08-24T22:25:12 -27.79 153.093568 +2009-08-24T22:25:13 -27.78 153.093647 +2009-08-24T22:25:14 -27.77 153.093727 +2009-08-24T22:25:15 -27.76 153.093810 +2009-08-24T22:25:16 -27.75 153.093896 +2009-08-24T22:25:17 -27.74 153.093984 +2009-08-24T22:25:18 -27.72 153.094074 +2009-08-24T22:25:19 -27.70 153.094168 +2009-08-24T22:25:20 -27.71 153.094267 +2009-08-24T22:25:21 -27.69 153.094370 +2009-08-24T22:25:22 -27.68 153.094474 +2009-08-24T22:25:23 -27.67 153.094581 +2009-08-24T22:25:24 -27.66 153.094688 +2009-08-24T22:25:25 -27.65 153.094796 +2009-08-24T22:25:26 -27.64 153.094905 +2009-08-24T22:25:27 -27.63 153.095012 +2009-08-24T22:25:28 -27.62 153.095121 +2009-08-24T22:25:29 -27.61 153.095231 +2009-08-24T22:25:30 -27.60 153.095340 +2009-08-24T22:25:31 -27.59 153.095449 +2009-08-24T22:25:32 -27.58 153.095558 +2009-08-24T22:25:33 -27.57 153.095667 +2009-08-24T22:25:34 -27.56 153.095776 +2009-08-24T22:25:35 -27.55 153.095885 +2009-08-24T22:25:36 -27.54 153.095995 +2009-08-24T22:25:37 -27.53 153.096109 +2009-08-24T22:25:38 -27.52 153.096226 +2009-08-24T22:25:39 -27.51 153.096337 +2009-08-24T22:25:40 -27.50 153.096441 +2009-08-24T22:25:41 -27.49 153.096537 +2009-08-24T22:25:42 -27.48 153.096628 +2009-08-24T22:25:43 -27.47 153.096714 +2009-08-24T22:25:44 -27.46 153.096795 +2009-08-24T22:25:45 -27.45 153.096847 +2009-08-24T22:25:46 -27.44 153.096855 +2009-08-24T22:25:47 -27.43 153.096873 +2009-08-24T22:25:48 -27.42 153.096875 +2009-08-24T22:25:49 -27.41 153.096878 +2009-08-24T22:25:50 -27.40 153.096880 +2009-08-24T22:25:51 -27.39 153.096880 +2009-08-24T22:25:52 -27.38 153.096881 +2009-08-24T22:25:53 -27.37 153.096882 +2009-08-24T22:25:54 -27.36 153.096883 +2009-08-24T22:25:55 -27.35 153.096883 +2009-08-24T22:25:56 -27.34 153.096883 +2009-08-24T22:25:57 -27.33 153.096890 +2009-08-24T22:25:58 -27.32 153.096919 +2009-08-24T22:25:59 -27.31 153.096985 +2009-08-24T22:26:00 -27.30 153.097060 diff --git a/tests/auto/qgeoareamonitor/tst_qgeoareamonitor.cpp b/tests/auto/qgeoareamonitor/tst_qgeoareamonitor.cpp new file mode 100644 index 0000000..bad3be4 --- /dev/null +++ b/tests/auto/qgeoareamonitor/tst_qgeoareamonitor.cpp @@ -0,0 +1,760 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "logfilepositionsource.h" + + +QT_USE_NAMESPACE +#define UPDATE_INTERVAL 200 + +Q_DECLARE_METATYPE(QGeoAreaMonitorInfo) + +QString tst_qgeoareamonitorinfo_debug; + +void tst_qgeoareamonitorinfo_messageHandler(QtMsgType type, + const QMessageLogContext &, + const QString &msg) +{ + switch (type) { + case QtDebugMsg : + tst_qgeoareamonitorinfo_debug = msg; + break; + default: + break; + } +} + +class tst_QGeoAreaMonitorSource : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase() + { +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif + qRegisterMetaType(); + } + + void init() + { + } + + void cleanup() + { + QGeoAreaMonitorSource *obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + + QList list = obj->activeMonitors(); + if (list.count() > 0) { + //cleanup installed monitors + foreach (const QGeoAreaMonitorInfo& info, list) { + QVERIFY(obj->stopMonitoring(info)); + } + } + QVERIFY(obj->activeMonitors().count() == 0); + } + + void cleanupTestCase() + { + } + + void tst_monitor() + { + QGeoAreaMonitorInfo defaultMonitor; + QVERIFY(defaultMonitor.name().isEmpty()); + QVERIFY(!defaultMonitor.identifier().isEmpty()); + QCOMPARE(defaultMonitor.isPersistent(), false); + QVERIFY(!defaultMonitor.area().isValid()); + QVERIFY(!defaultMonitor.isValid()); + QCOMPARE(defaultMonitor.expiration(), QDateTime()); + QCOMPARE(defaultMonitor.notificationParameters(), QVariantMap()); + + QGeoAreaMonitorSource *obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + QVERIFY(!obj->startMonitoring(defaultMonitor)); + QCOMPARE(obj->activeMonitors().count(), 0); + QVERIFY(!obj->requestUpdate(defaultMonitor, + SIGNAL(areaEntered(QGeoMonitorInfo,QGeoAreaPositionInfo)))); + delete obj; + + //copy constructor based + QGeoAreaMonitorInfo copy(defaultMonitor); + QVERIFY(copy.name().isEmpty()); + QCOMPARE(copy.identifier(), defaultMonitor.identifier()); + QVERIFY(copy == defaultMonitor); + QVERIFY(!(copy != defaultMonitor)); + QCOMPARE(copy.isPersistent(), false); + + copy.setName(QString("my name")); + QCOMPARE(copy.name(), QString("my name")); + + + QDateTime now = QDateTime::currentDateTime().addSecs(1000); //little bit in the future + copy.setExpiration(now); + QVERIFY(copy != defaultMonitor); + QCOMPARE(copy.expiration(), now); + + QCOMPARE(copy.isPersistent(), defaultMonitor.isPersistent()); + copy.setPersistent(true); + QCOMPARE(copy.isPersistent(), true); + QCOMPARE(defaultMonitor.isPersistent(), false); + copy.setPersistent(false); + + QVERIFY(copy.area() == defaultMonitor.area()); + QVERIFY(!copy.area().isValid()); + copy.setArea(QGeoCircle(QGeoCoordinate(1, 2), 4)); + QVERIFY(copy.area().isValid()); + QVERIFY(copy.area() != defaultMonitor.area()); + QVERIFY(copy.area().contains(QGeoCoordinate(1, 2))); + + QVERIFY(copy.notificationParameters().isEmpty()); + QVariantMap map; + map.insert(QString("MyKey"), QVariant(123)); + copy.setNotificationParameters(map); + QVERIFY(!copy.notificationParameters().isEmpty()); + QCOMPARE(copy.notificationParameters().value(QString("MyKey")).toInt(), 123); + QCOMPARE(defaultMonitor.notificationParameters().value(QString("MyKey")).toInt(), 0); + + QCOMPARE(defaultMonitor.identifier(), copy.identifier()); + + //assignment operator based + QGeoAreaMonitorInfo assignmentCopy; + assignmentCopy = copy; + QVERIFY(copy == assignmentCopy); + QVERIFY(assignmentCopy != defaultMonitor); + + QVERIFY(assignmentCopy.area().contains(QGeoCoordinate(1, 2))); + QCOMPARE(assignmentCopy.expiration(), now); + QCOMPARE(assignmentCopy.isPersistent(), false); + QCOMPARE(assignmentCopy.notificationParameters().value(QString("MyKey")).toInt(), 123); + QCOMPARE(defaultMonitor.identifier(), assignmentCopy.identifier()); + QCOMPARE(assignmentCopy.name(), QString("my name")); + + //validity checks for requestUpdate() + obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + QCOMPARE(obj->activeMonitors().count(), 0); + //reference -> should work + QVERIFY(obj->requestUpdate(copy, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo)))); + QCOMPARE(obj->activeMonitors().count(), 1); + //replaces areaEntered single shot + QVERIFY(obj->requestUpdate(copy, SIGNAL(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo)))); + QCOMPARE(obj->activeMonitors().count(), 1); + //replaces areaExited single shot + QVERIFY(obj->startMonitoring(copy)); + QCOMPARE(obj->activeMonitors().count(), 1); + + + //invalid signal + QVERIFY(!obj->requestUpdate(copy, 0)); + QCOMPARE(obj->activeMonitors().count(), 1); + + //signal that doesn't exist + QVERIFY(!obj->requestUpdate(copy, SIGNAL(areaEntered(QGeoMonitor)))); + QCOMPARE(obj->activeMonitors().count(), 1); + + QVERIFY(!obj->requestUpdate(copy, "SIGNAL(areaEntered(QGeoMonitor))")); + QCOMPARE(obj->activeMonitors().count(), 1); + + //ensure that we cannot add a persistent monitor to a source + //that doesn't support persistence + QGeoAreaMonitorInfo persistenceMonitor(copy); + persistenceMonitor.setPersistent(obj->supportedAreaMonitorFeatures() & QGeoAreaMonitorSource::PersistentAreaMonitorFeature); + persistenceMonitor.setPersistent(!persistenceMonitor.isPersistent()); + + QVERIFY(!obj->requestUpdate(persistenceMonitor, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo)))); + QCOMPARE(obj->activeMonitors().count(), 1); + QVERIFY(!obj->startMonitoring(persistenceMonitor)); + QCOMPARE(obj->activeMonitors().count(), 1); + + //ensure that persistence was only reason for rejection + persistenceMonitor.setPersistent(!persistenceMonitor.isPersistent()); + QVERIFY(obj->startMonitoring(persistenceMonitor)); + //persistenceMonitor is copy of already added monitor + //the last call was an update + QCOMPARE(obj->activeMonitors().count(), 1); + + delete obj; + } + + void tst_monitorValid() + { + QGeoAreaMonitorInfo mon; + QVERIFY(!mon.isValid()); + QCOMPARE(mon.name(), QString()); + QCOMPARE(mon.area().isValid(), false); + + QGeoAreaMonitorInfo mon2 = mon; + QVERIFY(!mon2.isValid()); + + QGeoShape invalidShape; + QGeoCircle emptyCircle(QGeoCoordinate(0,1), 0); + QGeoCircle validCircle(QGeoCoordinate(0,1), 1); + + //all invalid since no name set yet + mon2.setArea(invalidShape); + QVERIFY(mon2.area() == invalidShape); + QVERIFY(!mon2.isValid()); + + mon2.setArea(emptyCircle); + QVERIFY(mon2.area() == emptyCircle); + QVERIFY(!mon2.isValid()); + + mon2.setArea(validCircle); + QVERIFY(mon2.area() == validCircle); + QVERIFY(!mon2.isValid()); + + //valid since name and non-empy shape has been set + QGeoAreaMonitorInfo validMonitor("TestMonitor"); + QVERIFY(validMonitor.name() == QString("TestMonitor")); + QVERIFY(!validMonitor.isValid()); + + validMonitor.setArea(invalidShape); + QVERIFY(validMonitor.area() == invalidShape); + QVERIFY(!validMonitor.isValid()); + + validMonitor.setArea(emptyCircle); + QVERIFY(validMonitor.area() == emptyCircle); + QVERIFY(!validMonitor.isValid()); + + validMonitor.setArea(validCircle); + QVERIFY(validCircle == validMonitor.area()); + QVERIFY(validMonitor.isValid()); + } + + void tst_monitorStreaming() + { + QByteArray container; + QDataStream stream(&container, QIODevice::ReadWrite); + + QGeoAreaMonitorInfo monitor("someName"); + monitor.setArea(QGeoCircle(QGeoCoordinate(1,3), 5.4)); + QVERIFY(monitor.isValid()); + QCOMPARE(monitor.name(), QString("someName")); + + QGeoAreaMonitorInfo target; + QVERIFY(!target.isValid()); + QVERIFY(target.name().isEmpty()); + + QVERIFY(target != monitor); + + stream << monitor; + stream.device()->seek(0); + stream >> target; + + QVERIFY(target == monitor); + QVERIFY(target.isValid()); + QCOMPARE(target.name(), QString("someName")); + QVERIFY(target.area() == QGeoCircle(QGeoCoordinate(1,3), 5.4)); + } + + void tst_createDefaultSource() + { + QObject* parent = new QObject; + QGeoAreaMonitorSource* obj = QGeoAreaMonitorSource::createDefaultSource(parent); + QVERIFY(obj != 0); + QVERIFY(obj->parent() == parent); + delete obj; + + const QStringList monitors = QGeoAreaMonitorSource::availableSources(); + QVERIFY(!monitors.isEmpty()); + QVERIFY(monitors.contains(QStringLiteral("positionpoll"))); + + obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), parent); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + delete parent; + + obj = QGeoAreaMonitorSource::createSource(QStringLiteral("randomNonExistingName"), 0); + QVERIFY(obj == 0); + } + + void tst_activeMonitors() + { + QGeoAreaMonitorSource *obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + + LogFilePositionSource *source = new LogFilePositionSource(this); + source->setUpdateInterval(UPDATE_INTERVAL); + obj->setPositionInfoSource(source); + QCOMPARE(obj->positionInfoSource(), source); + + + QVERIFY(obj->activeMonitors().isEmpty()); + + QGeoAreaMonitorInfo mon("Monitor_Circle"); + mon.setArea(QGeoCircle(QGeoCoordinate(1,1), 1000)); + QVERIFY(obj->startMonitoring(mon)); + + QGeoAreaMonitorInfo mon2("Monitor_rectangle_below"); + QGeoRectangle r_below(QGeoCoordinate(1,1),2,2); + mon2.setArea(r_below); + QVERIFY(obj->startMonitoring(mon2)); + + QGeoAreaMonitorInfo mon3("Monitor_rectangle_above"); + QGeoRectangle r_above(QGeoCoordinate(2,1),2,2); + mon3.setArea(r_above); + QVERIFY(obj->startMonitoring(mon3)); + + QList results = obj->activeMonitors(); + QCOMPARE(results.count(), 3); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon || info == mon2 || info == mon3); + } + + results = obj->activeMonitors(QGeoShape()); + QCOMPARE(results.count(), 0); + + results = obj->activeMonitors(QGeoRectangle(QGeoCoordinate(1,1),0.2, 0.2)); + QCOMPARE(results.count(), 2); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon || info == mon2); + } + + results = obj->activeMonitors(QGeoCircle(QGeoCoordinate(1,1),1000)); + QCOMPARE(results.count(), 2); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon || info == mon2); + } + + results = obj->activeMonitors(QGeoCircle(QGeoCoordinate(2,1),1000)); + QCOMPARE(results.count(), 1); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon3); + } + + //same as above except that we use a different monitor source object instance + //all monitor objects of same type share same active monitors + QGeoAreaMonitorSource *secondObj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(secondObj != 0); + QCOMPARE(secondObj->sourceName(), QStringLiteral("positionpoll")); + + results = secondObj->activeMonitors(); + QCOMPARE(results.count(), 3); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon || info == mon2 || info == mon3); + } + + results = secondObj->activeMonitors(QGeoShape()); + QCOMPARE(results.count(), 0); + + results = secondObj->activeMonitors(QGeoRectangle(QGeoCoordinate(1,1),0.2, 0.2)); + QCOMPARE(results.count(), 2); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon || info == mon2); + } + + results = secondObj->activeMonitors(QGeoCircle(QGeoCoordinate(1,1),1000)); + QCOMPARE(results.count(), 2); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon || info == mon2); + } + + results = secondObj->activeMonitors(QGeoCircle(QGeoCoordinate(2,1),1000)); + QCOMPARE(results.count(), 1); + foreach (const QGeoAreaMonitorInfo& info, results) { + QVERIFY(info == mon3); + } + + delete obj; + delete secondObj; + } + + void tst_testExpiryTimeout() + { + QGeoAreaMonitorSource *obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + + QGeoAreaMonitorSource *secondObj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(secondObj != 0); + QCOMPARE(secondObj->sourceName(), QStringLiteral("positionpoll")); + + LogFilePositionSource *source = new LogFilePositionSource(this); + source->setUpdateInterval(UPDATE_INTERVAL); + obj->setPositionInfoSource(source); + + //Singleton pattern behind QGeoAreaMonitorSource ensures same position info source + QCOMPARE(obj->positionInfoSource(), source); + QCOMPARE(secondObj->positionInfoSource(), source); + + QSignalSpy expirySpy(obj, SIGNAL(monitorExpired(QGeoAreaMonitorInfo))); + QSignalSpy expirySpy2(secondObj, SIGNAL(monitorExpired(QGeoAreaMonitorInfo))); + + QDateTime now = QDateTime::currentDateTime(); + + const int monitorCount = 4; + for (int i = 1; i <= monitorCount; i++) { + QGeoAreaMonitorInfo mon(QString::number(i)); + mon.setArea(QGeoRectangle(QGeoCoordinate(i,i), i, i)); + mon.setExpiration(now.addSecs(i*5)); + QVERIFY(mon.isValid()); + QVERIFY(obj->startMonitoring(mon)); + } + + + + QCOMPARE(obj->activeMonitors().count(), monitorCount); + QCOMPARE(secondObj->activeMonitors().count(), monitorCount); + + QGeoAreaMonitorInfo info("InvalidExpiry"); + info.setArea(QGeoRectangle(QGeoCoordinate(10,10), 1, 1 )); + QVERIFY(info.isValid()); + info.setExpiration(now.addSecs(-1000)); + QVERIFY(info.expiration() < now); + QVERIFY(!obj->startMonitoring(info)); + QCOMPARE(obj->activeMonitors().count(), monitorCount); + QVERIFY(!obj->requestUpdate(info, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo)))); + QCOMPARE(obj->activeMonitors().count(), monitorCount); + + for (int i = 1; i <= monitorCount; i++) { + QTRY_VERIFY_WITH_TIMEOUT(expirySpy.count() == 1, 7000); //each expiry within 5 s + QGeoAreaMonitorInfo mon = expirySpy.takeFirst().at(0).value(); + QCOMPARE(obj->activeMonitors().count(), monitorCount-i); + QCOMPARE(mon.name(), QString::number(i)); + } + + QCOMPARE(expirySpy2.count(), monitorCount); + QCOMPARE(secondObj->activeMonitors().count(), 0); //all monitors expired + for (int i = 1; i <= monitorCount; i++) { + QGeoAreaMonitorInfo mon = expirySpy2.takeFirst().at(0).value(); + QCOMPARE(mon.name(), QString::number(i)); + } + + delete obj; + delete secondObj; + } + + void tst_enteredExitedSignal() + { + QGeoAreaMonitorSource *obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + obj->setObjectName("firstObject"); + QSignalSpy enteredSpy(obj, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo))); + QSignalSpy exitedSpy(obj, SIGNAL(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo))); + + LogFilePositionSource *source = new LogFilePositionSource(this); + source->setUpdateInterval(UPDATE_INTERVAL); + obj->setPositionInfoSource(source); + QCOMPARE(obj->positionInfoSource(), source); + + QGeoAreaMonitorSource *secondObj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(secondObj != 0); + QCOMPARE(secondObj->sourceName(), QStringLiteral("positionpoll")); + QSignalSpy enteredSpy2(secondObj, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo))); + QSignalSpy exitedSpy2(secondObj, SIGNAL(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo))); + secondObj->setObjectName("secondObject"); + + QGeoAreaMonitorInfo infoRectangle("Rectangle"); + infoRectangle.setArea(QGeoRectangle(QGeoCoordinate(-27.65, 153.093), 0.2, 0.2)); + QVERIFY(infoRectangle.isValid()); + QVERIFY(obj->startMonitoring(infoRectangle)); + + QGeoAreaMonitorInfo infoCircle("Circle"); + infoCircle.setArea(QGeoCircle(QGeoCoordinate(-27.70, 153.093),10000)); + QVERIFY(infoCircle.isValid()); + QVERIFY(obj->startMonitoring(infoCircle)); + + QGeoAreaMonitorInfo singleShot_enter("SingleShot_on_Entered"); + singleShot_enter.setArea(QGeoRectangle(QGeoCoordinate(-27.67, 153.093), 0.2, 0.2)); + QVERIFY(singleShot_enter.isValid()); + QVERIFY(obj->requestUpdate(singleShot_enter, + SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo)))); + + QGeoAreaMonitorInfo singleShot_exit("SingleShot_on_Exited"); + singleShot_exit.setArea(QGeoRectangle(QGeoCoordinate(-27.70, 153.093), 0.2, 0.2)); + QVERIFY(singleShot_exit.isValid()); + QVERIFY(obj->requestUpdate(singleShot_exit, + SIGNAL(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo)))); + + QVERIFY(obj->activeMonitors().count() == 4); //all monitors active + QVERIFY(secondObj->activeMonitors().count() == 4); //all monitors active + + static const int Number_Of_Entered_Events = 6; + static const int Number_Of_Exited_Events = 5; + //takes 87 (lines)*200(timeout)/1000 seconds to finish + QTRY_VERIFY_WITH_TIMEOUT(enteredSpy.count() == Number_Of_Entered_Events, 20000); + QTRY_VERIFY_WITH_TIMEOUT(exitedSpy.count() == Number_Of_Exited_Events, 20000); + QCOMPARE(enteredSpy.count(), Number_Of_Entered_Events); + QCOMPARE(exitedSpy.count(), Number_Of_Exited_Events); + + QList monitorsInExpectedEnteredEventOrder; + monitorsInExpectedEnteredEventOrder << infoRectangle << singleShot_enter << singleShot_exit + << infoCircle << infoCircle << infoRectangle; + + QList monitorsInExpectedExitedEventOrder; + monitorsInExpectedExitedEventOrder << infoRectangle << infoCircle + << singleShot_exit << infoCircle << infoRectangle; + + QList enteredEventCoordinateOrder; + enteredEventCoordinateOrder << QGeoCoordinate(-27.55, 153.090718) //infoRectangle + << QGeoCoordinate(-27.57, 153.090718) //singleshot_enter + << QGeoCoordinate(-27.60, 153.090908) //singleshot_exit + << QGeoCoordinate(-27.62, 153.091036) //infoCircle + << QGeoCoordinate(-27.78, 153.093647) //infoCircle + << QGeoCoordinate(-27.75, 153.093896);//infoRectangle + QCOMPARE(enteredEventCoordinateOrder.count(), Number_Of_Entered_Events); + QCOMPARE(monitorsInExpectedEnteredEventOrder.count(), Number_Of_Entered_Events); + + QList exitedEventCoordinateOrder; + exitedEventCoordinateOrder << QGeoCoordinate(-27.78, 153.092218) //infoRectangle + << QGeoCoordinate(-27.79, 153.092308) //infoCircle + << QGeoCoordinate(-27.81, 153.092530) //singleshot_exit + << QGeoCoordinate(-27.61, 153.095231) //infoCircle + << QGeoCoordinate(-27.54, 153.095995);//infoCircle + QCOMPARE(exitedEventCoordinateOrder.count(), Number_Of_Exited_Events); + QCOMPARE(monitorsInExpectedExitedEventOrder.count(), Number_Of_Exited_Events); + + //verify that both sources got the same signals + for (int i = 0; i < Number_Of_Entered_Events; i++) { + //first source + QGeoAreaMonitorInfo monInfo = enteredSpy.first().at(0).value(); + QGeoPositionInfo posInfo = enteredSpy.takeFirst().at(1).value(); + QVERIFY2(monInfo == monitorsInExpectedEnteredEventOrder.at(i), + qPrintable(QString::number(i) + ": " + monInfo.name())); + QVERIFY2(posInfo.coordinate() == enteredEventCoordinateOrder.at(i), + qPrintable(QString::number(i) + ". posInfo")); + + //reset info objects to avoid comparing the same + monInfo = QGeoAreaMonitorInfo(); + posInfo = QGeoPositionInfo(); + + //second source + monInfo = enteredSpy2.first().at(0).value(); + posInfo = enteredSpy2.takeFirst().at(1).value(); + QVERIFY2(monInfo == monitorsInExpectedEnteredEventOrder.at(i), + qPrintable(QString::number(i) + ": " + monInfo.name())); + QVERIFY2(posInfo.coordinate() == enteredEventCoordinateOrder.at(i), + qPrintable(QString::number(i) + ". posInfo")); + } + + for (int i = 0; i < Number_Of_Exited_Events; i++) { + //first source + QGeoAreaMonitorInfo monInfo = exitedSpy.first().at(0).value(); + QGeoPositionInfo posInfo = exitedSpy.takeFirst().at(1).value(); + QVERIFY2(monInfo == monitorsInExpectedExitedEventOrder.at(i), + qPrintable(QString::number(i) + ": " + monInfo.name())); + QVERIFY2(posInfo.coordinate() == exitedEventCoordinateOrder.at(i), + qPrintable(QString::number(i) + ". posInfo")); + + //reset info objects to avoid comparing the same + monInfo = QGeoAreaMonitorInfo(); + posInfo = QGeoPositionInfo(); + + //second source + monInfo = exitedSpy2.first().at(0).value(); + posInfo = exitedSpy2.takeFirst().at(1).value(); + QVERIFY2(monInfo == monitorsInExpectedExitedEventOrder.at(i), + qPrintable(QString::number(i) + ": " + monInfo.name())); + QVERIFY2(posInfo.coordinate() == exitedEventCoordinateOrder.at(i), + qPrintable(QString::number(i) + ". posInfo")); + } + + QCOMPARE(obj->activeMonitors().count(), 2); //single shot monitors have been removed + QCOMPARE(secondObj->activeMonitors().count(), 2); + + delete obj; + delete secondObj; + } + + void tst_swapOfPositionSource() + { + QGeoAreaMonitorSource *obj = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj != 0); + QCOMPARE(obj->sourceName(), QStringLiteral("positionpoll")); + obj->setObjectName("firstObject"); + QSignalSpy enteredSpy(obj, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo))); + QSignalSpy exitedSpy(obj, SIGNAL(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo))); + + QGeoAreaMonitorSource *obj2 = QGeoAreaMonitorSource::createSource(QStringLiteral("positionpoll"), 0); + QVERIFY(obj2 != 0); + QCOMPARE(obj2->sourceName(), QStringLiteral("positionpoll")); + obj2->setObjectName("secondObject"); + QSignalSpy enteredSpy2(obj2, SIGNAL(areaEntered(QGeoAreaMonitorInfo,QGeoPositionInfo))); + QSignalSpy exitedSpy2(obj2, SIGNAL(areaExited(QGeoAreaMonitorInfo,QGeoPositionInfo))); + + LogFilePositionSource *source = new LogFilePositionSource(this); + source->setUpdateInterval(UPDATE_INTERVAL); + source->setObjectName("FirstLogFileSource"); + + LogFilePositionSource *source2 = new LogFilePositionSource(this); + source2->setUpdateInterval(UPDATE_INTERVAL); + source2->setObjectName("SecondLogFileSource"); + + obj->setPositionInfoSource(source); + QCOMPARE(obj->positionInfoSource(), obj2->positionInfoSource()); + QCOMPARE(obj2->positionInfoSource(), source); + + QGeoAreaMonitorInfo infoRectangle("Rectangle"); + infoRectangle.setArea(QGeoRectangle(QGeoCoordinate(-27.70, 153.092), 0.2, 0.2)); + QVERIFY(infoRectangle.isValid()); + QVERIFY(obj->startMonitoring(infoRectangle)); + + QCOMPARE(obj->activeMonitors().count(), 1); + QCOMPARE(obj2->activeMonitors().count(), 1); + + QGeoCoordinate firstBorder(-27.6, 153.090908); + QGeoCoordinate secondBorder(-27.81, 153.092530); + + /***********************************/ + //1. trigger events on source (until areaExit + QTRY_VERIFY_WITH_TIMEOUT(exitedSpy.count() == 1, 20000); + QCOMPARE(enteredSpy.count(), enteredSpy2.count()); + QCOMPARE(exitedSpy.count(), exitedSpy2.count()); + + //compare entered event + QVERIFY(enteredSpy.first().at(0).value() == + enteredSpy2.first().at(0).value()); + QGeoPositionInfo info = enteredSpy.takeFirst().at(1).value(); + QVERIFY(info == enteredSpy2.takeFirst().at(1).value()); + QVERIFY(info.coordinate() == firstBorder); + //compare exit event + QVERIFY(exitedSpy.first().at(0).value() == + exitedSpy2.first().at(0).value()); + info = exitedSpy.takeFirst().at(1).value(); + QVERIFY(info == exitedSpy2.takeFirst().at(1).value()); + QVERIFY(info.coordinate() == secondBorder); + + QCOMPARE(exitedSpy.count(), 0); + QCOMPARE(enteredSpy.count(), 0); + QCOMPARE(exitedSpy2.count(), 0); + QCOMPARE(enteredSpy2.count(), 0); + + /***********************************/ + //2. change position source -> which restarts at beginning again + obj2->setPositionInfoSource(source2); + QCOMPARE(obj->positionInfoSource(), obj2->positionInfoSource()); + QCOMPARE(obj2->positionInfoSource(), source2); + + QTRY_VERIFY_WITH_TIMEOUT(exitedSpy.count() == 1, 20000); + QCOMPARE(enteredSpy.count(), enteredSpy2.count()); + QCOMPARE(exitedSpy.count(), exitedSpy2.count()); + + //compare entered event + QVERIFY(enteredSpy.first().at(0).value() == + enteredSpy2.first().at(0).value()); + info = enteredSpy.takeFirst().at(1).value(); + QVERIFY(info == enteredSpy2.takeFirst().at(1).value()); + QVERIFY(info.coordinate() == firstBorder); + //compare exit event + QVERIFY(exitedSpy.first().at(0).value() == + exitedSpy2.first().at(0).value()); + info = exitedSpy.takeFirst().at(1).value(); + QVERIFY(info == exitedSpy2.takeFirst().at(1).value()); + QVERIFY(info.coordinate() == secondBorder); + + + //obj was deleted when setting new source + delete obj2; + } + + void debug_data() + { + QTest::addColumn("info"); + QTest::addColumn("nextValue"); + QTest::addColumn("debugString"); + + QGeoAreaMonitorInfo info; + QTest::newRow("uninitialized") << info << 45 + << QString("QGeoAreaMonitorInfo(\"\", QGeoShape(Unknown), " + "persistent: false, expiry: QDateTime(Invalid)) 45"); + + info.setArea(QGeoRectangle()); + info.setPersistent(true); + info.setName("RectangleAreaMonitor"); + QTest::newRow("Rectangle Test") << info << 45 + << QString("QGeoAreaMonitorInfo(\"RectangleAreaMonitor\", QGeoShape(Rectangle), " + "persistent: true, expiry: QDateTime(Invalid)) 45"); + + info = QGeoAreaMonitorInfo(); + info.setArea(QGeoCircle()); + info.setPersistent(false); + info.setName("CircleAreaMonitor"); + QVariantMap map; + map.insert(QString("foobarKey"), QVariant(45)); //should be ignored + info.setNotificationParameters(map); + QTest::newRow("Circle Test") << info << 45 + << QString("QGeoAreaMonitorInfo(\"CircleAreaMonitor\", QGeoShape(Circle), " + "persistent: false, expiry: QDateTime(Invalid)) 45"); + + // we ignore any further QDateTime related changes to avoid depending on QDateTime related + // failures in case its QDebug string changes + } + + void debug() + { + QFETCH(QGeoAreaMonitorInfo, info); + QFETCH(int, nextValue); + QFETCH(QString, debugString); + + qInstallMessageHandler(tst_qgeoareamonitorinfo_messageHandler); + qDebug() << info << nextValue; + qInstallMessageHandler(0); + QCOMPARE(tst_qgeoareamonitorinfo_debug, debugString); + } +}; + + +QTEST_GUILESS_MAIN(tst_QGeoAreaMonitorSource) +#include "tst_qgeoareamonitor.moc" diff --git a/tests/auto/qgeocameracapabilities/qgeocameracapabilities.pro b/tests/auto/qgeocameracapabilities/qgeocameracapabilities.pro new file mode 100644 index 0000000..d061f18 --- /dev/null +++ b/tests/auto/qgeocameracapabilities/qgeocameracapabilities.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeocameracapabilities + +INCLUDEPATH += ../../../src/location/maps + +SOURCES += tst_qgeocameracapabilities.cpp + +QT += location-private positioning-private testlib diff --git a/tests/auto/qgeocameracapabilities/tst_qgeocameracapabilities.cpp b/tests/auto/qgeocameracapabilities/tst_qgeocameracapabilities.cpp new file mode 100644 index 0000000..06d85e1 --- /dev/null +++ b/tests/auto/qgeocameracapabilities/tst_qgeocameracapabilities.cpp @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoCameraCapabilities : public QObject +{ + Q_OBJECT + +public: + tst_QGeoCameraCapabilities(); + +private: + void populateGeoCameraCapabilitiesData(); + +private Q_SLOTS: + void constructorTest_data(); + void constructorTest(); + void minimumZoomLevelTest(); + void maximumZoomLevelTest(); + void supportsBearingTest(); + void supportsRollingTest(); + void supportsTiltingTest(); + void minimumTiltTest(); + void maximumTiltTest(); + void minimumFieldOfViewTest(); + void maximumFieldOfViewTest(); + void operatorsTest_data(); + void operatorsTest(); + void isValidTest(); +}; + +tst_QGeoCameraCapabilities::tst_QGeoCameraCapabilities() +{ +} + +void tst_QGeoCameraCapabilities::populateGeoCameraCapabilitiesData(){ + QTest::addColumn("minimumZoomLevel"); + QTest::addColumn("maximumZoomLevel"); + QTest::addColumn("minimumTilt"); + QTest::addColumn("maximumTilt"); + QTest::addColumn("minimumFieldOfView"); + QTest::addColumn("maximumFieldOfView"); + QTest::addColumn("bearingSupport"); + QTest::addColumn("rollingSupport"); + QTest::addColumn("tiltingSupport"); + QTest::newRow("zeros") << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0 << false << false << false; + QTest::newRow("valid") << 1.0 << 2.0 << 0.5 << 1.5 << 1.0 << 179.0 << true << true << true; + QTest::newRow("negative values") << 0.0 << 0.5 << -0.5 << -0.1 << -20.0 << -30.0 << true << true << true; +} + +void tst_QGeoCameraCapabilities::constructorTest_data(){ + populateGeoCameraCapabilitiesData(); +} + +void tst_QGeoCameraCapabilities::constructorTest() +{ + QFETCH(double, minimumZoomLevel); + QFETCH(double, maximumZoomLevel); + QFETCH(double, minimumTilt); + QFETCH(double, maximumTilt); + QFETCH(double, minimumFieldOfView); + QFETCH(double, maximumFieldOfView); + QFETCH(bool, bearingSupport); + QFETCH(bool, rollingSupport); + QFETCH(bool, tiltingSupport); + + minimumFieldOfView = qBound(1.0, minimumFieldOfView, 179.0); + maximumFieldOfView = qBound(1.0, maximumFieldOfView, 179.0); + + // contructor test with default values + QGeoCameraCapabilities cameraCapabilities; + QGeoCameraCapabilities cameraCapabilities2(cameraCapabilities); + QCOMPARE(cameraCapabilities.minimumZoomLevel(), cameraCapabilities2.minimumZoomLevel()); + QCOMPARE(cameraCapabilities.maximumZoomLevel(), cameraCapabilities2.maximumZoomLevel()); + QVERIFY2(cameraCapabilities.supportsBearing() == cameraCapabilities2.supportsBearing(), "Copy constructor failed for bearing support"); + QVERIFY2(cameraCapabilities.supportsRolling() == cameraCapabilities2.supportsRolling(), "Copy constructor failed for rolling support "); + QVERIFY2(cameraCapabilities.supportsTilting() == cameraCapabilities2.supportsTilting(), "Copy constructor failed for tilting support"); + QCOMPARE(cameraCapabilities.minimumTilt(), cameraCapabilities2.minimumTilt()); + QCOMPARE(cameraCapabilities.maximumTilt(), cameraCapabilities2.maximumTilt()); + QCOMPARE(cameraCapabilities.minimumFieldOfView(), cameraCapabilities2.minimumFieldOfView()); + QCOMPARE(cameraCapabilities.maximumFieldOfView(), cameraCapabilities2.maximumFieldOfView()); + + // constructor test after setting values + cameraCapabilities.setMinimumZoomLevel(minimumZoomLevel); + cameraCapabilities.setMaximumZoomLevel(maximumZoomLevel); + cameraCapabilities.setMinimumTilt(minimumTilt); + cameraCapabilities.setMaximumTilt(maximumTilt); + cameraCapabilities.setMinimumFieldOfView(minimumFieldOfView); + cameraCapabilities.setMaximumFieldOfView(maximumFieldOfView); + cameraCapabilities.setSupportsBearing(bearingSupport); + cameraCapabilities.setSupportsRolling(rollingSupport); + cameraCapabilities.setSupportsTilting(tiltingSupport); + + QGeoCameraCapabilities cameraCapabilities3(cameraCapabilities); + // test the correctness of the constructor copy + QCOMPARE(cameraCapabilities3.minimumZoomLevel(), minimumZoomLevel); + QCOMPARE(cameraCapabilities3.maximumZoomLevel(), maximumZoomLevel); + QCOMPARE(cameraCapabilities3.minimumTilt(), minimumTilt); + QCOMPARE(cameraCapabilities3.maximumTilt(), maximumTilt); + QCOMPARE(cameraCapabilities3.minimumFieldOfView(), minimumFieldOfView); + QCOMPARE(cameraCapabilities3.maximumFieldOfView(), maximumFieldOfView); + QVERIFY2(cameraCapabilities3.supportsBearing() == bearingSupport, "Copy constructor failed for bearing support"); + QVERIFY2(cameraCapabilities3.supportsRolling() == rollingSupport, "Copy constructor failed for rolling support "); + QVERIFY2(cameraCapabilities3.supportsTilting() == tiltingSupport, "Copy constructor failed for tilting support"); + // verify that values have not changed after a constructor copy + QCOMPARE(cameraCapabilities.minimumZoomLevel(), cameraCapabilities3.minimumZoomLevel()); + QCOMPARE(cameraCapabilities.maximumZoomLevel(), cameraCapabilities3.maximumZoomLevel()); + QVERIFY2(cameraCapabilities.supportsBearing() == cameraCapabilities3.supportsBearing(), "Copy constructor failed for bearing support"); + QVERIFY2(cameraCapabilities.supportsRolling() == cameraCapabilities3.supportsRolling(), "Copy constructor failed for rolling support "); + QVERIFY2(cameraCapabilities.supportsTilting() == cameraCapabilities3.supportsTilting(), "Copy constructor failed for tilting support"); + QCOMPARE(cameraCapabilities.minimumTilt(), cameraCapabilities3.minimumTilt()); + QCOMPARE(cameraCapabilities.maximumTilt(), cameraCapabilities3.maximumTilt()); + QCOMPARE(cameraCapabilities.minimumFieldOfView(), cameraCapabilities3.minimumFieldOfView()); + QCOMPARE(cameraCapabilities.maximumFieldOfView(), cameraCapabilities3.maximumFieldOfView()); +} + +void tst_QGeoCameraCapabilities::minimumZoomLevelTest() +{ + QGeoCameraCapabilities cameraCapabilities; + cameraCapabilities.setMinimumZoomLevel(1.5); + QCOMPARE(cameraCapabilities.minimumZoomLevel(), 1.5); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QCOMPARE(cameraCapabilities2.minimumZoomLevel(), 1.5); + cameraCapabilities.setMinimumZoomLevel(2.5); + QCOMPARE(cameraCapabilities2.minimumZoomLevel(), 1.5); +} + +void tst_QGeoCameraCapabilities::maximumZoomLevelTest() +{ + QGeoCameraCapabilities cameraCapabilities; + cameraCapabilities.setMaximumZoomLevel(3.5); + QCOMPARE(cameraCapabilities.maximumZoomLevel(), 3.5); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QCOMPARE(cameraCapabilities2.maximumZoomLevel(), 3.5); + cameraCapabilities.setMaximumZoomLevel(4.5); + QCOMPARE(cameraCapabilities2.maximumZoomLevel(), 3.5); +} + +void tst_QGeoCameraCapabilities::supportsBearingTest(){ + QGeoCameraCapabilities cameraCapabilities; + QVERIFY(!cameraCapabilities.supportsBearing()); + cameraCapabilities.setSupportsBearing(true); + QVERIFY2(cameraCapabilities.supportsBearing(), "Camera capabilities should support bearing"); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QVERIFY(cameraCapabilities2.supportsBearing()); + cameraCapabilities.setSupportsBearing(false); + QVERIFY2(cameraCapabilities2.supportsBearing(), "Camera capabilities should support bearing"); +} + +void tst_QGeoCameraCapabilities::supportsRollingTest(){ + QGeoCameraCapabilities cameraCapabilities; + QVERIFY(!cameraCapabilities.supportsRolling()); + cameraCapabilities.setSupportsRolling(true); + QVERIFY2(cameraCapabilities.supportsRolling(), "Camera capabilities should support rolling"); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QVERIFY(cameraCapabilities2.supportsRolling()); + cameraCapabilities.setSupportsRolling(false); + QVERIFY2(cameraCapabilities2.supportsRolling(), "Camera capabilities should support rolling"); +} + +void tst_QGeoCameraCapabilities::supportsTiltingTest(){ + QGeoCameraCapabilities cameraCapabilities; + QVERIFY(!cameraCapabilities.supportsTilting()); + cameraCapabilities.setSupportsTilting(true); + QVERIFY2(cameraCapabilities.supportsTilting(), "Camera capabilities should support tilting"); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QVERIFY(cameraCapabilities2.supportsTilting()); + cameraCapabilities.setSupportsTilting(false); + QVERIFY2(cameraCapabilities2.supportsTilting(), "Camera capabilities should support tilting"); +} + +void tst_QGeoCameraCapabilities::minimumTiltTest(){ + QGeoCameraCapabilities cameraCapabilities; + QCOMPARE(cameraCapabilities.minimumTilt(),0.0); + cameraCapabilities.setMinimumTilt(0.5); + QCOMPARE(cameraCapabilities.minimumTilt(),0.5); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QCOMPARE(cameraCapabilities2.minimumTilt(), 0.5); + cameraCapabilities.setMinimumTilt(1.5); + QCOMPARE(cameraCapabilities2.minimumTilt(), 0.5); +} + +void tst_QGeoCameraCapabilities::maximumTiltTest(){ + QGeoCameraCapabilities cameraCapabilities; + QCOMPARE(cameraCapabilities.maximumTilt(),0.0); + cameraCapabilities.setMaximumTilt(1.5); + QCOMPARE(cameraCapabilities.maximumTilt(),1.5); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QCOMPARE(cameraCapabilities2.maximumTilt(), 1.5); + cameraCapabilities.setMaximumTilt(2.5); + QCOMPARE(cameraCapabilities2.maximumTilt(), 1.5); +} + +void tst_QGeoCameraCapabilities::minimumFieldOfViewTest() +{ + QGeoCameraCapabilities cameraCapabilities; + QCOMPARE(cameraCapabilities.minimumFieldOfView(), 45.0); // min/max default to 45 + cameraCapabilities.setMinimumFieldOfView(1.5); + QCOMPARE(cameraCapabilities.minimumFieldOfView(), 1.5); + cameraCapabilities.setMinimumFieldOfView(-1.5); + QCOMPARE(cameraCapabilities.minimumFieldOfView(), 1.0); + cameraCapabilities.setMinimumFieldOfView(245.5); + QCOMPARE(cameraCapabilities.minimumFieldOfView(), 179.0); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QCOMPARE(cameraCapabilities2.minimumFieldOfView(), 179.0); + cameraCapabilities.setMinimumFieldOfView(2.5); + QCOMPARE(cameraCapabilities2.minimumFieldOfView(), 179.0); +} + +void tst_QGeoCameraCapabilities::maximumFieldOfViewTest() +{ + QGeoCameraCapabilities cameraCapabilities; + QCOMPARE(cameraCapabilities.maximumFieldOfView(), 45.0); // min/max default to 45 + cameraCapabilities.setMaximumFieldOfView(1.5); + QCOMPARE(cameraCapabilities.maximumFieldOfView(), 1.5); + cameraCapabilities.setMaximumFieldOfView(-1.5); + QCOMPARE(cameraCapabilities.maximumFieldOfView(), 1.0); + cameraCapabilities.setMaximumFieldOfView(245.5); + QCOMPARE(cameraCapabilities.maximumFieldOfView(), 179.0); + + QGeoCameraCapabilities cameraCapabilities2 = cameraCapabilities; + QCOMPARE(cameraCapabilities2.maximumFieldOfView(), 179.0); + cameraCapabilities.setMaximumFieldOfView(2.5); + QCOMPARE(cameraCapabilities2.maximumFieldOfView(), 179.0); +} + +void tst_QGeoCameraCapabilities::operatorsTest_data(){ + populateGeoCameraCapabilitiesData(); +} + +void tst_QGeoCameraCapabilities::operatorsTest(){ + + QFETCH(double, minimumZoomLevel); + QFETCH(double, maximumZoomLevel); + QFETCH(double, minimumTilt); + QFETCH(double, maximumTilt); + QFETCH(double, minimumFieldOfView); + QFETCH(double, maximumFieldOfView); + QFETCH(bool, bearingSupport); + QFETCH(bool, rollingSupport); + QFETCH(bool, tiltingSupport); + + minimumFieldOfView = qBound(1.0, minimumFieldOfView, 179.0); + maximumFieldOfView = qBound(1.0, maximumFieldOfView, 179.0); + + QGeoCameraCapabilities cameraCapabilities; + cameraCapabilities.setMinimumZoomLevel(minimumZoomLevel); + cameraCapabilities.setMaximumZoomLevel(maximumZoomLevel); + cameraCapabilities.setMinimumTilt(minimumTilt); + cameraCapabilities.setMaximumTilt(maximumTilt); + cameraCapabilities.setMinimumFieldOfView(minimumFieldOfView); + cameraCapabilities.setMaximumFieldOfView(maximumFieldOfView); + cameraCapabilities.setSupportsBearing(bearingSupport); + cameraCapabilities.setSupportsRolling(rollingSupport); + cameraCapabilities.setSupportsTilting(tiltingSupport); + QGeoCameraCapabilities cameraCapabilities2; + cameraCapabilities2 = cameraCapabilities; + // test the correctness of the assignment + QCOMPARE(cameraCapabilities2.minimumZoomLevel(), minimumZoomLevel); + QCOMPARE(cameraCapabilities2.maximumZoomLevel(), maximumZoomLevel); + QCOMPARE(cameraCapabilities2.minimumTilt(), minimumTilt); + QCOMPARE(cameraCapabilities2.maximumTilt(), maximumTilt); + QVERIFY2(cameraCapabilities2.supportsBearing() == bearingSupport, "Assignment operator failed for bearing support"); + QVERIFY2(cameraCapabilities2.supportsRolling() == rollingSupport, "Assignment operator failed for rolling support "); + QVERIFY2(cameraCapabilities2.supportsTilting() == tiltingSupport, "Assignment operator failed for tilting support"); + QCOMPARE(cameraCapabilities2.minimumFieldOfView(), minimumFieldOfView); + QCOMPARE(cameraCapabilities2.maximumFieldOfView(), maximumFieldOfView); + // verify that values have not changed after a constructor copy + QCOMPARE(cameraCapabilities.minimumZoomLevel(), cameraCapabilities2.minimumZoomLevel()); + QCOMPARE(cameraCapabilities.maximumZoomLevel(), cameraCapabilities2.maximumZoomLevel()); + QVERIFY2(cameraCapabilities.supportsBearing() == cameraCapabilities2.supportsBearing(), "Assignment operator failed for bearing support"); + QVERIFY2(cameraCapabilities.supportsRolling() == cameraCapabilities2.supportsRolling(), "Assignment operator failed for rolling support "); + QVERIFY2(cameraCapabilities.supportsTilting() == cameraCapabilities2.supportsTilting(), "Assignment operator failed for tilting support"); + QCOMPARE(cameraCapabilities.minimumTilt(), cameraCapabilities2.minimumTilt()); + QCOMPARE(cameraCapabilities.maximumTilt(), cameraCapabilities2.maximumTilt()); + QCOMPARE(cameraCapabilities.minimumFieldOfView(), cameraCapabilities2.minimumFieldOfView()); + QCOMPARE(cameraCapabilities.maximumFieldOfView(), cameraCapabilities2.maximumFieldOfView()); +} + +void tst_QGeoCameraCapabilities::isValidTest(){ + QGeoCameraCapabilities cameraCapabilities; + QVERIFY2(!cameraCapabilities.isValid(), "Camera capabilities should default to invalid"); + cameraCapabilities.setSupportsBearing(true); + QVERIFY2(cameraCapabilities.isValid(), "Camera capabilities should be valid"); + + QGeoCameraCapabilities cameraCapabilities2; + QVERIFY2(!cameraCapabilities2.isValid(), "Camera capabilities should default to invalid"); + cameraCapabilities2.setSupportsRolling(true); + QVERIFY2(cameraCapabilities2.isValid(), "Camera capabilities should be valid"); + + QGeoCameraCapabilities cameraCapabilities3; + QVERIFY2(!cameraCapabilities3.isValid(), "Camera capabilities should default to invalid"); + cameraCapabilities3.setSupportsTilting(true); + QVERIFY2(cameraCapabilities3.isValid(), "Camera capabilities should be valid"); + + QGeoCameraCapabilities cameraCapabilities4; + QVERIFY2(!cameraCapabilities4.isValid(), "Camera capabilities should default to invalid"); + cameraCapabilities4.setMinimumZoomLevel(1.0); + QVERIFY2(cameraCapabilities4.isValid(), "Camera capabilities should be valid"); + + QGeoCameraCapabilities cameraCapabilities5; + QVERIFY2(!cameraCapabilities5.isValid(), "Camera capabilities should default to invalid"); + cameraCapabilities5.setMaximumZoomLevel(1.5); + QVERIFY2(cameraCapabilities5.isValid(), "Camera capabilities should be valid"); + + QGeoCameraCapabilities cameraCapabilities6; + QVERIFY2(!cameraCapabilities6.isValid(), "Camera capabilities should default to invalid"); + cameraCapabilities6.setMinimumTilt(0.2); + QVERIFY2(cameraCapabilities6.isValid(), "Camera capabilities should be valid"); + + QGeoCameraCapabilities cameraCapabilities7; + QVERIFY2(!cameraCapabilities7.isValid(), "Camera capabilities should default to invalid"); + cameraCapabilities7.setMaximumTilt(0.8); + QVERIFY2(cameraCapabilities7.isValid(), "Camera capabilities should be valid"); +} + +QTEST_APPLESS_MAIN(tst_QGeoCameraCapabilities) + +#include "tst_qgeocameracapabilities.moc" diff --git a/tests/auto/qgeocameradata/qgeocameradata.pro b/tests/auto/qgeocameradata/qgeocameradata.pro new file mode 100644 index 0000000..80069ea --- /dev/null +++ b/tests/auto/qgeocameradata/qgeocameradata.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeocameradata + +INCLUDEPATH += ../../../src/location/maps + +SOURCES += tst_qgeocameradata.cpp + +QT += location-private positioning-private testlib diff --git a/tests/auto/qgeocameradata/tst_qgeocameradata.cpp b/tests/auto/qgeocameradata/tst_qgeocameradata.cpp new file mode 100644 index 0000000..be75acf --- /dev/null +++ b/tests/auto/qgeocameradata/tst_qgeocameradata.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qgeocameradata_p.h" + +QT_USE_NAMESPACE + +class tst_QGeoCameraData : public QObject +{ + Q_OBJECT + +public: + tst_QGeoCameraData(); + +private: + void populateCameraData(); + +private Q_SLOTS: + void constructorTest_data(); + void constructorTest(); + void centerTest(); + void bearingTest(); + void tiltTest(); + void rollTest(); + void zoomLevelTest(); + void operatorsTest_data(); + void operatorsTest(); +}; + +tst_QGeoCameraData::tst_QGeoCameraData() +{ +} + + +void tst_QGeoCameraData::populateCameraData() +{ + QTest::addColumn("center"); + QTest::addColumn("bearing"); + QTest::addColumn("tilt"); + QTest::addColumn("roll"); + QTest::addColumn("zoomLevel"); + QTest::newRow("zeros") << QGeoCoordinate() << 0.0 << 0.0 << 0.0 << 0.0; + QTest::newRow("valid") << QGeoCoordinate(10.0,20.5,30.8) << 0.1 << 0.2 << 0.3 << 2.0; + QTest::newRow("negative values") << QGeoCoordinate(-50,-20,100) << -0.1 << -0.2 << -0.3 << 1.0; +} + +void tst_QGeoCameraData::constructorTest_data(){ + populateCameraData(); +} + +void tst_QGeoCameraData::constructorTest() +{ + QFETCH(QGeoCoordinate, center); + QFETCH(double, bearing); + QFETCH(double, tilt); + QFETCH(double, roll); + QFETCH(double, zoomLevel); + + // constructor test with default values + QGeoCameraData cameraData; + QGeoCameraData cameraData2(cameraData); + QCOMPARE(cameraData.center(), cameraData2.center()); + QCOMPARE(cameraData.bearing(), cameraData2.bearing()); + QCOMPARE(cameraData.tilt(), cameraData2.tilt()); + QCOMPARE(cameraData.roll(), cameraData2.roll()); + QCOMPARE(cameraData.zoomLevel(), cameraData2.zoomLevel()); + + // constructor test after setting values + cameraData.setCenter(center); + cameraData.setBearing(bearing); + cameraData.setTilt(tilt); + cameraData.setRoll(roll); + cameraData.setZoomLevel(zoomLevel); + QGeoCameraData cameraData3(cameraData); + // test the correctness of the constructor copy + QCOMPARE(cameraData3.center(), center); + QCOMPARE(cameraData3.bearing(), bearing); + QCOMPARE(cameraData3.tilt(), tilt); + QCOMPARE(cameraData3.roll(), roll); + QCOMPARE(cameraData3.zoomLevel(), zoomLevel); + // verify that values have not changed after a constructor copy + QCOMPARE(cameraData.center(), cameraData3.center()); + QCOMPARE(cameraData.bearing(), cameraData3.bearing()); + QCOMPARE(cameraData.tilt(), cameraData3.tilt()); + QCOMPARE(cameraData.roll(), cameraData3.roll()); + QCOMPARE(cameraData.zoomLevel(), cameraData3.zoomLevel()); +} + + +void tst_QGeoCameraData::centerTest() +{ + QGeoCameraData cameraData; //center currently default to (-27.5, 153) + cameraData.setCenter(QGeoCoordinate(10.0,20.4,30.8)); + QCOMPARE(cameraData.center(),QGeoCoordinate(10.0,20.4,30.8)); +} + +void tst_QGeoCameraData::bearingTest(){ + QGeoCameraData cameraData; + QCOMPARE(cameraData.bearing(),0.0); + cameraData.setBearing(0.1); + QCOMPARE(cameraData.bearing(),0.1); + + QGeoCameraData cameraData2 = cameraData; + QCOMPARE(cameraData2.bearing(),0.1); + cameraData.setBearing(0.2); + QCOMPARE(cameraData2.bearing(),0.1); +} + +void tst_QGeoCameraData::tiltTest(){ + QGeoCameraData cameraData; + QCOMPARE(cameraData.tilt(),0.0); + cameraData.setTilt(0.4); + QCOMPARE(cameraData.tilt(),0.4); + + QGeoCameraData cameraData2 = cameraData; + QCOMPARE(cameraData2.tilt(),0.4); + cameraData.setTilt(0.5); + QCOMPARE(cameraData2.tilt(),0.4); +} + +void tst_QGeoCameraData::rollTest(){ + QGeoCameraData cameraData; + QCOMPARE(cameraData.roll(),0.0); + cameraData.setRoll(0.5); + QCOMPARE(cameraData.roll(),0.5); + + QGeoCameraData cameraData2 = cameraData; + QCOMPARE(cameraData2.roll(),0.5); + cameraData.setRoll(0.6); + QCOMPARE(cameraData2.roll(),0.5); +} + +void tst_QGeoCameraData::zoomLevelTest(){ + QGeoCameraData cameraData; //zoom level currently defaults to 9.0 + cameraData.setZoomLevel(8.0); + QCOMPARE(cameraData.zoomLevel(),8.0); + + QGeoCameraData cameraData2 = cameraData; + QCOMPARE(cameraData2.zoomLevel(),8.0); + cameraData.setZoomLevel(9.0); + QCOMPARE(cameraData2.zoomLevel(),8.0); +} + +void tst_QGeoCameraData::operatorsTest_data(){ + populateCameraData(); +} + +void tst_QGeoCameraData::operatorsTest(){ + QGeoCameraData cameraData; + QGeoCameraData cameraData2; + QVERIFY2(cameraData == cameraData2, "Camera data with default values are not copied correctly"); + + QFETCH(QGeoCoordinate, center); + QFETCH(double, bearing); + QFETCH(double, tilt); + QFETCH(double, roll); + QFETCH(double, zoomLevel); + cameraData.setCenter(center); + cameraData.setBearing(bearing); + cameraData.setTilt(tilt); + cameraData.setRoll(roll); + cameraData.setZoomLevel(zoomLevel); + + QGeoCameraData cameraData3; + cameraData3 = cameraData; + QVERIFY2(cameraData == cameraData3, "Camera data not copied correctly"); + + // test QGeoCameraData pairs where they differ in one field + QGeoCameraData cameraData4; + cameraData4 = cameraData; + cameraData4.setCenter(QGeoCoordinate(10.0,20.0,30.0)); + QVERIFY2(cameraData != cameraData4, "Camera data should be different"); + QGeoCameraData cameraData5; + cameraData5 = cameraData; + cameraData5.setBearing(bearing+1.0); + QVERIFY2(cameraData != cameraData5, "Camera data should be different"); + QGeoCameraData cameraData6; + cameraData6 = cameraData; + cameraData6.setTilt(tilt+0.1); + QVERIFY2(cameraData != cameraData6, "Camera data should be different"); + QGeoCameraData cameraData7; + cameraData7 = cameraData; + cameraData7.setRoll(roll+0.1); + QVERIFY2(cameraData != cameraData7, "Camera data should be different"); + QGeoCameraData cameraData8; + cameraData8 = cameraData; + cameraData8.setZoomLevel(zoomLevel+1.0); + QVERIFY2(cameraData != cameraData8, "Camera data should be different"); +} + +QTEST_APPLESS_MAIN(tst_QGeoCameraData) + +#include "tst_qgeocameradata.moc" diff --git a/tests/auto/qgeocameratiles/qgeocameratiles.pro b/tests/auto/qgeocameratiles/qgeocameratiles.pro new file mode 100644 index 0000000..63ab277 --- /dev/null +++ b/tests/auto/qgeocameratiles/qgeocameratiles.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qgeocameratiles + +INCLUDEPATH += ../../../src/location/maps + +SOURCES += tst_qgeocameratiles.cpp + +QT += location-private positioning-private testlib diff --git a/tests/auto/qgeocameratiles/tst_qgeocameratiles.cpp b/tests/auto/qgeocameratiles/tst_qgeocameratiles.cpp new file mode 100644 index 0000000..49c4657 --- /dev/null +++ b/tests/auto/qgeocameratiles/tst_qgeocameratiles.cpp @@ -0,0 +1,1830 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location/maps + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +struct PositionTestInfo { + QString xyString; + QString zoomString; + QString wString; + QString hString; + double x; + double y; + double zoom; + double w; + double h; +}; + +class tst_QGeoCameraTiles : public QObject +{ + Q_OBJECT + +private: + + void row(const PositionTestInfo &pti, int xOffset, int yOffset, int tileX, int tileY, int tileW, int tileH); + void test_group(const PositionTestInfo &pti, QList &xVals, QList &wVals, QList &yVals, QList &hVals); + +private slots: + + void tilesPlugin(); + void tilesMapType(); + void tilesPositions(); + void tilesPositions_data(); + void test_tilted_frustum(); +}; + +void tst_QGeoCameraTiles::row(const PositionTestInfo &pti, int xOffset, int yOffset, int tileX, int tileY, int tileW, int tileH) +{ + double step = 1 / (qPow(2.0, 4.0) * 4); + + QString row = pti.xyString; + row += QStringLiteral(" - "); + row += pti.zoomString; + row += QStringLiteral(" - ("); + row += QString::number(xOffset); + row += QStringLiteral(","); + row += QString::number(yOffset); + row += QStringLiteral(") - "); + row += pti.wString; + row += QStringLiteral(" x "); + row += pti.hString; + + QList xRow; + QList yRow; + + for (int y = 0; y < tileH; ++y) { + for (int x = 0; x < tileW; ++x) { + if (tileX + x < 16) + xRow << tileX + x; + else + xRow << tileX + x - 16; + yRow << tileY + y; + } + } + + QTest::newRow(qPrintable(row)) + << pti.x + step * xOffset << pti.y + step * yOffset + << pti.zoom << pti.w << pti.h + << xRow + << yRow; +} + +void tst_QGeoCameraTiles::test_group(const PositionTestInfo &pti, QList &xVals, QList &wVals, QList &yVals, QList &hVals) +{ + for (int x = 0; x < 5; ++x) { + for (int y = 0; y < 5; ++y) { + row(pti, x, y, xVals.at(x), yVals.at(y), wVals.at(x), hVals.at(y)); + } + } +} + +void tst_QGeoCameraTiles::test_tilted_frustum() +{ + // ctFull : Full map in the view, all 16 zl2 tiles visible. Using this as control. + QGeoCameraData cameraFull; + cameraFull.setZoomLevel(2); + cameraFull.setCenter(QGeoCoordinate(0,0)); + QGeoCameraTiles ctFull; + ctFull.setTileSize(64); + ctFull.setCameraData(cameraFull); + ctFull.setScreenSize(QSize(256, 256)); + + QGeoCameraData camera; + camera.setZoomLevel(2.322); + camera.setTilt(30); + camera.setCenter(QWebMercator::mercatorToCoord(QDoubleVector2D(0.75, 0.5))); + QGeoCameraTiles ct; + ct.setTileSize(64); + ct.setScreenSize(QSize(320, 180)); + ct.setCameraData(camera); + + QCOMPARE(ct.createTiles(), ctFull.createTiles()); +} + +void tst_QGeoCameraTiles::tilesPlugin() +{ + QGeoCameraData camera; + camera.setZoomLevel(4.0); + camera.setCenter(QGeoCoordinate(0.0, 0.0)); + + QGeoCameraTiles ct; + ct.setTileSize(16); + ct.setCameraData(camera); + ct.setScreenSize(QSize(32, 32)); + ct.setMapType(QGeoMapType(QGeoMapType::StreetMap, "street map", "street map", false, false, 1, QByteArrayLiteral(""), QGeoCameraCapabilities())); + + QSet tiles1 = ct.createTiles(); + + ct.setPluginString("pluginA"); + + QSet tiles2 = ct.createTiles(); + + typedef QSet::const_iterator iter; + iter i1 = tiles1.constBegin(); + iter end1 = tiles1.constEnd(); + + QSet tiles2_check; + + for (; i1 != end1; ++i1) { + QGeoTileSpec tile = *i1; + tiles2_check.insert(QGeoTileSpec("pluginA", tile.mapId(), tile.zoom(), tile.x(), tile.y())); + } + + QCOMPARE(tiles2, tiles2_check); + + ct.setPluginString("pluginB"); + + QSet tiles3 = ct.createTiles(); + + iter i2 = tiles2.constBegin(); + iter end2 = tiles2.constEnd(); + + QSet tiles3_check; + + for (; i2 != end2; ++i2) { + QGeoTileSpec tile = *i2; + tiles3_check.insert(QGeoTileSpec("pluginB", tile.mapId(), tile.zoom(), tile.x(), tile.y())); + } + + QCOMPARE(tiles3, tiles3_check); +} + +void tst_QGeoCameraTiles::tilesMapType() +{ + QGeoCameraData camera; + camera.setZoomLevel(4.0); + camera.setCenter(QGeoCoordinate(0.0, 0.0)); + + QGeoCameraTiles ct; + ct.setTileSize(16); + ct.setCameraData(camera); + ct.setScreenSize(QSize(32, 32)); + ct.setPluginString("pluginA"); + + QSet tiles1 = ct.createTiles(); + + QGeoMapType mapType1 = QGeoMapType(QGeoMapType::StreetMap, "street map", "street map", false, false, 1, QByteArrayLiteral(""), QGeoCameraCapabilities()); + ct.setMapType(mapType1); + + QSet tiles2 = ct.createTiles(); + + typedef QSet::const_iterator iter; + iter i1 = tiles1.constBegin(); + iter end1 = tiles1.constEnd(); + + QSet tiles2_check; + + for (; i1 != end1; ++i1) { + QGeoTileSpec tile = *i1; + tiles2_check.insert(QGeoTileSpec(tile.plugin(), mapType1.mapId(), tile.zoom(), tile.x(), tile.y())); + } + + QCOMPARE(tiles2, tiles2_check); + + QGeoMapType mapType2 = QGeoMapType(QGeoMapType::StreetMap, "satellite map", "satellite map", false, false, 2, QByteArrayLiteral(""), QGeoCameraCapabilities()); + ct.setMapType(mapType2); + + QSet tiles3 = ct.createTiles(); + + iter i2 = tiles2.constBegin(); + iter end2 = tiles2.constEnd(); + + QSet tiles3_check; + + for (; i2 != end2; ++i2) { + QGeoTileSpec tile = *i2; + tiles3_check.insert(QGeoTileSpec(tile.plugin(), mapType2.mapId(), tile.zoom(), tile.x(), tile.y())); + } + + QCOMPARE(tiles3, tiles3_check); +} + +void tst_QGeoCameraTiles::tilesPositions() +{ + QFETCH(double, mercatorX); + QFETCH(double, mercatorY); + QFETCH(double, zoom); + QFETCH(double, width); + QFETCH(double, height); + QFETCH(QList , tilesX); + QFETCH(QList , tilesY); + + QGeoCameraData camera; + camera.setZoomLevel(zoom); + camera.setCenter(QWebMercator::mercatorToCoord(QDoubleVector2D(mercatorX, mercatorY))); + + QGeoCameraTiles ct; + ct.setTileSize(16); + ct.setCameraData(camera); + ct.setScreenSize(QSize(qCeil(width), qCeil(height))); + + QSet tiles; + + QVERIFY2(tilesX.size() == tilesY.size(), "tilesX and tilesY have different size"); + + for (int i = 0; i < tilesX.size(); ++i) + tiles.insert(QGeoTileSpec("", 0, static_cast(qFloor(zoom)), tilesX.at(i), tilesY.at(i))); + + QCOMPARE(ct.createTiles(), tiles); +} + +void tst_QGeoCameraTiles::tilesPositions_data() +{ + QTest::addColumn("mercatorX"); + QTest::addColumn("mercatorY"); + QTest::addColumn("zoom"); + QTest::addColumn("width"); + QTest::addColumn("height"); + QTest::addColumn >("tilesX"); + QTest::addColumn >("tilesY"); + + int t = 16; + + PositionTestInfo pti; + + /* + This test sets up various viewports onto a 16x16 map, + and checks which tiles are visible against those that + are expected to be visible. + + The tests are run in 5 groups, corresponding to where + the viewport is centered on the map. + + The groups are named as follows, with the tile in + which the viewport is centered listed in parenthesis: + - mid (8, 8) + - top (8, 0) + - bottom (8, 15) + - left (0, 8) + - right (15, 8) + + For each of these groups a number of tests are run, + which involve modifying various parameters. + + If "t" is the width of a tile, the width and height + of the viewport take on values including: + - (t - 1) + - t + - (t + 1) + - (2t - 1) + - 2t + - (2t + 1) + + The viewport is also offset by fractions of a tile + in both the x and y directions. The offsets are in + quarters of a tile. + + The diagrams below present a justification for our + test expectations. + + The diagrams show variations in viewport width and + x offset into the target tile , although can easily + be taken to be the variations in viewport height and + y offset into the target tile. + + The symbols have the following meanings: + "+" - tile boundaries + "*" - viewport boundary + "T" - boundary of tile the viewport is centered on + "O" - same as "T" but coincident with the viewport boundary + + Whenever the viewport boundary is coincident with a tile boundary, + the tiles on both sides of the boundary are expected to be fetched. + + Those tiles are needed in case we perform bilinear antialiasing, + provide us with at least a pixel width of tolerance for errors in + other parts of the code or the system. There is a decent chance + that some or all of those extra tiles will need to be fetched before + long, so getting them into the cache sooner rather than later is + likely to be beneficial on average. + + The tests are carried out per viewport height / width. + + Lists are created of the first tile along an axis that is expected to + be in the viewport and for the number of tiles along an axis that + are expected to be in the viewport. + + These lists are used for both the x and y axes, although alternative + lists are created for the x axis when the viewport spans the dateline + and for the y axis when the viewport is clipped by the top or bottom of + the map. + + These 5 areas are checked at an integral zoom level to see that the + expected visible tiles match the actual visible tiles generated by + the code under test. + + After that a fractional zoom level is set and the width and height of + the viewport are scaled such that the same tiles should be visible, + and the tests are repeated. + */ + + // TODO + // nail down semantics, modify tests and code to suite + // add corners of the map + + /* + width = t - 1 + */ + + QList mid_tm1x; + QList mid_tm1w; + QList top_tm1x; + QList top_tm1w; + QList bottom_tm1x; + QList bottom_tm1w; + QList left_tm1x; + QList left_tm1w; + QList right_tm1x; + QList right_tm1w; + + pti.w = t - 1; + pti.h = t - 1; + pti.wString = QStringLiteral("(1T - 1)"); + pti.hString = QStringLiteral("(1T - 1)"); + + /* + + offset = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * *+* * + + + + + + * + * + + + + + + + + + + +*+ T T*T T T + + + + + + + + + + + * T * T + + + + + * *T* * T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 2 tiles + */ + + mid_tm1x << 7; + mid_tm1w << 2; + + top_tm1x << 0; + top_tm1w << 1; + + bottom_tm1x << 14; + bottom_tm1w << 2; + + left_tm1x << 15; + left_tm1w << 2; + + right_tm1x << 14; + right_tm1w << 2; + + /* + offset = 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *+* * * + + + + + + *+ * + + + + + + + + + + + +*T T T*T T + + + + + + + + + + + *T * T + + + + + *T* * * T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 2 tiles + */ + + mid_tm1x << 7; + mid_tm1w << 2; + + top_tm1x << 0; + top_tm1w << 1; + + bottom_tm1x << 14; + bottom_tm1w << 2; + + left_tm1x << 15; + left_tm1w << 2; + + right_tm1x << 14; + right_tm1w << 2; + + /* + offset = 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +* * * *+ + + + + + +* *+ + + + + + + + + + + + T*T T T*T + + + + + + + + + + + T* *T + + + + + T* * * *T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 1 tile + */ + + mid_tm1x << 8; + mid_tm1w << 1; + + top_tm1x << 0; + top_tm1w << 1; + + bottom_tm1x << 15; + bottom_tm1w << 1; + + left_tm1x << 0; + left_tm1w << 1; + + right_tm1x << 15; + right_tm1w << 1; + + /* + offset = 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * *+* + + + + + + * +* + + + + + + + + + + + T T*T T T*+ + + + + + + + + + + T * T* + + + + + T * * *T* + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 2 tiles + */ + + mid_tm1x << 8; + mid_tm1w << 2; + + top_tm1x << 0; + top_tm1w << 2; + + bottom_tm1x << 15; + bottom_tm1w << 1; + + left_tm1x << 0; + left_tm1w << 2; + + right_tm1x << 15; + right_tm1w << 2; + + /* + offset = 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * *+* * + + + + + + * + * + + + + + + + + + + + T T T*T T +*+ + + + + + + + + + T * T * + + + + + T * *T* * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 2 tiles + */ + + mid_tm1x << 8; + mid_tm1w << 2; + + top_tm1x << 0; + top_tm1w << 2; + + bottom_tm1x << 15; + bottom_tm1w << 1; + + left_tm1x << 0; + left_tm1w << 2; + + right_tm1x << 15; + right_tm1w << 2; + + pti.zoom = 4.0; + pti.zoomString = QStringLiteral("int zoom"); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_tm1x, mid_tm1w, mid_tm1x, mid_tm1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_tm1x, mid_tm1w, top_tm1x, top_tm1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_tm1x, mid_tm1w, bottom_tm1x, bottom_tm1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_tm1x, left_tm1w, mid_tm1x, mid_tm1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_tm1x, right_tm1w, mid_tm1x, mid_tm1w); + + pti.zoom = 4.5; + pti.zoomString = QStringLiteral("frac zoom"); + pti.w = pti.w * qPow(2.0, 0.5); + pti.h = pti.h * qPow(2.0, 0.5); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_tm1x, mid_tm1w, mid_tm1x, mid_tm1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_tm1x, mid_tm1w, top_tm1x, top_tm1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_tm1x, mid_tm1w, bottom_tm1x, bottom_tm1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_tm1x, left_tm1w, mid_tm1x, mid_tm1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_tm1x, right_tm1w, mid_tm1x, mid_tm1w); + + /* + width = t + */ + + QList mid_tx; + QList mid_tw; + QList top_tx; + QList top_tw; + QList bottom_tx; + QList bottom_tw; + QList left_tx; + QList left_tw; + QList right_tx; + QList right_tw; + + pti.w = t; + pti.h = t; + pti.wString = QStringLiteral("1T"); + pti.hString = QStringLiteral("1T"); + + /* + + offset = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * + + + + + + * + * + + + + + + + + + + * + T T O T T + + + + + + + + + + + * T * T + + + + + * * O * * T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 2 tiles + */ + + mid_tx << 7; + mid_tw << 2; + + top_tx << 0; + top_tw << 1; + + bottom_tx << 14; + bottom_tw << 2; + + left_tx << 15; + left_tw << 2; + + right_tx << 14; + right_tw << 2; + + /* + offset = 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * + + + + + + * + * + + + + + + + + + + + * T T T O T + + + + + + + + + + + * T * T + + + + + * O * * * T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 2 tiles + */ + + mid_tx << 7; + mid_tw << 2; + + top_tx << 0; + top_tw << 1; + + bottom_tx << 14; + bottom_tw << 2; + + left_tx << 15; + left_tw << 2; + + right_tx << 14; + right_tw << 2; + + /* + offset = 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * + + + + + * * + + + + + + + + + + + O T T T O + + + + + + + + + + + O O + + + + + O * * * O + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_tx << 7; + mid_tw << 3; + + top_tx << 0; + top_tw << 2; + + bottom_tx << 14; + bottom_tw << 2; + + left_tx << 15; + left_tw << 3; + + right_tx << 14; + right_tw << 3; + + /* + offset = 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * + + + + + + * * + + + + + + + + + + + T O T T T * + + + + + + + + + + T * T * + + + + + T * * * O * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 2 tiles + */ + + mid_tx << 8; + mid_tw << 2; + + top_tx << 0; + top_tw << 2; + + bottom_tx << 15; + bottom_tw << 1; + + left_tx << 0; + left_tw << 2; + + right_tx << 15; + right_tw << 2; + + /* + offset = 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * + + + + + + * + * + + + + + + + + + + + T T O T T + * + + + + + + + + + T * T * + + + + + T * * O * * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 2 tiles + */ + + mid_tx << 8; + mid_tw << 2; + + top_tx << 0; + top_tw << 2; + + bottom_tx << 15; + bottom_tw << 1; + + left_tx << 0; + left_tw << 2; + + right_tx << 15; + right_tw << 2; + + pti.zoom = 4.0; + pti.zoomString = QStringLiteral("int zoom"); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_tx, mid_tw, mid_tx, mid_tw); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_tx, mid_tw, top_tx, top_tw); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_tx, mid_tw, bottom_tx, bottom_tw); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_tx, left_tw, mid_tx, mid_tw); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_tx, right_tw, mid_tx, mid_tw); + + pti.zoom = 4.5; + pti.zoomString = QStringLiteral("frac zoom"); + pti.w = pti.w * qPow(2.0, 0.5); + pti.h = pti.h * qPow(2.0, 0.5); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_tx, mid_tw, mid_tx, mid_tw); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_tx, mid_tw, top_tx, top_tw); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_tx, mid_tw, bottom_tx, bottom_tw); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_tx, left_tw, mid_tx, mid_tw); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_tx, right_tw, mid_tx, mid_tw); + + /* + width = t + 1 + */ + + QList mid_tp1x; + QList mid_tp1w; + QList top_tp1x; + QList top_tp1w; + QList bottom_tp1x; + QList bottom_tp1w; + QList left_tp1x; + QList left_tp1w; + QList right_tp1x; + QList right_tp1w; + + pti.w = t + 1; + pti.h = t + 1; + pti.wString = QStringLiteral("(1T + 1)"); + pti.hString = QStringLiteral("(1T + 1)"); + + /* + + offset = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * *+* * * + + + + + + * + * + + + + + + + + + +*+ + T T T*T T + + + + + + + + + + + * T * T + + + + + * * *T* * * T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 2 tiles + */ + + mid_tp1x << 7; + mid_tp1w << 2; + + top_tp1x << 0; + top_tp1w << 1; + + bottom_tp1x << 14; + bottom_tp1w << 2; + + left_tp1x << 15; + left_tp1w << 2; + + right_tp1x << 14; + right_tp1w << 2; + + /* + offset = 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * *+* * * *+ + + + + + * + *+ + + + + + + + + + +*+ T T T T*T + + + + + + + + + + + * T *T + + + + + * *T* * * *T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 2 tiles + */ + + mid_tp1x << 7; + mid_tp1w << 2; + + top_tp1x << 0; + top_tp1w << 1; + + bottom_tp1x << 14; + bottom_tp1w << 2; + + left_tp1x << 15; + left_tp1w << 2; + + right_tp1x << 14; + right_tp1w << 2; + + /* + offset = 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *+* * * *+* + + + + + *+ +* + + + + + + + + + + +*T T T T T*+ + + + + + + + + + + *T T* + + + + + *T* * * *T* + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_tp1x << 7; + mid_tp1w << 3; + + top_tp1x << 0; + top_tp1w << 2; + + bottom_tp1x << 14; + bottom_tp1w << 2; + + left_tp1x << 15; + left_tp1w << 3; + + right_tp1x << 14; + right_tp1w << 3; + + /* + offset = 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +* * * *+* * + + + + + +* + * + + + + + + + + + + + T*T T T T +*+ + + + + + + + + + T* T * + + + + + T* * * *T* * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 2 tiles + */ + + mid_tp1x << 8; + mid_tp1w << 2; + + top_tp1x << 0; + top_tp1w << 2; + + bottom_tp1x << 15; + bottom_tp1w << 1; + + left_tp1x << 0; + left_tp1w << 2; + + right_tp1x << 15; + right_tp1w << 2; + + /* + offset = 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * *+* * * + + + + + + * + * + + + + + + + + + + + T T*T T T + +*+ + + + + + + + + T * T * + + + + + T * * *T* * * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 2 tiles + */ + + mid_tp1x << 8; + mid_tp1w << 2; + + top_tp1x << 0; + top_tp1w << 2; + + bottom_tp1x << 15; + bottom_tp1w << 1; + + left_tp1x << 0; + left_tp1w << 2; + + right_tp1x << 15; + right_tp1w << 2; + + pti.zoom = 4.0; + pti.zoomString = QStringLiteral("int zoom"); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_tp1x, mid_tp1w, mid_tp1x, mid_tp1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_tp1x, mid_tp1w, top_tp1x, top_tp1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_tp1x, mid_tp1w, bottom_tp1x, bottom_tp1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_tp1x, left_tp1w, mid_tp1x, mid_tp1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_tp1x, right_tp1w, mid_tp1x, mid_tp1w); + + pti.zoom = 4.5; + pti.zoomString = QStringLiteral("frac zoom"); + pti.w = pti.w * qPow(2.0, 0.5); + pti.h = pti.h * qPow(2.0, 0.5); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_tp1x, mid_tp1w, mid_tp1x, mid_tp1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_tp1x, mid_tp1w, top_tp1x, top_tp1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_tp1x, mid_tp1w, bottom_tp1x, bottom_tp1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_tp1x, left_tp1w, mid_tp1x, mid_tp1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_tp1x, right_tp1w, mid_tp1x, mid_tp1w); + + /* + width = 2t - 1 + */ + + QList mid_t2m1x; + QList mid_t2m1w; + QList top_t2m1x; + QList top_t2m1w; + QList bottom_t2m1x; + QList bottom_t2m1w; + QList left_t2m1x; + QList left_t2m1w; + QList right_t2m1x; + QList right_t2m1w; + + pti.w = 2 * t - 1; + pti.h = 2 * t - 1; + pti.wString = QStringLiteral("(2T - 1)"); + pti.hString = QStringLiteral("(2T - 1)"); + + /* + offset = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +* * * *+* * * *+ + + + + +* + *+ + + + + + + + +*+ + + T T T T*T + + + + + + + + + + +* T *T + + + + +* * * *T* * * *T + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 2 tiles + */ + + mid_t2m1x << 7; + mid_t2m1w << 2; + + top_t2m1x << 0; + top_t2m1w << 1; + + bottom_t2m1x << 14; + bottom_t2m1w << 2; + + left_t2m1x << 15; + left_t2m1w << 2; + + right_t2m1x << 14; + right_t2m1w << 2; + + /* + offset = 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * *+* * * *+* + + + + + * + +* + + + + + + + + +*+ + T T T T T*+ + + + + + + + + + + * T T* + + + + + * * *T* * * *T* + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2m1x << 7; + mid_t2m1w << 3; + + top_t2m1x << 0; + top_t2m1w << 2; + + bottom_t2m1x << 14; + bottom_t2m1w << 2; + + left_t2m1x << 15; + left_t2m1w << 3; + + right_t2m1x << 14; + right_t2m1w << 3; + + /* + offset = 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * *+* * * *+* * + + + + + * + + * + + + + + + + + + +*+ T T T T T +*+ + + + + + + + + + * T T * + + + + + * *T* * * *T* * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2m1x << 7; + mid_t2m1w << 3; + + top_t2m1x << 0; + top_t2m1w << 2; + + bottom_t2m1x << 14; + bottom_t2m1w << 2; + + left_t2m1x << 15; + left_t2m1w << 3; + + right_t2m1x << 14; + right_t2m1w << 3; + + /* + offset = 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *+* * * *+* * * + + + + + *+ + * + + + + + + + + + + +*T T T T T + +*+ + + + + + + + + *T T * + + + + + *T* * * *T* * * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2m1x << 7; + mid_t2m1w << 3; + + top_t2m1x << 0; + top_t2m1w << 2; + + bottom_t2m1x << 14; + bottom_t2m1w << 2; + + left_t2m1x << 15; + left_t2m1w << 3; + + right_t2m1x << 14; + right_t2m1w << 3; + + /* + offset = 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +* * * *+* * * *+ + + + + +* + *+ + + + + + + + + + + T*T T T T + + +*+ + + + + + + + T* T *+ + + + + T* * * *T* * * *+ + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T + Covers: 2 tiles + */ + + mid_t2m1x << 8; + mid_t2m1w << 2; + + top_t2m1x << 0; + top_t2m1w << 2; + + bottom_t2m1x << 15; + bottom_t2m1w << 1; + + left_t2m1x << 0; + left_t2m1w << 2; + + right_t2m1x << 15; + right_t2m1w << 2; + + pti.zoom = 4.0; + pti.zoomString = QStringLiteral("int zoom"); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_t2m1x, mid_t2m1w, mid_t2m1x, mid_t2m1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_t2m1x, mid_t2m1w, top_t2m1x, top_t2m1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_t2m1x, mid_t2m1w, bottom_t2m1x, bottom_t2m1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_t2m1x, left_t2m1w, mid_t2m1x, mid_t2m1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_t2m1x, right_t2m1w, mid_t2m1x, mid_t2m1w); + + pti.zoom = 4.5; + pti.zoomString = QStringLiteral("frac zoom"); + pti.w = pti.w * qPow(2.0, 0.5); + pti.h = pti.h * qPow(2.0, 0.5); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_t2m1x, mid_t2m1w, mid_t2m1x, mid_t2m1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_t2m1x, mid_t2m1w, top_t2m1x, top_t2m1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_t2m1x, mid_t2m1w, bottom_t2m1x, bottom_t2m1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_t2m1x, left_t2m1w, mid_t2m1x, mid_t2m1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_t2m1x, right_t2m1w, mid_t2m1x, mid_t2m1w); + + /* + width = 2t + */ + + + QList mid_t2x; + QList mid_t2w; + QList top_t2x; + QList top_t2w; + QList bottom_t2x; + QList bottom_t2w; + QList left_t2x; + QList left_t2w; + QList right_t2x; + QList right_t2w; + + pti.w = 2 * t; + pti.h = 2 * t; + pti.wString = QStringLiteral("2T"); + pti.hString = QStringLiteral("2T"); + + /* + + offset = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * * * * * + + + + * + * + + + + + + + * + + + T T T T O + + + + + + + + + + * T O + + + + * * * * O * * * O + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 2 + Covers: 4 tiles + */ + + mid_t2x << 6; + mid_t2w << 4; + + top_t2x << 0; + top_t2w << 2; + + bottom_t2x << 13; + bottom_t2w << 3; + + left_t2x << 14; + left_t2w << 4; + + right_t2x << 13; + right_t2w << 4; + + /* + offset = 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * * * * * + + + + + * + * + + + + + + + + * + + T T T T T * + + + + + + + + + + * T T * + + + + + * * * O * * * O * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2x << 7; + mid_t2w << 3; + + top_t2x << 0; + top_t2w << 2; + + bottom_t2x << 14; + bottom_t2w << 2; + + left_t2x << 15; + left_t2w << 3; + + right_t2x << 14; + right_t2w << 3; + + /* + offset = 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * * * * * + + + + + * + * + + + + + + + + + * + T T T T T + * + + + + + + + + + * T T * + + + + + * * O * * * O * * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2x << 7; + mid_t2w << 3; + + top_t2x << 0; + top_t2w << 2; + + bottom_t2x << 14; + bottom_t2w << 2; + + left_t2x << 15; + left_t2w << 3; + + right_t2x << 14; + right_t2w << 3; + + /* + offset = 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * * * * * + + + + + * + * + + + + + + + + + + * T T T T T + + * + + + + + + + + * T T * + + + + + * O * * * O * * * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2x << 7; + mid_t2w << 3; + + top_t2x << 0; + top_t2w << 2; + + bottom_t2x << 14; + bottom_t2w << 2; + + left_t2x << 15; + left_t2w << 3; + + right_t2x << 14; + right_t2w << 3; + + /* + offset = 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * * * * * * * * + + + + * * + + + + + + + + + + O T T T T + + + * + + + + + + + O T * + + + + O * * * O * * * * + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 4 tiles + */ + + mid_t2x << 7; + mid_t2w << 4; + + top_t2x << 0; + top_t2w << 3; + + bottom_t2x << 14; + bottom_t2w << 2; + + left_t2x << 15; + left_t2w << 4; + + right_t2x << 14; + right_t2w << 4; + + pti.zoom = 4.0; + pti.zoomString = QStringLiteral("int zoom"); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_t2x, mid_t2w, mid_t2x, mid_t2w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_t2x, mid_t2w, top_t2x, top_t2w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_t2x, mid_t2w, bottom_t2x, bottom_t2w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_t2x, left_t2w, mid_t2x, mid_t2w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_t2x, right_t2w, mid_t2x, mid_t2w); + + pti.zoom = 4.5; + pti.zoomString = QStringLiteral("frac zoom"); + pti.w = pti.w * qPow(2.0, 0.5); + pti.h = pti.h * qPow(2.0, 0.5); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_t2x, mid_t2w, mid_t2x, mid_t2w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_t2x, mid_t2w, top_t2x, top_t2w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_t2x, mid_t2w, bottom_t2x, bottom_t2w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_t2x, left_t2w, mid_t2x, mid_t2w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_t2x, right_t2w, mid_t2x, mid_t2w); + + /* + width = 2t + 1 + */ + + QList mid_t2p1x; + QList mid_t2p1w; + QList top_t2p1x; + QList top_t2p1w; + QList bottom_t2p1x; + QList bottom_t2p1w; + QList left_t2p1x; + QList left_t2p1w; + QList right_t2p1x; + QList right_t2p1w; + + pti.w = 2 * t + 1; + pti.h = 2 * t + 1; + pti.wString = QStringLiteral("(2T + 1)"); + pti.hString = QStringLiteral("(2T + 1)"); + + /* + + offset = 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *+* * * *+* * * *+* + + + + *+ + +* + + + + + + +*+ + + + T T T T T*+ + + + + + + + + + *+ T T* + + + + *+* * * *T* * * *T* + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 2 + Covers: 4 tiles + */ + + mid_t2p1x << 6; + mid_t2p1w << 4; + + top_t2p1x << 0; + top_t2p1w << 2; + + bottom_t2p1x << 13; + bottom_t2p1w << 3; + + left_t2p1x << 14; + left_t2p1w << 4; + + right_t2p1x << 13; + right_t2p1w << 4; + + /* + offset = 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +* * * *+* * * *+* * + + + + +* + + * + + + + + + + +*+ + + T T T T T +*+ + + + + + + + + +* T T * + + + + +* * * *T* * * *T* * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2p1x << 7; + mid_t2p1w << 3; + + top_t2p1x << 0; + top_t2p1w << 2; + + bottom_t2p1x << 14; + bottom_t2p1w << 2; + + left_t2p1x << 15; + left_t2p1w << 3; + + right_t2p1x << 14; + right_t2p1w << 3; + + /* + offset = 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * * *+* * * *+* * * + + + + + * + + * + + + + + + + + +*+ + T T T T T + +*+ + + + + + + + + * T T * + + + + + * * *T* * * *T* * * + + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2p1x << 7; + mid_t2p1w << 3; + + top_t2p1x << 0; + top_t2p1w << 2; + + bottom_t2p1x << 14; + bottom_t2p1w << 2; + + left_t2p1x << 15; + left_t2p1w << 3; + + right_t2p1x << 14; + right_t2p1w << 3; + + /* + offset = 3 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + * *+* * * *+* * * *+ + + + + * + + *+ + + + + + + + + +*+ T T T T T + + +*+ + + + + + + + * T T *+ + + + + * *T* * * *T* * * *+ + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 3 tiles + */ + + mid_t2p1x << 7; + mid_t2p1w << 3; + + top_t2p1x << 0; + top_t2p1w << 2; + + bottom_t2p1x << 14; + bottom_t2p1w << 2; + + left_t2p1x << 15; + left_t2p1w << 3; + + right_t2p1x << 14; + right_t2p1w << 3; + + /* + offset = 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + *+* * * *+* * * *+* + + + + *+ + +* + + + + + + + + + +*T T T T T + + + +*+ + + + + + + *T T +* + + + + *T* * * *T* * * *+* + + + + T T + + + + + + + + + + + T T T T T + + + + + + + + + + Starts at: T - 1 + Covers: 4 tiles + */ + + mid_t2p1x << 7; + mid_t2p1w << 4; + + top_t2p1x << 0; + top_t2p1w << 3; + + bottom_t2p1x << 14; + bottom_t2p1w << 2; + + left_t2p1x << 15; + left_t2p1w << 4; + + right_t2p1x << 14; + right_t2p1w << 4; + + pti.zoom = 4.0; + pti.zoomString = QStringLiteral("int zoom"); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_t2p1x, mid_t2p1w, mid_t2p1x, mid_t2p1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_t2p1x, mid_t2p1w, top_t2p1x, top_t2p1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_t2p1x, mid_t2p1w, bottom_t2p1x, bottom_t2p1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_t2p1x, left_t2p1w, mid_t2p1x, mid_t2p1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_t2p1x, right_t2p1w, mid_t2p1x, mid_t2p1w); + + pti.zoom = 4.5; + pti.zoomString = QStringLiteral("frac zoom"); + pti.w = pti.w * qPow(2.0, 0.5); + pti.h = pti.h * qPow(2.0, 0.5); + + pti.x = 0.5; + pti.y = 0.5; + pti.xyString = QStringLiteral("middle"); + + test_group(pti, mid_t2p1x, mid_t2p1w, mid_t2p1x, mid_t2p1w); + + pti.x = 0.5; + pti.y = 0.0; + pti.xyString = QStringLiteral("top"); + + test_group(pti, mid_t2p1x, mid_t2p1w, top_t2p1x, top_t2p1w); + + pti.x = 0.5; + pti.y = 15.0 / 16.0; + pti.xyString = QStringLiteral("bottom"); + + test_group(pti, mid_t2p1x, mid_t2p1w, bottom_t2p1x, bottom_t2p1w); + + pti.x = 0.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("left"); + + test_group(pti, left_t2p1x, left_t2p1w, mid_t2p1x, mid_t2p1w); + + pti.x = 15.0 / 16.0; + pti.y = 0.5; + pti.xyString = QStringLiteral("right"); + + test_group(pti, right_t2p1x, right_t2p1w, mid_t2p1x, mid_t2p1w); +} + +QTEST_GUILESS_MAIN(tst_QGeoCameraTiles) +#include "tst_qgeocameratiles.moc" diff --git a/tests/auto/qgeocircle/qgeocircle.pro b/tests/auto/qgeocircle/qgeocircle.pro new file mode 100644 index 0000000..eb56a7f --- /dev/null +++ b/tests/auto/qgeocircle/qgeocircle.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeocircle + +SOURCES += \ + tst_qgeocircle.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeocircle/tst_qgeocircle.cpp b/tests/auto/qgeocircle/tst_qgeocircle.cpp new file mode 100644 index 0000000..8777e7d --- /dev/null +++ b/tests/auto/qgeocircle/tst_qgeocircle.cpp @@ -0,0 +1,453 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoCircle : public QObject +{ + Q_OBJECT + +private slots: + void defaultConstructor(); + void centerRadiusConstructor(); + void assignment(); + + void comparison(); + void type(); + + void radius(); + void center(); + + void translate_data(); + void translate(); + + void valid_data(); + void valid(); + + void empty_data(); + void empty(); + + void contains_data(); + void contains(); + + void boundingGeoRectangle_data(); + void boundingGeoRectangle(); + + void extendCircle(); + void extendCircle_data(); + + void areaComparison(); + void areaComparison_data(); + + void boxComparison(); + void boxComparison_data(); +}; + +void tst_QGeoCircle::defaultConstructor() +{ + QGeoCircle c; + QVERIFY(!c.center().isValid()); + QCOMPARE(c.radius(), qreal(-1.0)); +} + +void tst_QGeoCircle::centerRadiusConstructor() +{ + QGeoCircle c(QGeoCoordinate(1,1), qreal(50.0)); + QCOMPARE(c.center(), QGeoCoordinate(1,1)); + QCOMPARE(c.radius(), qreal(50.0)); +} + +void tst_QGeoCircle::assignment() +{ + QGeoCircle c1 = QGeoCircle(QGeoCoordinate(10.0, 0.0), 20.0); + QGeoCircle c2 = QGeoCircle(QGeoCoordinate(20.0, 0.0), 30.0); + + QVERIFY(c1 != c2); + + c2 = c1; + QCOMPARE(c2.center(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(c2.radius(), 20.0); + QCOMPARE(c1, c2); + + c2.setCenter(QGeoCoordinate(30.0, 0.0)); + c2.setRadius(15.0); + QCOMPARE(c1.center(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(c1.radius(), 20.0); + + // Assign c1 to an area + QGeoShape area = c1; + QCOMPARE(area.type(), c1.type()); + QVERIFY(area == c1); + + // Assign the area back to a bounding circle + QGeoCircle ca = area; + QCOMPARE(ca.center(), c1.center()); + QCOMPARE(ca.radius(), c1.radius()); + + // Check that the copy is not modified when modifying the original. + c1.setCenter(QGeoCoordinate(15.0, 15.0)); + QVERIFY(ca.center() != c1.center()); + QVERIFY(ca != c1); +} + +void tst_QGeoCircle::comparison() +{ + QGeoCircle c1(QGeoCoordinate(1,1), qreal(50.0)); + QGeoCircle c2(QGeoCoordinate(1,1), qreal(50.0)); + QGeoCircle c3(QGeoCoordinate(1,1), qreal(35.0)); + QGeoCircle c4(QGeoCoordinate(1,2), qreal(50.0)); + + QVERIFY(c1 == c2); + QVERIFY(!(c1 != c2)); + + QVERIFY(!(c1 == c3)); + QVERIFY(c1 != c3); + + QVERIFY(!(c1 == c4)); + QVERIFY(c1 != c4); + + QVERIFY(!(c2 == c3)); + QVERIFY(c2 != c3); + + QGeoRectangle b1(QGeoCoordinate(20,20),QGeoCoordinate(10,30)); + QVERIFY(!(c1 == b1)); + QVERIFY(c1 != b1); + + QGeoShape *c2Ptr = &c2; + QVERIFY(c1 == *c2Ptr); + QVERIFY(!(c1 != *c2Ptr)); + + QGeoShape *c3Ptr = &c3; + QVERIFY(!(c1 == *c3Ptr)); + QVERIFY(c1 != *c3Ptr); +} + +void tst_QGeoCircle::type() +{ + QGeoCircle c; + QCOMPARE(c.type(), QGeoShape::CircleType); +} + +void tst_QGeoCircle::radius() +{ + QGeoCircle c; + c.setRadius(1.0); + QCOMPARE(c.radius(), qreal(1.0)); + c.setRadius(5.0); + QCOMPARE(c.radius(), qreal(5.0)); +} + +void tst_QGeoCircle::center() +{ + QGeoCircle c; + c.setCenter(QGeoCoordinate(1,1)); + QCOMPARE(c.center(), QGeoCoordinate(1,1)); + + QGeoShape shape = c; + QCOMPARE(shape.center(), c.center()); + + c.setCenter(QGeoCoordinate(5,10)); + QCOMPARE(c.center(), QGeoCoordinate(5,10)); +} + +void tst_QGeoCircle::translate_data() +{ + QTest::addColumn("center"); + QTest::addColumn("radius"); + QTest::addColumn("lat"); + QTest::addColumn("lon"); + QTest::addColumn("newCenter"); + + QTest::newRow("from 0,0") << QGeoCoordinate(0,0) << qreal(10.0) << + 5.0 << 5.0 << QGeoCoordinate(5.0, 5.0); + QTest::newRow("across 0,0") << QGeoCoordinate(-2, -2) << qreal(20.0) << + 5.0 << 5.0 << QGeoCoordinate(3.0, 3.0); + QTest::newRow("backwards across 0,0") << QGeoCoordinate(5,5) << qreal(50.0) + << -13.0 << 5.0 + << QGeoCoordinate(-8.0, 10.0); +} + +void tst_QGeoCircle::translate() +{ + QFETCH(QGeoCoordinate, center); + QFETCH(qreal, radius); + QFETCH(double, lat); + QFETCH(double, lon); + QFETCH(QGeoCoordinate, newCenter); + + QGeoCircle c(center, radius); + QGeoCircle d = c; + + c.translate(lat, lon); + + QCOMPARE(c.radius(), radius); + QCOMPARE(c.center(), newCenter); + + c = d.translated(lat, lon); + d.setRadius(1.0); + + QCOMPARE(c.radius(), radius); + QCOMPARE(d.center(), center); + QCOMPARE(c.center(), newCenter); +} + +void tst_QGeoCircle::valid_data() +{ + QTest::addColumn("center"); + QTest::addColumn("radius"); + QTest::addColumn("valid"); + + QTest::newRow("default") << QGeoCoordinate() << qreal(-1.0) << false; + QTest::newRow("empty coord") << QGeoCoordinate() << qreal(5.0) << false; + QTest::newRow("NaN coord") << QGeoCoordinate(500, 500) << qreal(5.0) << false; + QTest::newRow("bad radius") << QGeoCoordinate(10, 10) << qreal(-5.0) << false; + QTest::newRow("NaN radius") << QGeoCoordinate(10, 10) << qreal(qQNaN()) << false; + QTest::newRow("zero radius") << QGeoCoordinate(10, 10) << qreal(0.0) << true; + QTest::newRow("good") << QGeoCoordinate(10, 10) << qreal(5.0) << true; +} + +void tst_QGeoCircle::valid() +{ + QFETCH(QGeoCoordinate, center); + QFETCH(qreal, radius); + QFETCH(bool, valid); + + QGeoCircle c(center, radius); + QCOMPARE(c.isValid(), valid); + + QGeoShape area = c; + QCOMPARE(area.isValid(), valid); +} + +void tst_QGeoCircle::empty_data() +{ + QTest::addColumn("center"); + QTest::addColumn("radius"); + QTest::addColumn("empty"); + + QTest::newRow("default") << QGeoCoordinate() << qreal(-1.0) << true; + QTest::newRow("empty coord") << QGeoCoordinate() << qreal(5.0) << true; + QTest::newRow("NaN coord") << QGeoCoordinate(500, 500) << qreal(5.0) << true; + QTest::newRow("bad radius") << QGeoCoordinate(10, 10) << qreal(-5.0) << true; + QTest::newRow("NaN radius") << QGeoCoordinate(10, 10) << qreal(qQNaN()) << true; + QTest::newRow("zero radius") << QGeoCoordinate(10, 10) << qreal(0.0) << true; + QTest::newRow("good") << QGeoCoordinate(10, 10) << qreal(5.0) << false; +} + +void tst_QGeoCircle::empty() +{ + QFETCH(QGeoCoordinate, center); + QFETCH(qreal, radius); + QFETCH(bool, empty); + + QGeoCircle c(center, radius); + QCOMPARE(c.isEmpty(), empty); + + QGeoShape area = c; + QCOMPARE(area.isEmpty(), empty); +} + +void tst_QGeoCircle::contains_data() +{ + QTest::addColumn("center"); + QTest::addColumn("radius"); + QTest::addColumn("probe"); + QTest::addColumn("result"); + + QTest::newRow("own center") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1,1) << true; + QTest::newRow("over the hills") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(30, 40) << false; + QTest::newRow("at 0.5*radius") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1.00015374,1.00015274) << true; + QTest::newRow("at 0.99*radius") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1.00077538, 0.99955527) << true; + QTest::newRow("at 1.01*radius") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1.00071413, 0.99943423) << false; + // TODO: add tests for edge circle cases: cross 1 pole, cross both poles +} + +void tst_QGeoCircle::contains() +{ + QFETCH(QGeoCoordinate, center); + QFETCH(qreal, radius); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, result); + + QGeoCircle c(center, radius); + QCOMPARE(c.contains(probe), result); + + QGeoShape area = c; + QCOMPARE(area.contains(probe), result); +} + +void tst_QGeoCircle::boundingGeoRectangle_data() +{ + QTest::addColumn("center"); + QTest::addColumn("radius"); + QTest::addColumn("probe"); + QTest::addColumn("result"); + + QTest::newRow("own center") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1,1) << true; + QTest::newRow("over the hills") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(30, 40) << false; + QTest::newRow("at 0.5*radius") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1.00015374,1.00015274) << true; + QTest::newRow("at 0.99*radius") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1.00077538, 0.99955527) << true; + QTest::newRow("Outside the box") << QGeoCoordinate(1,1) << qreal(100.0) << + QGeoCoordinate(1.00071413, 0.99903423) << false; + // TODO: add tests for edge circle cases: cross 1 pole, cross both poles +} + +void tst_QGeoCircle::boundingGeoRectangle() +{ + QFETCH(QGeoCoordinate, center); + QFETCH(qreal, radius); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, result); + + QGeoCircle c(center, radius); + QGeoRectangle box = c.boundingGeoRectangle(); + QCOMPARE(box.contains(probe), result); +} + +void tst_QGeoCircle::extendCircle() +{ + QFETCH(QGeoCircle, circle); + QFETCH(QGeoCoordinate, coord); + QFETCH(bool, containsFirst); + QFETCH(bool, containsExtended); + + QCOMPARE(circle.contains(coord), containsFirst); + circle.extendCircle(coord); + QCOMPARE(circle.contains(coord), containsExtended); + +} + +void tst_QGeoCircle::extendCircle_data() +{ + QTest::addColumn("circle"); + QTest::addColumn("coord"); + QTest::addColumn("containsFirst"); + QTest::addColumn("containsExtended"); + + QGeoCoordinate co1(20.0, 20.0); + + QTest::newRow("own center") + << QGeoCircle(co1, 100) + << QGeoCoordinate(20.0, 20.0) + << true + << true; + QTest::newRow("inside") + << QGeoCircle(co1, 100) + << QGeoCoordinate(20.0001, 20.0001) + << true + << true; + QTest::newRow("far away") + << QGeoCircle(co1, 100) + << QGeoCoordinate(50.0001, 50.0001) + << false + << true; + QTest::newRow("invalid circle") + << QGeoCircle() + << QGeoCoordinate(20.0, 20.0) + << false + << false; + QTest::newRow("invalid coordinate") + << QGeoCircle(co1, 100) + << QGeoCoordinate(99.0, 190.0) + << false + << false; +} + +void tst_QGeoCircle::areaComparison_data() +{ + QTest::addColumn("area"); + QTest::addColumn("circle"); + QTest::addColumn("equal"); + + QGeoCircle c1(QGeoCoordinate(10.0, 0.0), 10.0); + QGeoCircle c2(QGeoCoordinate(20.0, 10.0), 20.0); + QGeoRectangle b(QGeoCoordinate(10.0, 0.0), QGeoCoordinate(0.0, 10.0)); + + QTest::newRow("default constructed") << QGeoShape() << QGeoCircle() << false; + QTest::newRow("c1 c1") << QGeoShape(c1) << c1 << true; + QTest::newRow("c1 c2") << QGeoShape(c1) << c2 << false; + QTest::newRow("c2 c1") << QGeoShape(c2) << c1 << false; + QTest::newRow("c2 c2") << QGeoShape(c2) << c2 << true; + QTest::newRow("b c1") << QGeoShape(b) << c1 << false; +} + +void tst_QGeoCircle::areaComparison() +{ + QFETCH(QGeoShape, area); + QFETCH(QGeoCircle, circle); + QFETCH(bool, equal); + + QCOMPARE((area == circle), equal); + QCOMPARE((area != circle), !equal); + + QCOMPARE((circle == area), equal); + QCOMPARE((circle != area), !equal); +} + +void tst_QGeoCircle::boxComparison_data() +{ + QTest::addColumn("box"); + QTest::addColumn("circle"); + QTest::addColumn("equal"); + + QGeoCircle c(QGeoCoordinate(10.0, 0.0), 10.0); + QGeoRectangle b(QGeoCoordinate(10.0, 0.0), QGeoCoordinate(0.0, 10.0)); + + QTest::newRow("default constructed") << QGeoRectangle() << QGeoCircle() << false; + QTest::newRow("b c") << b << c << false; +} + +void tst_QGeoCircle::boxComparison() +{ + QFETCH(QGeoRectangle, box); + QFETCH(QGeoCircle, circle); + QFETCH(bool, equal); + + QCOMPARE((box == circle), equal); + QCOMPARE((box != circle), !equal); + + QCOMPARE((circle == box), equal); + QCOMPARE((circle != box), !equal); +} + +QTEST_MAIN(tst_QGeoCircle) +#include "tst_qgeocircle.moc" diff --git a/tests/auto/qgeocodereply/qgeocodereply.pro b/tests/auto/qgeocodereply/qgeocodereply.pro new file mode 100644 index 0000000..dea3d4c --- /dev/null +++ b/tests/auto/qgeocodereply/qgeocodereply.pro @@ -0,0 +1,10 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeocodereply + +HEADERS += ../utils/qlocationtestutils_p.h \ + tst_qgeocodereply.h +SOURCES += tst_qgeocodereply.cpp \ + ../utils/qlocationtestutils.cpp + +QT += location testlib diff --git a/tests/auto/qgeocodereply/tst_qgeocodereply.cpp b/tests/auto/qgeocodereply/tst_qgeocodereply.cpp new file mode 100644 index 0000000..bd47666 --- /dev/null +++ b/tests/auto/qgeocodereply/tst_qgeocodereply.cpp @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_qgeocodereply.h" + +QT_USE_NAMESPACE + +void tst_QGeoCodeReply::initTestCase() +{ + + reply = new SubGeocodeReply(); +} + +void tst_QGeoCodeReply::cleanupTestCase() +{ + + delete reply; + delete qgeolocation; +} + +void tst_QGeoCodeReply::init() +{ + qRegisterMetaType(); + signalerror = new QSignalSpy(reply, SIGNAL(error(QGeoCodeReply::Error,QString))); + signalfinished = new QSignalSpy(reply, SIGNAL(finished())); +} + +void tst_QGeoCodeReply::cleanup() +{ + delete signalerror; + delete signalfinished; +} + +void tst_QGeoCodeReply::constructor() +{ + QVERIFY(!reply->isFinished()); + + QCOMPARE(reply->limit(),-1); + QCOMPARE(reply->offset(),0); + QCOMPARE(reply->error(),QGeoCodeReply::NoError); + + QVERIFY( signalerror->isValid() ); + QVERIFY( signalfinished->isValid() ); + + QCOMPARE(signalerror->count(),0); + QCOMPARE(signalfinished->count(),0); +} + +void tst_QGeoCodeReply::constructor_error() +{ + QFETCH(QGeoCodeReply::Error,error); + QFETCH(QString,msg); + + QVERIFY( signalerror->isValid() ); + QVERIFY( signalfinished->isValid() ); + + QGeoCodeReply *qgeocodereplycopy = new QGeoCodeReply (error,msg,0); + + QCOMPARE(signalerror->count(),0); + QCOMPARE(signalfinished->count(),0); + + QCOMPARE (qgeocodereplycopy->error(),error); + QCOMPARE (qgeocodereplycopy->errorString(),msg); + + delete qgeocodereplycopy; +} + +void tst_QGeoCodeReply::constructor_error_data() +{ + QTest::addColumn("error"); + QTest::addColumn("msg"); + + QTest::newRow("error1") << QGeoCodeReply::NoError << "No error."; + QTest::newRow("error2") << QGeoCodeReply::EngineNotSetError << "Engine Not Set Error."; + QTest::newRow("error3") << QGeoCodeReply::CommunicationError << "Communication Error."; + QTest::newRow("error4") << QGeoCodeReply::ParseError << "Parse Error."; + QTest::newRow("error5") << QGeoCodeReply::UnsupportedOptionError << "Unsupported Option Error."; + QTest::newRow("error6") << QGeoCodeReply::UnknownError << "Unknown Error."; + +} + +void tst_QGeoCodeReply::destructor() +{ + QGeoCodeReply *qgeocodereplycopy; + QFETCH(QGeoCodeReply::Error,error); + QFETCH(QString,msg); + + qgeocodereplycopy = new QGeoCodeReply (error,msg,0); + delete qgeocodereplycopy; +} + +void tst_QGeoCodeReply::destructor_data() +{ + tst_QGeoCodeReply::constructor_error_data(); +} + +void tst_QGeoCodeReply::abort() +{ + QVERIFY( signalerror->isValid() ); + QVERIFY( signalfinished->isValid() ); + + QCOMPARE(signalerror->count(),0); + QCOMPARE (signalfinished->count(),0); + + reply->callSetFinished(true); + reply->abort(); + + QCOMPARE(signalerror->count(),0); + QCOMPARE (signalfinished->count(),1); + + reply->abort(); + reply->callSetFinished(false); + reply->abort(); + + QCOMPARE(signalerror->count(),0); + QCOMPARE (signalfinished->count(),2); +} + +void tst_QGeoCodeReply::error() +{ + QFETCH(QGeoCodeReply::Error,error); + QFETCH(QString,msg); + + QVERIFY( signalerror->isValid() ); + QVERIFY( signalfinished->isValid() ); + QCOMPARE(signalerror->count(),0); + + reply->callSetError(error,msg); + + QCOMPARE(signalerror->count(),1); + QCOMPARE(signalfinished->count(),1); + QCOMPARE(reply->errorString(),msg); + QCOMPARE(reply->error(),error); + + +} + +void tst_QGeoCodeReply::error_data() +{ + QTest::addColumn("error"); + QTest::addColumn("msg"); + + QTest::newRow("error1") << QGeoCodeReply::NoError << "No error."; + QTest::newRow("error2") << QGeoCodeReply::EngineNotSetError << "Engine Not Set Error."; + QTest::newRow("error3") << QGeoCodeReply::CommunicationError << "Communication Error."; + QTest::newRow("error4") << QGeoCodeReply::ParseError << "Parse Error."; + QTest::newRow("error5") << QGeoCodeReply::UnsupportedOptionError << "Unsupported Option Error."; + QTest::newRow("error6") << QGeoCodeReply::UnknownError << "Unknown Error."; +} + +void tst_QGeoCodeReply::finished() +{ + QVERIFY( signalerror->isValid() ); + QVERIFY( signalfinished->isValid() ); + + QCOMPARE(signalerror->count(),0); + QCOMPARE (signalfinished->count(),0); + + reply->callSetFinished(true); + QVERIFY(reply->isFinished()); + QCOMPARE(signalerror->count(),0); + QCOMPARE (signalfinished->count(),1); + + reply->callSetFinished(false); + + QVERIFY(!reply->isFinished()); + QCOMPARE(signalerror->count(),0); + QCOMPARE (signalfinished->count(),1); + + reply->callSetFinished(true); + + QVERIFY(reply->isFinished()); + QCOMPARE(signalerror->count(),0); + QCOMPARE (signalfinished->count(),2); +} + + + +void tst_QGeoCodeReply::limit() +{ + int limit =30; + reply->callSetLimit(limit); + QCOMPARE(reply->limit(),limit); +} + +void tst_QGeoCodeReply::offset() +{ + int offset = 2; + reply->callSetOffset(offset); + QCOMPARE(reply->offset(),offset); +} + +void tst_QGeoCodeReply::locations() +{ + QList geolocations; + geolocations = reply->locations(); + + QCOMPARE(geolocations.size(),0); + + QGeoAddress *qgeoaddress = new QGeoAddress (); + qgeoaddress->setCity("Berlin"); + + QGeoCoordinate *qgeocoordinate = new QGeoCoordinate (12.12 , 54.43); + + qgeolocation = new QGeoLocation (); + qgeolocation->setAddress(*qgeoaddress); + qgeolocation->setCoordinate(*qgeocoordinate); + + reply->callAddLocation(*qgeolocation); + + geolocations = reply->locations(); + QCOMPARE(geolocations.size(),1); + QCOMPARE(geolocations.at(0),*qgeolocation); + + QGeoLocation *qgeolocationcopy = new QGeoLocation (*qgeolocation); + + QList qgeolocations; + qgeolocations.append(*qgeolocation); + qgeolocations.append(*qgeolocationcopy); + + reply->callSetLocations(qgeolocations); + + geolocations = reply->locations(); + + QCOMPARE(geolocations.size(),qgeolocations.size()); + for (int i = 0 ; i < geolocations.size(); i++) + { + QCOMPARE(geolocations.at(i),qgeolocations.at(i)); + } + + delete qgeoaddress; + delete qgeocoordinate; + delete qgeolocationcopy; +} + +void tst_QGeoCodeReply::viewport() +{ + QGeoCoordinate *qgeocoordinate = new QGeoCoordinate (12.12 , 54.43); + + qgeoboundingbox = new QGeoRectangle (*qgeocoordinate, 0.5 , 0.5); + + reply->callSetViewport(*qgeoboundingbox); + + QCOMPARE (reply->viewport(), static_cast(*qgeoboundingbox)); + + delete qgeocoordinate; + delete qgeoboundingbox; +} + +QTEST_MAIN(tst_QGeoCodeReply); diff --git a/tests/auto/qgeocodereply/tst_qgeocodereply.h b/tests/auto/qgeocodereply/tst_qgeocodereply.h new file mode 100644 index 0000000..3db16da --- /dev/null +++ b/tests/auto/qgeocodereply/tst_qgeocodereply.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_QGEOCODEREPLY_H +#define TST_QGEOCODEREPLY_H + +#include +#include +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" + +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE +class SubGeocodeReply : public QGeoCodeReply +{ + Q_OBJECT +public: + SubGeocodeReply() : QGeoCodeReply() {} + + void callAddLocation ( const QGeoLocation & location ) {addLocation(location);} + void callSetError ( Error error, const QString & errorString ) {setError(error, errorString);} + void callSetFinished ( bool finished ) {setFinished(finished);} + void callSetLimit ( int limit ) {setLimit(limit);} + void callSetOffset ( int offset ) {setOffset(offset);} + void callSetLocations ( const QList & locations ) {setLocations(locations);} + void callSetViewport ( const QGeoShape &viewport ) {setViewport(viewport);} + +}; + +class tst_QGeoCodeReply :public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + //Start Unit Test for QGeoCodeReply +private slots: + void constructor(); + void constructor_error(); + void constructor_error_data(); + void destructor(); + void destructor_data(); + void abort(); + void error(); + void error_data(); + void finished(); + void limit(); + void offset(); + void locations(); + void viewport(); + + //End Unit Test for QGeoCodeReply + + + +private: + QSignalSpy *signalerror; + QSignalSpy *signalfinished; + SubGeocodeReply* reply; + QGeoLocation *qgeolocation; + QGeoRectangle *qgeoboundingbox; +}; + +Q_DECLARE_METATYPE(QList); +Q_DECLARE_METATYPE(QGeoCodeReply::Error); + +#endif // TST_QGEOCODEREPLY_H + diff --git a/tests/auto/qgeocodingmanager/qgeocodingmanager.pro b/tests/auto/qgeocodingmanager/qgeocodingmanager.pro new file mode 100644 index 0000000..373f1ff --- /dev/null +++ b/tests/auto/qgeocodingmanager/qgeocodingmanager.pro @@ -0,0 +1,12 @@ +CONFIG += testcase +TARGET = tst_qgeocodingmanager + +HEADERS += ../utils/qlocationtestutils_p.h \ + tst_qgeocodingmanager.h + +SOURCES += tst_qgeocodingmanager.cpp \ + ../utils/qlocationtestutils.cpp + +CONFIG -= app_bundle + +QT += location testlib diff --git a/tests/auto/qgeocodingmanager/tst_qgeocodingmanager.cpp b/tests/auto/qgeocodingmanager/tst_qgeocodingmanager.cpp new file mode 100644 index 0000000..9647c0d --- /dev/null +++ b/tests/auto/qgeocodingmanager/tst_qgeocodingmanager.cpp @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "tst_qgeocodingmanager.h" + +QT_USE_NAMESPACE + + +void tst_QGeoCodingManager::initTestCase() +{ +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif + tst_QGeoCodingManager::loadGeocodingManager(); +} + +void tst_QGeoCodingManager::cleanupTestCase() +{ + delete qgeoserviceprovider; +} + +void tst_QGeoCodingManager::init() +{ + qRegisterMetaType(); + qRegisterMetaType(); + + signalerror = new QSignalSpy(qgeocodingmanager, SIGNAL(error(QGeoCodeReply*,QGeoCodeReply::Error,QString))); + signalfinished = new QSignalSpy(qgeocodingmanager, SIGNAL(finished(QGeoCodeReply*))); + QVERIFY( signalerror->isValid() ); + QVERIFY( signalfinished->isValid() ); +} + +void tst_QGeoCodingManager::cleanup() +{ + delete signalerror; + delete signalfinished; +} + +void tst_QGeoCodingManager::loadGeocodingManager() +{ + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + QVERIFY(providers.contains("geocode.test.plugin")); + + qgeoserviceprovider = new QGeoServiceProvider("geocode.test.plugin"); + QVERIFY(qgeoserviceprovider); + QCOMPARE(qgeoserviceprovider->error(), QGeoServiceProvider::NotSupportedError); + + qgeoserviceprovider->setAllowExperimental(true); + QCOMPARE(qgeoserviceprovider->error(), QGeoServiceProvider::NoError); + QCOMPARE(qgeoserviceprovider->geocodingFeatures(), + QGeoServiceProvider::OfflineGeocodingFeature + | QGeoServiceProvider::ReverseGeocodingFeature); + + qgeocodingmanager = qgeoserviceprovider->geocodingManager(); + QVERIFY(qgeocodingmanager); +} + +void tst_QGeoCodingManager::locale() +{ + QLocale *german = new QLocale (QLocale::German, QLocale::Germany); + QLocale *english = new QLocale (QLocale::C, QLocale::AnyCountry); + + //Default Locale from the Search Engine + QCOMPARE(qgeocodingmanager->locale(),*german); + + qgeocodingmanager->setLocale(*english); + + QCOMPARE(qgeocodingmanager->locale(),*english); + + QVERIFY(qgeocodingmanager->locale() != *german); + + delete german; + delete english; +} + +void tst_QGeoCodingManager::name() +{ + QString name = "geocode.test.plugin"; + QCOMPARE(qgeocodingmanager->managerName(),name); +} + +void tst_QGeoCodingManager::version() +{ + int version=100; + QCOMPARE(qgeocodingmanager->managerVersion(),version); + +} + +void tst_QGeoCodingManager::search() +{ + QCOMPARE(signalerror->count(),0); + QCOMPARE(signalfinished->count(),0); + + QString search = "Berlin. Invaliendenstrasse"; + int limit = 10; + int offset = 2; + + QGeoCodeReply * reply = qgeocodingmanager->geocode(search, limit,offset); + + QCOMPARE(reply->errorString(),search); + QCOMPARE(signalfinished->count(),1); + QCOMPARE(signalerror->count(),0); + + delete reply; +} + +void tst_QGeoCodingManager::geocode() +{ + QCOMPARE(signalerror->count(),0); + QCOMPARE(signalfinished->count(),0); + + QGeoAddress *address = new QGeoAddress (); + QString city = "Berlin"; + address->setCity(city); + + QGeoCodeReply *reply = qgeocodingmanager->geocode(*address); + + QCOMPARE(reply->errorString(),city); + QCOMPARE(signalfinished->count(),1); + QCOMPARE(signalerror->count(),0); + + delete address; + delete reply; +} + +void tst_QGeoCodingManager::reverseGeocode() +{ + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 0); + + QGeoCoordinate *coordinate = new QGeoCoordinate(34.34, 56.65); + + QGeoCodeReply *reply = qgeocodingmanager->reverseGeocode(*coordinate); + + QCOMPARE(reply->errorString(), coordinate->toString()); + QCOMPARE(signalfinished->count(), 1); + QCOMPARE(signalerror->count(), 0); + + delete coordinate; + delete reply; + + +} + + +QTEST_GUILESS_MAIN(tst_QGeoCodingManager) + diff --git a/tests/auto/qgeocodingmanager/tst_qgeocodingmanager.h b/tests/auto/qgeocodingmanager/tst_qgeocodingmanager.h new file mode 100644 index 0000000..97dba3e --- /dev/null +++ b/tests/auto/qgeocodingmanager/tst_qgeocodingmanager.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#ifndef TST_QGEOCODINGMANAGER_H +#define TST_QGEOCODINGMANAGER_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +QT_USE_NAMESPACE + +class tst_QGeoCodingManager: public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + void locale(); + void name(); + void version(); + void search(); + void geocode(); + void reverseGeocode(); + +private: + QGeoServiceProvider *qgeoserviceprovider; + QGeoCodingManager *qgeocodingmanager; + QSignalSpy *signalerror; + QSignalSpy *signalfinished; + void loadGeocodingManager(); + +}; +Q_DECLARE_METATYPE(QGeoCodeReply*); +Q_DECLARE_METATYPE(QGeoCodeReply::Error); + +#endif + diff --git a/tests/auto/qgeocodingmanagerplugins/geocoding_plugin.json b/tests/auto/qgeocodingmanagerplugins/geocoding_plugin.json new file mode 100644 index 0000000..9ac9353 --- /dev/null +++ b/tests/auto/qgeocodingmanagerplugins/geocoding_plugin.json @@ -0,0 +1,10 @@ +{ + "Keys": ["geocode.test.plugin"], + "Provider": "geocode.test.plugin", + "Version": 100, + "Experimental": true, + "Features": [ + "OfflineGeocodingFeature", + "ReverseGeocodingFeature" + ] +} diff --git a/tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerengine_test.h b/tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerengine_test.h new file mode 100644 index 0000000..6d88764 --- /dev/null +++ b/tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerengine_test.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOCODINGMANAGERENGINE_TEST_H +#define QGEOCODINGMANAGERENGINE_TEST_H + +#include +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class GeocodeReplyTest : public QGeoCodeReply +{ + Q_OBJECT +public: + GeocodeReplyTest(QObject *parent = 0) : QGeoCodeReply(parent) {} + + void callAddLocation ( const QGeoLocation & location ) {addLocation(location);} + void callSetError ( Error error, const QString & errorString ) {setError(error, errorString);} + void callSetFinished ( bool finished ) {setFinished(finished);} + void callSetLimit ( int limit ) {setLimit(limit);} + void callSetOffset ( int offset ) {setOffset(offset);} + void callSetLocations ( const QList & locations ) {setLocations(locations);} + void callSetViewport ( const QGeoShape &viewport ) {setViewport(viewport);} + +}; + +class QGeoCodingManagerEngineTest: public QGeoCodingManagerEngine + +{ +Q_OBJECT +public: + QGeoCodingManagerEngineTest(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) : + QGeoCodingManagerEngine(parameters) + { + Q_UNUSED(error) + Q_UNUSED(errorString) + setLocale(QLocale(QLocale::German, QLocale::Germany)); + } + + QGeoCodeReply* geocode(const QString &searchString, int limit, int offset, const QGeoShape &bounds) + { + GeocodeReplyTest *geocodereply = new GeocodeReplyTest(); + geocodereply->callSetLimit(limit); + geocodereply->callSetOffset(offset); + geocodereply->callSetViewport(bounds); + geocodereply->callSetError(QGeoCodeReply::NoError,searchString); + geocodereply->callSetFinished(true); + emit(this->finished(geocodereply)); + + return static_cast(geocodereply); + } + + QGeoCodeReply* geocode (const QGeoAddress &address, const QGeoShape &bounds) + { + GeocodeReplyTest *geocodereply = new GeocodeReplyTest(); + geocodereply->callSetViewport(bounds); + geocodereply->callSetError(QGeoCodeReply::NoError,address.city()); + geocodereply->callSetFinished(true); + emit(this->finished(geocodereply)); + + return static_cast(geocodereply); + } + + QGeoCodeReply* reverseGeocode(const QGeoCoordinate &coordinate, const QGeoShape &bounds) + { + GeocodeReplyTest *geocodereply = new GeocodeReplyTest(); + geocodereply->callSetViewport(bounds); + geocodereply->callSetError(QGeoCodeReply::NoError,coordinate.toString()); + geocodereply->callSetFinished(true); + emit(this->finished(geocodereply)); + return static_cast(geocodereply); + } +}; + +#endif diff --git a/tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerplugins.pro b/tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerplugins.pro new file mode 100644 index 0000000..650bccb --- /dev/null +++ b/tests/auto/qgeocodingmanagerplugins/qgeocodingmanagerplugins.pro @@ -0,0 +1,15 @@ +TARGET = qtgeoservices_geocodingplugin +QT += location + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = GeocodingTestGeoServicePlugin +PLUGIN_EXTENDS = - +load(qt_plugin) + +HEADERS += qgeocodingmanagerengine_test.h \ + qgeoserviceproviderplugin_test.h + +SOURCES += qgeoserviceproviderplugin_test.cpp + +OTHER_FILES += \ + geocoding_plugin.json diff --git a/tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.cpp b/tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.cpp new file mode 100644 index 0000000..c7729c2 --- /dev/null +++ b/tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderplugin_test.h" +#include "qgeocodingmanagerengine_test.h" + +#include + +QGeoServiceProviderFactoryTest::QGeoServiceProviderFactoryTest() +{ +} + +QGeoServiceProviderFactoryTest::~QGeoServiceProviderFactoryTest() +{ +} + +QGeoCodingManagerEngine* QGeoServiceProviderFactoryTest::createGeocodingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString) const +{ + return new QGeoCodingManagerEngineTest(parameters, error, errorString); +} diff --git a/tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.h b/tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.h new file mode 100644 index 0000000..53d2961 --- /dev/null +++ b/tests/auto/qgeocodingmanagerplugins/qgeoserviceproviderplugin_test.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_TEST_H +#define QGEOSERVICEPROVIDER_TEST_H + +#include +#include + +QT_USE_NAMESPACE + +class QGeoServiceProviderFactoryTest: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "geocoding_plugin.json") + +public: + QGeoServiceProviderFactoryTest(); + ~QGeoServiceProviderFactoryTest(); + + QGeoCodingManagerEngine* createGeocodingManagerEngine( + const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const; +}; + +#endif + + diff --git a/tests/auto/qgeocoordinate/qgeocoordinate.pro b/tests/auto/qgeocoordinate/qgeocoordinate.pro new file mode 100644 index 0000000..52795e1 --- /dev/null +++ b/tests/auto/qgeocoordinate/qgeocoordinate.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qgeocoordinate + +HEADERS += ../utils/qlocationtestutils_p.h +SOURCES += tst_qgeocoordinate.cpp \ + ../utils/qlocationtestutils.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeocoordinate/tst_qgeocoordinate.cpp b/tests/auto/qgeocoordinate/tst_qgeocoordinate.cpp new file mode 100644 index 0000000..1b30f64 --- /dev/null +++ b/tests/auto/qgeocoordinate/tst_qgeocoordinate.cpp @@ -0,0 +1,952 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "../utils/qlocationtestutils_p.h" + +#include +#include + +#include +#include + +#include + +QT_USE_NAMESPACE + +Q_DECLARE_METATYPE(QGeoCoordinate::CoordinateFormat) +Q_DECLARE_METATYPE(QGeoCoordinate::CoordinateType) + +static const QGeoCoordinate BRISBANE(-27.46758, 153.027892); +static const QGeoCoordinate MELBOURNE(-37.814251, 144.963169); +static const QGeoCoordinate LONDON(51.500152, -0.126236); +static const QGeoCoordinate NEW_YORK(40.71453, -74.00713); +static const QGeoCoordinate NORTH_POLE(90, 0); +static const QGeoCoordinate SOUTH_POLE(-90, 0); + +static const QChar DEGREES_SYMB(0x00B0); + + +QByteArray tst_qgeocoordinate_debug; + +void tst_qgeocoordinate_messageHandler(QtMsgType type, const QMessageLogContext &, const QString &msg) +{ + switch (type) { + case QtDebugMsg : + tst_qgeocoordinate_debug = msg.toLocal8Bit(); + break; + default: + break; + } +} + + +class tst_QGeoCoordinate : public QObject +{ + Q_OBJECT + +private: + enum TestDataType { + Latitude, + Longitude, + Altitude + }; + +private slots: + void initTestcase() + { + qRegisterMetaType(); + QMetaType::registerEqualsComparator(); + } + + void constructor() + { + QGeoCoordinate c; + QVERIFY(!c.isValid()); + QCOMPARE(c, QGeoCoordinate()); + } + + void constructor_lat_long() + { + QFETCH(double, latitude); + QFETCH(double, longitude); + QFETCH(QGeoCoordinate::CoordinateType, type); + + QGeoCoordinate c(latitude, longitude); + QCOMPARE(c.type(), type); + if (type != QGeoCoordinate::InvalidCoordinate) { + QVERIFY(c.isValid()); + QCOMPARE(c.latitude(), latitude); + QCOMPARE(c.longitude(), longitude); + } else { + QVERIFY(!c.isValid()); + QVERIFY(c.latitude() != latitude); + QVERIFY(c.longitude() != longitude); + } + } + + void constructor_lat_long_data() + { + QTest::addColumn("latitude"); + QTest::addColumn("longitude"); + QTest::addColumn("type"); + + QTest::newRow("both zero") << 0.0 << 0.0 << QGeoCoordinate::Coordinate2D; + QTest::newRow("both negative") << -1.0 << -1.0 << QGeoCoordinate::Coordinate2D; + QTest::newRow("both positive") << 1.0 << 1.0 << QGeoCoordinate::Coordinate2D; + QTest::newRow("latitude negative") << -1.0 << 1.0 << QGeoCoordinate::Coordinate2D; + QTest::newRow("longitude negative") << 1.0 << -1.0 << QGeoCoordinate::Coordinate2D; + + QTest::newRow("both too high") << 90.1 << 180.1 << QGeoCoordinate::InvalidCoordinate; + QTest::newRow("latitude too high") << 90.1 << 0.1 << QGeoCoordinate::InvalidCoordinate; + QTest::newRow("longitude too high") << 0.1 << 180.1 << QGeoCoordinate::InvalidCoordinate; + + QTest::newRow("both too low") << -90.1 << -180.1 << QGeoCoordinate::InvalidCoordinate; + QTest::newRow("latitude too low") << -90.1 << 0.1 << QGeoCoordinate::InvalidCoordinate; + QTest::newRow("longitude too low") << 0.1 << -180.1 << QGeoCoordinate::InvalidCoordinate; + + QTest::newRow("both not too high") << 90.0 << 180.0 << QGeoCoordinate::Coordinate2D; + QTest::newRow("both not too low") << -90.0 << -180.0 << QGeoCoordinate::Coordinate2D; + + QTest::newRow("latitude too high and longitude too low") << 90.1 << -180.1 << QGeoCoordinate::InvalidCoordinate; + QTest::newRow("latitude too low and longitude too high") << -90.1 << 180.1 << QGeoCoordinate::InvalidCoordinate; + } + + void constructor_lat_long_alt() + { + QFETCH(double, latitude); + QFETCH(double, longitude); + QFETCH(double, altitude); + QFETCH(QGeoCoordinate::CoordinateType, type); + + QGeoCoordinate c(latitude, longitude, altitude); + QCOMPARE(c.type(), type); + if (type != QGeoCoordinate::InvalidCoordinate) { + QCOMPARE(c.latitude(), latitude); + QCOMPARE(c.longitude(), longitude); + QCOMPARE(c.altitude(), altitude); + } else { + QVERIFY(!c.isValid()); + QVERIFY(c.latitude() != latitude); + QVERIFY(c.longitude() != longitude); + QVERIFY(c.altitude() != altitude); + } + } + + void constructor_lat_long_alt_data() + { + QTest::addColumn("latitude"); + QTest::addColumn("longitude"); + QTest::addColumn("altitude"); + QTest::addColumn("type"); + + QTest::newRow("all zero") << 0.0 << 0.0 << 0.0 << QGeoCoordinate::Coordinate3D; + QTest::newRow("all negative") << -1.0 << -1.0 << -2.0 << QGeoCoordinate::Coordinate3D; + QTest::newRow("all positive") << 1.0 << 1.0 << 4.0 << QGeoCoordinate::Coordinate3D; + + QTest::newRow("latitude negative") << -1.0 << 1.0 << 1.0 << QGeoCoordinate::Coordinate3D; + QTest::newRow("longitude negative") << 1.0 << -1.0 << 1.0 << QGeoCoordinate::Coordinate3D; + QTest::newRow("altitude negative") << 1.0 << 1.0 << -1.0 << QGeoCoordinate::Coordinate3D; + + QTest::newRow("altitude not too high") << 1.0 << 1.0 << DBL_MAX << QGeoCoordinate::Coordinate3D; + QTest::newRow("altitude not too low") << 1.0 << 1.0 << DBL_MIN << QGeoCoordinate::Coordinate3D; + + QTest::newRow("all not too high") << 90.0 << 180.0 << DBL_MAX << QGeoCoordinate::Coordinate3D; + QTest::newRow("all not too low") << -90.0 << -180.0 << DBL_MIN << QGeoCoordinate::Coordinate3D; + + QTest::newRow("all too high") << 90.1 << 180.1 << DBL_MAX << QGeoCoordinate::InvalidCoordinate; + QTest::newRow("all too low") << -90.1 << -180.1 << DBL_MIN << QGeoCoordinate::InvalidCoordinate; + } + + void copy_constructor() + { + QFETCH(QGeoCoordinate, c); + + QGeoCoordinate copy(c); + QCOMPARE(copy.type(), c.type()); + if (c.type() != QGeoCoordinate::InvalidCoordinate) { + QCOMPARE(copy.latitude(), c.latitude()); + QCOMPARE(copy.longitude(), c.longitude()); + if (c.type() == QGeoCoordinate::Coordinate3D) + QCOMPARE(copy.altitude(), c.altitude()); + } + } + + void copy_constructor_data() + { + QTest::addColumn("c"); + + QTest::newRow("no argument") << QGeoCoordinate(); + QTest::newRow("latitude, longitude arguments all zero") << QGeoCoordinate(0.0, 0.0); + + QTest::newRow("latitude, longitude arguments not too high") << QGeoCoordinate(90.0, 180.0); + QTest::newRow("latitude, longitude arguments not too low") << QGeoCoordinate(-90.0, -180.0); + QTest::newRow("latitude, longitude arguments too high") << QGeoCoordinate(90.1, 180.1); + QTest::newRow("latitude, longitude arguments too low") << QGeoCoordinate(-90.1, -180.1); + + QTest::newRow("latitude, longitude, altitude arguments all zero") << QGeoCoordinate(0.0, 0.0, 0.0); + QTest::newRow("latitude, longitude, altitude arguments not too high values") << QGeoCoordinate(90.0, 180.0, DBL_MAX); + QTest::newRow("latitude, longitude, altitude arguments not too low values") << QGeoCoordinate(-90.0, -180.0, DBL_MIN); + + QTest::newRow("latitude, longitude, altitude arguments too high latitude & longitude") << QGeoCoordinate(90.1, 180.1, DBL_MAX); + QTest::newRow("latitude, longitude, altitude arguments too low latitude & longitude") << QGeoCoordinate(-90.1, -180.1, DBL_MAX); + } + + void destructor() + { + QGeoCoordinate *coordinate; + + coordinate = new QGeoCoordinate(); + delete coordinate; + + coordinate = new QGeoCoordinate(0.0, 0.0); + delete coordinate; + + coordinate = new QGeoCoordinate(0.0, 0.0, 0.0); + delete coordinate; + + coordinate = new QGeoCoordinate(90.0, 180.0); + delete coordinate; + + coordinate = new QGeoCoordinate(-90.0, -180.0); + delete coordinate; + + coordinate = new QGeoCoordinate(90.1, 180.1); + delete coordinate; + + coordinate = new QGeoCoordinate(-90.1, -180.1); + delete coordinate; + } + + void destructor2() + { + QFETCH(QGeoCoordinate, c); + QGeoCoordinate *coordinate = new QGeoCoordinate(c); + delete coordinate; + } + + void destructor2_data() + { + copy_constructor_data(); + } + + void assign() + { + QFETCH(QGeoCoordinate, c); + + QGeoCoordinate c1 = c; + QCOMPARE(c.type(), c1.type()); + if (c.isValid()) { + QCOMPARE(c.latitude(), c.latitude()); + QCOMPARE(c.longitude(), c.longitude()); + if (c.type() == QGeoCoordinate::Coordinate3D) + QCOMPARE(c.altitude(), c.altitude()); + } + } + + void assign_data() + { + copy_constructor_data(); + } + + void comparison() + { + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(bool, result); + + QCOMPARE(c1 == c2, result); + QVariant v1 = QVariant::fromValue(c1); + QVariant v2 = QVariant::fromValue(c2); + QCOMPARE(v2 == v1, result); + } + + void comparison_data() + { + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("result"); + + QTest::newRow("Invalid != BRISBANE") + << QGeoCoordinate(-190,-1000) << BRISBANE << false; + QTest::newRow("BRISBANE != MELBOURNE") + << BRISBANE << MELBOURNE << false; + QTest::newRow("equal") + << BRISBANE << BRISBANE << true; + QTest::newRow("LONDON != uninitialized data") + << LONDON << QGeoCoordinate() << false; + QTest::newRow("uninitialized data == uninitialized data") + << QGeoCoordinate() << QGeoCoordinate() << true; + QTest::newRow("invalid == same invalid") + << QGeoCoordinate(-190,-1000) << QGeoCoordinate(-190,-1000) << true; + QTest::newRow("invalid == different invalid") + << QGeoCoordinate(-190,-1000) << QGeoCoordinate(190,1000) << true; + QTest::newRow("valid != another valid") + << QGeoCoordinate(-90,-180) << QGeoCoordinate(-45,+45) << false; + QTest::newRow("valid == same valid") + << QGeoCoordinate(-90,-180) << QGeoCoordinate(-90,-180) << true; + QTest::newRow("different longitudes at north pole are equal") + << QGeoCoordinate(90, 0) << QGeoCoordinate(90, 45) << true; + QTest::newRow("different longitudes at south pole are equal") + << QGeoCoordinate(-90, 0) << QGeoCoordinate(-90, 45) << true; + } + + void type() + { + QFETCH(QGeoCoordinate, c); + QFETCH(QGeoCoordinate::CoordinateType, type); + + QCOMPARE(c.type(), type); + } + + void type_data() + { + QTest::addColumn("c"); + QTest::addColumn("type"); + + QGeoCoordinate c; + + QTest::newRow("no values set") << c << QGeoCoordinate::InvalidCoordinate; + + c.setAltitude(1.0); + QTest::newRow("only altitude is set") << c << QGeoCoordinate::InvalidCoordinate; + + c.setLongitude(1.0); + QTest::newRow("only latitude and altitude is set") << c << QGeoCoordinate::InvalidCoordinate; + + c.setLatitude(-1.0); + QTest::newRow("all valid: 3D Coordinate") << c << QGeoCoordinate::Coordinate3D; + + c.setLatitude(-90.1); + QTest::newRow("too low latitude and valid longitude") << c << QGeoCoordinate::InvalidCoordinate; + + c.setLongitude(-180.1); + c.setLatitude(90.0); + QTest::newRow("valid latitude and too low longitude") << c << QGeoCoordinate::InvalidCoordinate; + + c.setLatitude(90.1); + c.setLongitude(-180.0); + QTest::newRow("too high latitude and valid longitude") << c << QGeoCoordinate::InvalidCoordinate; + + c.setLatitude(-90.0); + c.setLongitude(180.1); + QTest::newRow("valid latitude and too high longitude") << c << QGeoCoordinate::InvalidCoordinate; + } + + void valid() + { + QFETCH(QGeoCoordinate, c); + QCOMPARE(c.isValid(), c.type() != QGeoCoordinate::InvalidCoordinate); + } + + void valid_data() + { + type_data(); + } + + void addDataValues(TestDataType type) + { + QTest::addColumn("value"); + QTest::addColumn("valid"); + + QTest::newRow("negative") << -1.0 << true; + QTest::newRow("zero") << 0.0 << true; + QTest::newRow("positive") << 1.0 << true; + + switch (type) { + case Latitude: + QTest::newRow("too low") << -90.1 << false; + QTest::newRow("not too low") << -90.0 << true; + QTest::newRow("not too hight") << 90.0 << true; + QTest::newRow("too high") << 90.1; + break; + case Longitude: + QTest::newRow("too low") << -180.1 << false; + QTest::newRow("not too low") << -180.0 << true; + QTest::newRow("not too hight") << 180.0 << true; + QTest::newRow("too high") << 180.1; + break; + case Altitude: + break; + } + } + + void latitude() + { + QFETCH(double, value); + QGeoCoordinate c; + c.setLatitude(value); + QCOMPARE(c.latitude(), value); + + QGeoCoordinate c2 = c; + QCOMPARE(c.latitude(), value); + QCOMPARE(c2, c); + } + void latitude_data() { addDataValues(Latitude); } + + void longitude() + { + QFETCH(double, value); + QGeoCoordinate c; + c.setLongitude(value); + QCOMPARE(c.longitude(), value); + + QGeoCoordinate c2 = c; + QCOMPARE(c.longitude(), value); + QCOMPARE(c2, c); + } + void longitude_data() { addDataValues(Longitude); } + + void altitude() + { + QFETCH(double, value); + QGeoCoordinate c; + c.setAltitude(value); + QCOMPARE(c.altitude(), value); + + QGeoCoordinate c2 = c; + QCOMPARE(c.altitude(), value); + QCOMPARE(c2, c); + } + void altitude_data() { addDataValues(Altitude); } + + void distanceTo() + { + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(qreal, distance); + + QCOMPARE(QString::number(c1.distanceTo(c2)), QString::number(distance)); + } + + void distanceTo_data() + { + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("distance"); + + QTest::newRow("invalid coord 1") + << QGeoCoordinate() << BRISBANE << qreal(0.0); + QTest::newRow("invalid coord 2") + << BRISBANE << QGeoCoordinate() << qreal(0.0); + QTest::newRow("brisbane -> melbourne") + << BRISBANE << MELBOURNE << qreal(1374820.1618767744); + QTest::newRow("london -> new york") + << LONDON << NEW_YORK << qreal(5570538.4987236429); + QTest::newRow("north pole -> south pole") + << NORTH_POLE << SOUTH_POLE << qreal(20015109.4154876769); + } + + void azimuthTo() + { + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(qreal, azimuth); + + qreal result = c1.azimuthTo(c2); + QVERIFY(result >= 0.0); + QVERIFY(result < 360.0); + QCOMPARE(QString::number(result), QString::number(azimuth)); + } + + void azimuthTo_data() + { + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("azimuth"); + + QTest::newRow("invalid coord 1") + << QGeoCoordinate() << BRISBANE << qreal(0.0); + QTest::newRow("invalid coord 2") + << BRISBANE << QGeoCoordinate() << qreal(0.0); + QTest::newRow("brisbane -> melbourne") + << BRISBANE << MELBOURNE << qreal(211.1717286649); + QTest::newRow("london -> new york") + << LONDON << NEW_YORK << qreal(288.3388804508); + QTest::newRow("north pole -> south pole") + << NORTH_POLE << SOUTH_POLE << qreal(180.0); + QTest::newRow("Almost 360degrees bearing") + << QGeoCoordinate(0.5,45.0,0.0) << QGeoCoordinate(0.5,-134.9999651,0.0) << qreal(359.998); + } + + void atDistanceAndAzimuth() + { + QFETCH(QGeoCoordinate, origin); + QFETCH(qreal, distance); + QFETCH(qreal, azimuth); + QFETCH(QGeoCoordinate, result); + + QCOMPARE(result, origin.atDistanceAndAzimuth(distance, azimuth)); + } + + void atDistanceAndAzimuth_data() + { + QTest::addColumn("origin"); + QTest::addColumn("distance"); + QTest::addColumn("azimuth"); + QTest::addColumn("result"); + + QTest::newRow("invalid coord") + << QGeoCoordinate() + << qreal(1000.0) + << qreal(10.0) + << QGeoCoordinate(); + if (sizeof(qreal) == sizeof(double)) { + QTest::newRow("brisbane -> melbourne") + << BRISBANE + << qreal(1374820.1618767744) + << qreal(211.1717286649) + << MELBOURNE; + QTest::newRow("london -> new york") + << LONDON + << qreal(5570538.4987236429) + << qreal(288.3388804508) + << NEW_YORK; + QTest::newRow("north pole -> south pole") + << NORTH_POLE + << qreal(20015109.4154876769) + << qreal(180.0) + << SOUTH_POLE; + } else { + QTest::newRow("brisbane -> melbourne") + << BRISBANE + << qreal(1374820.1618767744) + << qreal(211.1717286649) + << QGeoCoordinate(-37.8142515084775, 144.963170622944); + QTest::newRow("london -> new york") + << LONDON + << qreal(5570538.4987236429) + << qreal(288.3388804508) + << QGeoCoordinate(40.7145220608416, -74.0071216045375); + QTest::newRow("north pole -> south pole") + << NORTH_POLE + << qreal(20015109.4154876769) + << qreal(180.0) + << QGeoCoordinate(-89.9999947369857, -90.0); + } + } + + void degreesToString() + { + QFETCH(QGeoCoordinate, coord); + QFETCH(QGeoCoordinate::CoordinateFormat, format); + QFETCH(QString, string); + + QCOMPARE(coord.toString(format), string); + } + + void degreesToString_data() + { + QTest::addColumn("coord"); + QTest::addColumn("format"); + QTest::addColumn("string"); + + QGeoCoordinate northEast(27.46758, 153.027892); + QGeoCoordinate northEastWithAlt(27.46758, 153.027892, 28.23411); + QGeoCoordinate southEast(-27.46758, 153.027892); + QGeoCoordinate southEastWithAlt(-27.46758, 153.027892, 28.23411); + QGeoCoordinate northWest(27.46758, -153.027892); + QGeoCoordinate northWestWithAlt(27.46758, -153.027892, 28.23411); + QGeoCoordinate southWest(-27.46758, -153.027892); + QGeoCoordinate southWestWithAlt(-27.46758, -153.027892, 28.23411); + + QGeoCoordinate empty; + QGeoCoordinate toohigh(90.1, 180.1); + QGeoCoordinate toolow(-90.1, -180.1); + QGeoCoordinate zeroLatLong(0.0, 0.0); + QGeoCoordinate allZero(0.0, 0.0, 0.0); + + QTest::newRow("empty, dd, no hemisphere") + << empty << QGeoCoordinate::Degrees + << QString(); + QTest::newRow("empty, dd, hemisphere") + << empty << QGeoCoordinate::DegreesWithHemisphere + << QString(); + QTest::newRow("empty, dm, no hemisphere") + << empty << QGeoCoordinate::DegreesMinutes + << QString(); + QTest::newRow("empty, dm, hemisphere") + << empty << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString(); + QTest::newRow("empty, dms, no hemisphere") + << empty << QGeoCoordinate::DegreesMinutesSeconds + << QString(); + QTest::newRow("empty, dms, hemisphere") + << empty << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString(); + + QTest::newRow("too low, dd, no hemisphere") + << toolow << QGeoCoordinate::Degrees + << QString(); + QTest::newRow("too low, dd, hemisphere") + << toolow << QGeoCoordinate::DegreesWithHemisphere + << QString(); + QTest::newRow("too low, dm, no hemisphere") + << toolow << QGeoCoordinate::DegreesMinutes + << QString(); + QTest::newRow("too low, dm, hemisphere") + << toolow << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString(); + QTest::newRow("too low, dms, no hemisphere") + << toolow << QGeoCoordinate::DegreesMinutesSeconds + << QString(); + QTest::newRow("too low, dms, hemisphere") + << toolow << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString(); + + QTest::newRow("too high, dd, no hemisphere") + << toohigh << QGeoCoordinate::Degrees + << QString(); + QTest::newRow("too high, dd, hemisphere") + << toohigh << QGeoCoordinate::DegreesWithHemisphere + << QString(); + QTest::newRow("too high, dm, no hemisphere") + << toohigh << QGeoCoordinate::DegreesMinutes + << QString(); + QTest::newRow("too high, dm, hemisphere") + << toohigh << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString(); + QTest::newRow("too high, dms, no hemisphere") + << toohigh << QGeoCoordinate::DegreesMinutesSeconds + << QString(); + QTest::newRow("too high, dms, hemisphere") + << toohigh << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString(); + + QTest::newRow("zeroLatLong, dd, no hemisphere") + << zeroLatLong << QGeoCoordinate::Degrees + << QString("0.00000%1, 0.00000%1").arg(DEGREES_SYMB); + QTest::newRow("zeroLatLong, dd, hemisphere") + << zeroLatLong << QGeoCoordinate::DegreesWithHemisphere + << QString("0.00000%1, 0.00000%1").arg(DEGREES_SYMB); + QTest::newRow("zeroLatLong, dm, no hemisphere") + << zeroLatLong << QGeoCoordinate::DegreesMinutes + << QString("0%1 0.000', 0%1 0.000'").arg(DEGREES_SYMB); + QTest::newRow("zeroLatLong, dm, hemisphere") + << zeroLatLong << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("0%1 0.000', 0%1 0.000'").arg(DEGREES_SYMB); + QTest::newRow("zeroLatLong, dms, no hemisphere") + << zeroLatLong << QGeoCoordinate::DegreesMinutesSeconds + << QString("0%1 0' 0.0\", 0%1 0' 0.0\"").arg(DEGREES_SYMB); + QTest::newRow("zeroLatLong, dms, hemisphere") + << zeroLatLong << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("0%1 0' 0.0\", 0%1 0' 0.0\"").arg(DEGREES_SYMB); + + QTest::newRow("allZero, dd, no hemisphere") + << allZero << QGeoCoordinate::Degrees + << QString("0.00000%1, 0.00000%1, 0m").arg(DEGREES_SYMB); + QTest::newRow("allZero, dd, hemisphere") + << allZero << QGeoCoordinate::DegreesWithHemisphere + << QString("0.00000%1, 0.00000%1, 0m").arg(DEGREES_SYMB); + QTest::newRow("allZero, dm, no hemisphere") + << allZero << QGeoCoordinate::DegreesMinutes + << QString("0%1 0.000', 0%1 0.000', 0m").arg(DEGREES_SYMB); + QTest::newRow("allZero, dm, hemisphere") + << allZero << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("0%1 0.000', 0%1 0.000', 0m").arg(DEGREES_SYMB); + QTest::newRow("allZero, dms, no hemisphere") + << allZero << QGeoCoordinate::DegreesMinutesSeconds + << QString("0%1 0' 0.0\", 0%1 0' 0.0\", 0m").arg(DEGREES_SYMB); + QTest::newRow("allZero, dms, hemisphere") + << allZero << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("0%1 0' 0.0\", 0%1 0' 0.0\", 0m").arg(DEGREES_SYMB); + + QTest::newRow("NE, dd, no hemisphere") + << northEast << QGeoCoordinate::Degrees + << QString("27.46758%1, 153.02789%1").arg(DEGREES_SYMB); + QTest::newRow("NE, dd, hemisphere") + << northEast << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 N, 153.02789%1 E").arg(DEGREES_SYMB); + QTest::newRow("NE, dm, no hemisphere") + << northEast << QGeoCoordinate::DegreesMinutes + << QString("27%1 28.055', 153%1 1.674'").arg(DEGREES_SYMB); + QTest::newRow("NE, dm, hemisphere") + << northEast << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' N, 153%1 1.674' E").arg(DEGREES_SYMB); + QTest::newRow("NE, dms, no hemisphere") + << northEast << QGeoCoordinate::DegreesMinutesSeconds + << QString("27%1 28' 3.3\", 153%1 1' 40.4\"").arg(DEGREES_SYMB); + QTest::newRow("NE, dms, hemisphere") + << northEast << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" N, 153%1 1' 40.4\" E").arg(DEGREES_SYMB); + + QTest::newRow("NE with alt, dd, no hemisphere") + << northEastWithAlt << QGeoCoordinate::Degrees + << QString("27.46758%1, 153.02789%1, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NE with alt, dd, hemisphere") + << northEastWithAlt << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 N, 153.02789%1 E, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NE with alt, dm, no hemisphere") + << northEastWithAlt << QGeoCoordinate::DegreesMinutes + << QString("27%1 28.055', 153%1 1.674', 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NE with alt, dm, hemisphere") + << northEastWithAlt << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' N, 153%1 1.674' E, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NE with alt, dms, no hemisphere") + << northEastWithAlt << QGeoCoordinate::DegreesMinutesSeconds + << QString("27%1 28' 3.3\", 153%1 1' 40.4\", 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NE with alt, dms, hemisphere") + << northEastWithAlt << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" N, 153%1 1' 40.4\" E, 28.2341m").arg(DEGREES_SYMB); + + QTest::newRow("SE, dd, no hemisphere") + << southEast << QGeoCoordinate::Degrees + << QString("-27.46758%1, 153.02789%1").arg(DEGREES_SYMB); + QTest::newRow("SE, dd, hemisphere") + << southEast << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 S, 153.02789%1 E").arg(DEGREES_SYMB); + QTest::newRow("SE, dm, no hemisphere") + << southEast << QGeoCoordinate::DegreesMinutes + << QString("-27%1 28.055', 153%1 1.674'").arg(DEGREES_SYMB); + QTest::newRow("SE, dm, hemisphere") + << southEast << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' S, 153%1 1.674' E").arg(DEGREES_SYMB); + QTest::newRow("SE, dms, no hemisphere") + << southEast << QGeoCoordinate::DegreesMinutesSeconds + << QString("-27%1 28' 3.3\", 153%1 1' 40.4\"").arg(DEGREES_SYMB); + QTest::newRow("SE, dms, hemisphere") + << southEast << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" S, 153%1 1' 40.4\" E").arg(DEGREES_SYMB); + + QTest::newRow("SE with alt, dd, no hemisphere, 28.2341m") + << southEastWithAlt << QGeoCoordinate::Degrees + << QString("-27.46758%1, 153.02789%1, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SE with alt, dd, hemisphere, 28.2341m") + << southEastWithAlt << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 S, 153.02789%1 E, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SE with alt, dm, no hemisphere, 28.2341m") + << southEastWithAlt << QGeoCoordinate::DegreesMinutes + << QString("-27%1 28.055', 153%1 1.674', 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SE with alt, dm, hemisphere, 28.2341m") + << southEastWithAlt << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' S, 153%1 1.674' E, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SE with alt, dms, no hemisphere, 28.2341m") + << southEastWithAlt << QGeoCoordinate::DegreesMinutesSeconds + << QString("-27%1 28' 3.3\", 153%1 1' 40.4\", 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SE with alt, dms, hemisphere, 28.2341m") + << southEastWithAlt << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" S, 153%1 1' 40.4\" E, 28.2341m").arg(DEGREES_SYMB);; + + QTest::newRow("NW, dd, no hemisphere") + << northWest << QGeoCoordinate::Degrees + << QString("27.46758%1, -153.02789%1").arg(DEGREES_SYMB); + QTest::newRow("NW, dd, hemisphere") + << northWest << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 N, 153.02789%1 W").arg(DEGREES_SYMB); + QTest::newRow("NW, dm, no hemisphere") + << northWest << QGeoCoordinate::DegreesMinutes + << QString("27%1 28.055', -153%1 1.674'").arg(DEGREES_SYMB); + QTest::newRow("NW, dm, hemisphere") + << northWest << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' N, 153%1 1.674' W").arg(DEGREES_SYMB); + QTest::newRow("NW, dms, no hemisphere") + << northWest << QGeoCoordinate::DegreesMinutesSeconds + << QString("27%1 28' 3.3\", -153%1 1' 40.4\"").arg(DEGREES_SYMB); + QTest::newRow("NW, dms, hemisphere") + << northWest << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" N, 153%1 1' 40.4\" W").arg(DEGREES_SYMB); + + QTest::newRow("NW with alt, dd, no hemisphere, 28.2341m") + << northWestWithAlt << QGeoCoordinate::Degrees + << QString("27.46758%1, -153.02789%1, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NW with alt, dd, hemisphere, 28.2341m") + << northWestWithAlt << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 N, 153.02789%1 W, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NW with alt, dm, no hemisphere, 28.2341m") + << northWestWithAlt << QGeoCoordinate::DegreesMinutes + << QString("27%1 28.055', -153%1 1.674', 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NW with alt, dm, hemisphere, 28.2341m") + << northWestWithAlt << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' N, 153%1 1.674' W, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NW with alt, dms, no hemisphere, 28.2341m") + << northWestWithAlt << QGeoCoordinate::DegreesMinutesSeconds + << QString("27%1 28' 3.3\", -153%1 1' 40.4\", 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("NW with alt, dms, hemisphere, 28.2341m") + << northWestWithAlt << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" N, 153%1 1' 40.4\" W, 28.2341m").arg(DEGREES_SYMB); + + QTest::newRow("SW, dd, no hemisphere") + << southWest << QGeoCoordinate::Degrees + << QString("-27.46758%1, -153.02789%1").arg(DEGREES_SYMB); + QTest::newRow("SW, dd, hemisphere") + << southWest << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 S, 153.02789%1 W").arg(DEGREES_SYMB); + QTest::newRow("SW, dm, no hemisphere") + << southWest << QGeoCoordinate::DegreesMinutes + << QString("-27%1 28.055', -153%1 1.674'").arg(DEGREES_SYMB); + QTest::newRow("SW, dm, hemisphere") + << southWest << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' S, 153%1 1.674' W").arg(DEGREES_SYMB); + QTest::newRow("SW, dms, no hemisphere") + << southWest << QGeoCoordinate::DegreesMinutesSeconds + << QString("-27%1 28' 3.3\", -153%1 1' 40.4\"").arg(DEGREES_SYMB); + QTest::newRow("SW, dms, hemisphere") + << southWest << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" S, 153%1 1' 40.4\" W").arg(DEGREES_SYMB); + + QTest::newRow("SW with alt, dd, no hemisphere, 28.2341m") + << southWestWithAlt << QGeoCoordinate::Degrees + << QString("-27.46758%1, -153.02789%1, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SW with alt, dd, hemisphere, 28.2341m") + << southWestWithAlt << QGeoCoordinate::DegreesWithHemisphere + << QString("27.46758%1 S, 153.02789%1 W, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SW with alt, dm, no hemisphere, 28.2341m") + << southWestWithAlt << QGeoCoordinate::DegreesMinutes + << QString("-27%1 28.055', -153%1 1.674', 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SW with alt, dm, hemisphere, 28.2341m") + << southWestWithAlt << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString("27%1 28.055' S, 153%1 1.674' W, 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SW with alt, dms, no hemisphere, 28.2341m") + << southWestWithAlt << QGeoCoordinate::DegreesMinutesSeconds + << QString("-27%1 28' 3.3\", -153%1 1' 40.4\", 28.2341m").arg(DEGREES_SYMB); + QTest::newRow("SW with alt, dms, hemisphere, 28.2341m") + << southWestWithAlt << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString("27%1 28' 3.3\" S, 153%1 1' 40.4\" W, 28.2341m").arg(DEGREES_SYMB); + + QTest::newRow("Wrap seconds to Minutes DMSH") + << QGeoCoordinate(1.1333333, 1.1333333) << QGeoCoordinate::DegreesMinutesSecondsWithHemisphere + << QString( "1%1 8' 0.0\" N, 1%1 8' 0.0\" E").arg(DEGREES_SYMB); + QTest::newRow("Wrap seconds to Minutes DMS") + << QGeoCoordinate(1.1333333, 1.1333333) << QGeoCoordinate::DegreesMinutesSeconds + << QString( "1%1 8' 0.0\", 1%1 8' 0.0\"").arg(DEGREES_SYMB); + QTest::newRow("Wrap minutes to Degrees DMH") + << QGeoCoordinate(1.999999, 1.999999) << QGeoCoordinate::DegreesMinutesWithHemisphere + << QString( "2%1 0.000' N, 2%1 0.000' E").arg(DEGREES_SYMB); + QTest::newRow("Wrap minutes to Degrees DM") + << QGeoCoordinate(1.999999, 1.999999) << QGeoCoordinate::DegreesMinutes + << QString( "2%1 0.000', 2%1 0.000'").arg(DEGREES_SYMB); + + QTest::newRow("Wrap seconds to minutes to Degrees DM -> above valid long/lat values") + << QGeoCoordinate(89.9999, 179.9999) << QGeoCoordinate::DegreesMinutesSeconds + << QString( "90%1 0' 0.0\", 180%1 0' 0.0\"").arg(DEGREES_SYMB); + + QTest::newRow("Wrap minutes to Degrees DM ->above valid long/lat values") + << QGeoCoordinate(89.9999, 179.9999) << QGeoCoordinate::DegreesMinutes + << QString( "90%1 0.000', 180%1 0.000'").arg(DEGREES_SYMB); + + } + + void datastream() + { + QFETCH(QGeoCoordinate, c); + + QByteArray ba; + QDataStream out(&ba, QIODevice::WriteOnly); + out << c; + + QDataStream in(&ba, QIODevice::ReadOnly); + QGeoCoordinate inCoord; + in >> inCoord; + QCOMPARE(inCoord, c); + } + + void datastream_data() + { + QTest::addColumn("c"); + + QTest::newRow("invalid") << QGeoCoordinate(); + QTest::newRow("valid lat, long") << BRISBANE; + QTest::newRow("valid lat, long, alt") << QGeoCoordinate(-1, -1, -1); + QTest::newRow("valid lat, long, alt again") << QGeoCoordinate(1, 1, 1); + } + + void debug() + { + QFETCH(QGeoCoordinate, c); + QFETCH(int, nextValue); + QFETCH(QByteArray, debugString); + + qInstallMessageHandler(tst_qgeocoordinate_messageHandler); + qDebug() << c << nextValue; + qInstallMessageHandler(0); + QCOMPARE(tst_qgeocoordinate_debug, debugString); + } + + void debug_data() + { + QTest::addColumn("c"); + QTest::addColumn("nextValue"); + QTest::addColumn("debugString"); + + QTest::newRow("uninitialized") << QGeoCoordinate() << 45 + << QByteArray("QGeoCoordinate(?, ?) 45"); + QTest::newRow("initialized without altitude") << BRISBANE << 45 + << (QString("QGeoCoordinate(%1, %2) 45").arg(BRISBANE.latitude(), 0, 'g', 9) + .arg(BRISBANE.longitude(), 0, 'g', 9)).toLatin1(); + QTest::newRow("invalid initialization") << QGeoCoordinate(-100,-200) << 45 + << QByteArray("QGeoCoordinate(?, ?) 45"); + QTest::newRow("initialized with altitude") << QGeoCoordinate(1,2,3) << 45 + << QByteArray("QGeoCoordinate(1, 2, 3) 45"); + QTest::newRow("extra long coordinates") << QGeoCoordinate(89.123412341, 179.123412341) + << 45 << QByteArray("QGeoCoordinate(89.123412341, 179.12341234) 45"); + } + + void hash() + { + uint s1 = qHash(QGeoCoordinate(1, 1, 2)); + uint s2 = qHash(QGeoCoordinate(2, 1, 1)); + uint s3 = qHash(QGeoCoordinate(1, 2, 1)); + uint s10 = qHash(QGeoCoordinate(0, 0, 2)); + uint s20 = qHash(QGeoCoordinate(2, 0, 0)); + uint s30 = qHash(QGeoCoordinate(0, 2, 0)); + uint s30NoAlt = qHash(QGeoCoordinate(0, 2)); + uint s30WithSeed = qHash(QGeoCoordinate(0, 2, 0), 1); + uint nullCoordinate = qHash(QGeoCoordinate()); + + uint north1 = qHash(QGeoCoordinate(90.0, 34.7, 0)); + uint north2 = qHash(QGeoCoordinate(90.0, 180, 0)); + + uint south1 = qHash(QGeoCoordinate(90.0, 67.7, 34.0)); + uint south2 = qHash(QGeoCoordinate(90.0, 111, 34.0)); + + QVERIFY(s1 != s2); + QVERIFY(s2 != s3); + QVERIFY(s1 != s3); + QVERIFY(s10 != s20); + QVERIFY(s20 != s30); + QVERIFY(s10 != s30); + QVERIFY(s30NoAlt != s30); + QVERIFY(s30WithSeed != s30); + + QVERIFY(nullCoordinate != s1); + QVERIFY(nullCoordinate != s2); + QVERIFY(nullCoordinate != s3); + QVERIFY(nullCoordinate != s10); + QVERIFY(nullCoordinate != s20); + QVERIFY(nullCoordinate != s30); + QVERIFY(nullCoordinate != s30NoAlt); + QVERIFY(nullCoordinate != s30WithSeed); + + QVERIFY(north1 == north2); + QVERIFY(south1 == south2); + } +}; + +QTEST_GUILESS_MAIN(tst_QGeoCoordinate) +#include "tst_qgeocoordinate.moc" diff --git a/tests/auto/qgeolocation/qgeolocation.pro b/tests/auto/qgeolocation/qgeolocation.pro new file mode 100644 index 0000000..3b5b3a3 --- /dev/null +++ b/tests/auto/qgeolocation/qgeolocation.pro @@ -0,0 +1,9 @@ +CONFIG += testcase +TARGET = tst_qgeolocation + +HEADERS += ../utils/qlocationtestutils_p.h \ + tst_qgeolocation.h +SOURCES += tst_qgeolocation.cpp \ + ../utils/qlocationtestutils.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeolocation/tst_qgeolocation.cpp b/tests/auto/qgeolocation/tst_qgeolocation.cpp new file mode 100644 index 0000000..9e40b0b --- /dev/null +++ b/tests/auto/qgeolocation/tst_qgeolocation.cpp @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_qgeolocation.h" + +QT_USE_NAMESPACE + +tst_QGeoLocation::tst_QGeoLocation() +{ +} + +void tst_QGeoLocation::initTestCase() +{ + +} + +void tst_QGeoLocation::cleanupTestCase() +{ + +} + +void tst_QGeoLocation::init() +{ +} + +void tst_QGeoLocation::cleanup() +{ +} + +void tst_QGeoLocation::constructor() +{ + QCOMPARE(m_location.address(), m_address); + QCOMPARE(m_location.coordinate(), m_coordinate); + QCOMPARE(m_location.boundingBox(), m_viewport); +} + +void tst_QGeoLocation::copy_constructor() +{ + QGeoLocation *qgeolocationcopy = new QGeoLocation (m_location); + QCOMPARE(m_location, *qgeolocationcopy); + delete qgeolocationcopy; +} + +void tst_QGeoLocation::destructor() +{ + QGeoLocation *qgeolocationcopy; + + qgeolocationcopy = new QGeoLocation(); + delete qgeolocationcopy; + + qgeolocationcopy = new QGeoLocation(m_location); + delete qgeolocationcopy; +} + +void tst_QGeoLocation::address() +{ + m_address.setCity("Berlin"); + m_address.setCountry("Germany"); + m_address.setCountryCode("DEU"); + m_address.setDistrict("Mitte"); + m_address.setPostalCode("10115"); + m_address.setStreet("Invalidenstrasse"); + + m_location.setAddress(m_address); + + QCOMPARE(m_location.address(),m_address); + + m_address.setPostalCode("10125"); + QVERIFY(m_location.address() != m_address); +} + +void tst_QGeoLocation::coordinate() +{ + m_coordinate.setLatitude(13.3851); + m_coordinate.setLongitude(52.5312); + m_coordinate.setAltitude(134.23); + + m_location.setCoordinate(m_coordinate); + + QCOMPARE(m_location.coordinate(), m_coordinate); + + m_coordinate.setAltitude(0); + QVERIFY(m_location.coordinate() != m_coordinate); +} + +void tst_QGeoLocation::viewport() +{ + m_coordinate.setLatitude(13.3851); + m_coordinate.setLongitude(52.5312); + + QGeoRectangle qgeoboundingboxcopy(m_coordinate, 0.4, 0.4); + m_location.setBoundingBox(qgeoboundingboxcopy); + + QCOMPARE(m_location.boundingBox(),qgeoboundingboxcopy); + + qgeoboundingboxcopy.setHeight(1); + + QVERIFY(m_location.boundingBox() != qgeoboundingboxcopy); +} + +void tst_QGeoLocation::operators() +{ + QGeoAddress qgeoaddresscopy; + qgeoaddresscopy.setCity("Berlin"); + qgeoaddresscopy.setCountry("Germany"); + qgeoaddresscopy.setCountryCode("DEU"); + + QGeoCoordinate qgeocoordinatecopy (32.324 , 41.324 , 24.55); + + m_address.setCity("Madrid"); + m_address.setCountry("Spain"); + m_address.setCountryCode("SPA"); + + m_coordinate.setLatitude(21.3434); + m_coordinate.setLongitude(38.43443); + m_coordinate.setAltitude(634.21); + + m_location.setAddress(m_address); + m_location.setCoordinate(m_coordinate); + + //Create a copy and see that they are the same + QGeoLocation qgeolocationcopy(m_location); + QVERIFY(m_location == qgeolocationcopy); + QVERIFY(!(m_location != qgeolocationcopy)); + + //Modify one and test if they are different + qgeolocationcopy.setAddress(qgeoaddresscopy); + QVERIFY(!(m_location == qgeolocationcopy)); + QVERIFY(m_location != qgeolocationcopy); + qgeolocationcopy.setCoordinate(qgeocoordinatecopy); + QVERIFY(!(m_location == qgeolocationcopy)); + QVERIFY(m_location != qgeolocationcopy); + + //delete qgeolocationcopy; + //Asign and test that they are the same + qgeolocationcopy = m_location; + QVERIFY(m_location ==qgeolocationcopy); + QVERIFY(!(m_location != qgeolocationcopy)); +} + +void tst_QGeoLocation::comparison() +{ + QFETCH(QString, dataField); + + QGeoLocation location; + + //set address + QGeoAddress address; + address.setStreet("21 jump st"); + address.setCountry("USA"); + location.setAddress(address); + + //set coordinate + location.setCoordinate(QGeoCoordinate(5,10)); + + //set viewport + location.setBoundingBox(QGeoRectangle(QGeoCoordinate(5,5),0.4,0.4)); + + QGeoLocation otherLocation(location); + + if (dataField == "no change") { + QCOMPARE(location, otherLocation); + } else { + if (dataField == "address") { + QGeoAddress otherAddress; + otherAddress.setStreet("42 evergreen tce"); + otherAddress.setCountry("USA"); + otherLocation.setAddress(otherAddress); + } else if (dataField == "coordinate") { + otherLocation.setCoordinate(QGeoCoordinate(12,13)); + } else if (dataField == "viewport"){ + otherLocation.setBoundingBox(QGeoRectangle(QGeoCoordinate(1,2), 0.5,0.5)); + } else { + qFatal("Unknown data field to test"); + } + + QVERIFY(location != otherLocation); + } +} + +void tst_QGeoLocation::comparison_data() +{ + QTest::addColumn ("dataField"); + QTest::newRow("no change") << "no change"; + QTest::newRow("address") << "address"; + QTest::newRow("coordinate") << "coordinate"; +} + +void tst_QGeoLocation::isEmpty() +{ + QGeoAddress address; + address.setCity(QStringLiteral("Braunschweig")); + QVERIFY(!address.isEmpty()); + + QGeoRectangle boundingBox; + boundingBox.setTopLeft(QGeoCoordinate(1, -1)); + boundingBox.setBottomRight(QGeoCoordinate(-1, 1)); + QVERIFY(!boundingBox.isEmpty()); + + QGeoLocation location; + + QVERIFY(location.isEmpty()); + + // address + location.setAddress(address); + QVERIFY(!location.isEmpty()); + location.setAddress(QGeoAddress()); + QVERIFY(location.isEmpty()); + + // coordinate + location.setCoordinate(QGeoCoordinate(1, 2)); + QVERIFY(!location.isEmpty()); + location.setCoordinate(QGeoCoordinate()); + QVERIFY(location.isEmpty()); + + // bounding box + location.setBoundingBox(boundingBox); + QVERIFY(!location.isEmpty()); + location.setBoundingBox(QGeoRectangle()); + QVERIFY(location.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QGeoLocation); + diff --git a/tests/auto/qgeolocation/tst_qgeolocation.h b/tests/auto/qgeolocation/tst_qgeolocation.h new file mode 100644 index 0000000..182cad2 --- /dev/null +++ b/tests/auto/qgeolocation/tst_qgeolocation.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_QGEOLOCATION_H +#define TST_QGEOLOCATION_H + +#include +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoLocation : public QObject +{ + Q_OBJECT + +public: + tst_QGeoLocation(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + //Start Unit Tests for qgeolocation.h + void constructor(); + void copy_constructor(); + void destructor(); + void address(); + void coordinate(); + void viewport(); + void operators(); + void comparison(); + void comparison_data(); + void isEmpty(); + //End Unit Tests for qgeolocation.h + +private: + QGeoLocation m_location; + + QGeoAddress m_address; + QGeoCoordinate m_coordinate; + QGeoRectangle m_viewport; +}; + +Q_DECLARE_METATYPE( QGeoCoordinate::CoordinateFormat); +Q_DECLARE_METATYPE( QGeoCoordinate::CoordinateType); +Q_DECLARE_METATYPE( QList); + +#endif + diff --git a/tests/auto/qgeomaneuver/qgeomaneuver.pro b/tests/auto/qgeomaneuver/qgeomaneuver.pro new file mode 100644 index 0000000..fac57c9 --- /dev/null +++ b/tests/auto/qgeomaneuver/qgeomaneuver.pro @@ -0,0 +1,11 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeomaneuver + +# Input +HEADERS += ../utils/qlocationtestutils_p.h \ + tst_qgeomaneuver.h +SOURCES += tst_qgeomaneuver.cpp \ + ../utils/qlocationtestutils.cpp + +QT += location-private testlib diff --git a/tests/auto/qgeomaneuver/tst_qgeomaneuver.cpp b/tests/auto/qgeomaneuver/tst_qgeomaneuver.cpp new file mode 100644 index 0000000..c35b57f --- /dev/null +++ b/tests/auto/qgeomaneuver/tst_qgeomaneuver.cpp @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_qgeomaneuver.h" +#include + +class QGeoManeuverPrivateDefaultAlt : public QGeoManeuverPrivateDefault +{ +public: + QGeoManeuverPrivateDefaultAlt() {} + QGeoManeuverPrivateDefaultAlt(const QGeoManeuverPrivateDefaultAlt &other) + : QGeoManeuverPrivateDefault(other) {} + ~QGeoManeuverPrivateDefaultAlt() {} + + QString text() const override + { + return QStringLiteral("QGeoManeuverPrivateDefaultAlt"); // To identify this is actually a QGeoManeuverPrivateDefaultAlt + } +}; + +class QGeoManeuverAlt : public QGeoManeuver +{ +public: + QGeoManeuverAlt() + : QGeoManeuver(QSharedDataPointer(new QGeoManeuverPrivateDefaultAlt())) + { + } +}; + +tst_QGeoManeuver::tst_QGeoManeuver() +{ +} + +void tst_QGeoManeuver::initTestCase() +{ + +} + +void tst_QGeoManeuver::cleanupTestCase() +{ + +} + +void tst_QGeoManeuver::init() +{ + + qgeomaneuver = new QGeoManeuver(); +} + +void tst_QGeoManeuver::cleanup() +{ + delete qgeomaneuver; +} + +void tst_QGeoManeuver::constructor() +{ + QString empty =""; + + QVERIFY(!qgeomaneuver->isValid()); + QCOMPARE(qgeomaneuver->direction(),QGeoManeuver::NoDirection); + QCOMPARE(qgeomaneuver->distanceToNextInstruction(), qreal(0.0)); + QCOMPARE(qgeomaneuver->instructionText(),empty); + QCOMPARE(qgeomaneuver->timeToNextInstruction(),0); +} + +void tst_QGeoManeuver::copy_constructor() +{ + QGeoManeuver *qgeomaneuvercopy = new QGeoManeuver (*qgeomaneuver); + + QCOMPARE(*qgeomaneuver,*qgeomaneuvercopy); + + delete qgeomaneuvercopy; +} + +void tst_QGeoManeuver::destructor() +{ + QGeoManeuver *qgeomaneuvercopy; + + qgeomaneuvercopy = new QGeoManeuver(); + delete qgeomaneuvercopy; + + qgeomaneuvercopy = new QGeoManeuver(*qgeomaneuver); + delete qgeomaneuvercopy; +} + +void tst_QGeoManeuver::direction() +{ + QFETCH(QGeoManeuver::InstructionDirection,direction); + + qgeomaneuver->setDirection(direction); + + QCOMPARE(qgeomaneuver->direction(),direction); +} +void tst_QGeoManeuver::direction_data() +{ + QTest::addColumn("direction"); + + QTest::newRow("instruction1") << QGeoManeuver::NoDirection; + QTest::newRow("instruction2") << QGeoManeuver::DirectionForward; + QTest::newRow("instruction3") << QGeoManeuver::DirectionBearRight; + QTest::newRow("instruction4") << QGeoManeuver::DirectionLightRight; + QTest::newRow("instruction5") << QGeoManeuver::DirectionRight; + QTest::newRow("instruction6") << QGeoManeuver::DirectionHardRight; + QTest::newRow("instruction7") << QGeoManeuver::DirectionUTurnRight; + QTest::newRow("instruction8") << QGeoManeuver::DirectionUTurnLeft; + QTest::newRow("instruction9") << QGeoManeuver::DirectionHardLeft; + QTest::newRow("instruction10") << QGeoManeuver::DirectionLeft; + QTest::newRow("instruction11") << QGeoManeuver::DirectionLightLeft; + QTest::newRow("instruction12") << QGeoManeuver::DirectionBearLeft; +} + +void tst_QGeoManeuver::distanceToNextInstruction() +{ + qreal distance = 0.0; + qgeomaneuver->setDistanceToNextInstruction(distance); + + QCOMPARE (qgeomaneuver->distanceToNextInstruction(), distance); + + distance = -3423.4324; + + QVERIFY (qgeomaneuver->distanceToNextInstruction() != distance); + + qgeomaneuver->setDistanceToNextInstruction(distance); + QCOMPARE (qgeomaneuver->distanceToNextInstruction(),distance); +} + +void tst_QGeoManeuver::instructionText() +{ + QString text = "After 50m turn left"; + + qgeomaneuver->setInstructionText(text); + + QCOMPARE (qgeomaneuver->instructionText(),text); + + text="After 40m, turn left"; + QVERIFY (qgeomaneuver->instructionText() != text); + +} + +void tst_QGeoManeuver::position() +{ + QFETCH(double, latitude); + QFETCH(double, longitude); + + qgeocoordinate = new QGeoCoordinate (latitude,longitude); + + qgeomaneuver->setPosition(*qgeocoordinate); + + QCOMPARE(qgeomaneuver->position(),*qgeocoordinate); + + delete qgeocoordinate; +} + +void tst_QGeoManeuver::position_data() +{ + QTest::addColumn("latitude"); + QTest::addColumn("longitude"); + + QTest::newRow("invalid0") << -12220.0 << 0.0; + QTest::newRow("invalid1") << 0.0 << 181.0; + + QTest::newRow("correct0") << 0.0 << 0.0; + QTest::newRow("correct1") << 90.0 << 0.0; + QTest::newRow("correct2") << 0.0 << 180.0; + QTest::newRow("correct3") << -90.0 << 0.0; + QTest::newRow("correct4") << 0.0 << -180.0; + QTest::newRow("correct5") << 45.0 << 90.0; +} + +void tst_QGeoManeuver::timeToNextInstruction() +{ + int time = 0; + qgeomaneuver->setTimeToNextInstruction(time); + + QCOMPARE (qgeomaneuver->timeToNextInstruction(),time); + + time = 35; + + QVERIFY (qgeomaneuver->timeToNextInstruction() != time); + + qgeomaneuver->setTimeToNextInstruction(time); + QCOMPARE (qgeomaneuver->timeToNextInstruction(),time); +} + +void tst_QGeoManeuver::waypoint() +{ + QFETCH(double, latitude); + QFETCH(double, longitude); + + qgeocoordinate = new QGeoCoordinate (latitude,longitude); + + qgeomaneuver->setWaypoint(*qgeocoordinate); + + QCOMPARE(qgeomaneuver->waypoint(),*qgeocoordinate); + + qgeocoordinate->setLatitude(30.3); + QVERIFY(qgeomaneuver->waypoint() != *qgeocoordinate); + + + delete qgeocoordinate; +} +void tst_QGeoManeuver::waypoint_data() +{ + QTest::addColumn("latitude"); + QTest::addColumn("longitude"); + + QTest::newRow("invalid0") << -12220.0 << 0.0; + QTest::newRow("invalid1") << 0.0 << 181.0; + + QTest::newRow("correct0") << 0.0 << 0.0; + QTest::newRow("correct1") << 90.0 << 0.0; + QTest::newRow("correct2") << 0.0 << 180.0; + QTest::newRow("correct3") << -90.0 << 0.0; + QTest::newRow("correct4") << 0.0 << -180.0; + QTest::newRow("correct5") << 45.0 << 90.0; +} + +void tst_QGeoManeuver::isValid() +{ + QVERIFY(!qgeomaneuver->isValid()); + qgeomaneuver->setDirection(QGeoManeuver::DirectionBearLeft); + QVERIFY(qgeomaneuver->isValid()); +} + +void tst_QGeoManeuver::operators(){ + + QGeoManeuver *qgeomaneuvercopy = new QGeoManeuver(*qgeomaneuver); + + QVERIFY(qgeomaneuver->operator ==(*qgeomaneuvercopy)); + QVERIFY(!qgeomaneuver->operator !=(*qgeomaneuvercopy)); + + qgeomaneuver->setDirection(QGeoManeuver::DirectionBearLeft); + qgeomaneuver->setInstructionText("Turn left in 50m"); + qgeomaneuver->setTimeToNextInstruction(60); + qgeomaneuver->setDistanceToNextInstruction(560.45); + + qgeomaneuvercopy->setDirection(QGeoManeuver::DirectionForward); + qgeomaneuvercopy->setInstructionText("Turn left in 80m"); + qgeomaneuvercopy->setTimeToNextInstruction(70); + qgeomaneuvercopy->setDistanceToNextInstruction(56065.45); + + QVERIFY(!(qgeomaneuver->operator ==(*qgeomaneuvercopy))); + QVERIFY(qgeomaneuver->operator !=(*qgeomaneuvercopy)); + + *qgeomaneuvercopy = qgeomaneuvercopy->operator =(*qgeomaneuver); + QVERIFY(qgeomaneuver->operator ==(*qgeomaneuvercopy)); + QVERIFY(!qgeomaneuver->operator !=(*qgeomaneuvercopy)); + + delete qgeomaneuvercopy; +} + +void tst_QGeoManeuver::alternateImplementation() +{ + QGeoManeuver qgeomaneuvercopy = *qgeomaneuver; + + QVERIFY(qgeomaneuvercopy == (*qgeomaneuver)); + + qgeomaneuvercopy.setDirection(QGeoManeuver::DirectionForward); + qgeomaneuvercopy.setInstructionText("Turn left in 80m"); + qgeomaneuvercopy.setTimeToNextInstruction(70); + qgeomaneuvercopy.setDistanceToNextInstruction(56065.45); + + QVERIFY(qgeomaneuvercopy != (*qgeomaneuver)); + + QGeoManeuverAlt mAlt; + QGeoManeuver m = mAlt; + + QCOMPARE(m.instructionText(), "QGeoManeuverPrivateDefaultAlt"); + m = qgeomaneuvercopy; + QCOMPARE(m.instructionText(), "Turn left in 80m"); + m = mAlt; + QCOMPARE(m.instructionText(), "QGeoManeuverPrivateDefaultAlt"); +} + + +QTEST_APPLESS_MAIN(tst_QGeoManeuver); diff --git a/tests/auto/qgeomaneuver/tst_qgeomaneuver.h b/tests/auto/qgeomaneuver/tst_qgeomaneuver.h new file mode 100644 index 0000000..279ed23 --- /dev/null +++ b/tests/auto/qgeomaneuver/tst_qgeomaneuver.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtLocation module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_QGEOMANEUVER_H +#define TST_QGEOMANEUVER_H + + +#include +#include +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" +#include +#include + + +class tst_QGeoManeuver : public QObject +{ + Q_OBJECT + +public: + tst_QGeoManeuver(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + //Start unit test for QGeoRouteManeuver + void constructor(); + void copy_constructor(); + void destructor(); + void direction(); + void direction_data(); + void distanceToNextInstruction(); + void instructionText(); + void position(); + void position_data(); + void timeToNextInstruction(); + void waypoint(); + void waypoint_data(); + void isValid(); + void operators(); + void alternateImplementation(); + //End Unit Test for QGeoRouteManeuver + +private: + QGeoManeuver *qgeomaneuver; + QGeoCoordinate *qgeocoordinate; + +}; + +Q_DECLARE_METATYPE( QList); +Q_DECLARE_METATYPE (QGeoManeuver::InstructionDirection); + +#endif // TST_QGEOMANEUVER_H + diff --git a/tests/auto/qgeopath/qgeopath.pro b/tests/auto/qgeopath/qgeopath.pro new file mode 100644 index 0000000..eec0597 --- /dev/null +++ b/tests/auto/qgeopath/qgeopath.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeopath + +SOURCES += \ + tst_qgeopath.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeopath/tst_qgeopath.cpp b/tests/auto/qgeopath/tst_qgeopath.cpp new file mode 100644 index 0000000..5781e50 --- /dev/null +++ b/tests/auto/qgeopath/tst_qgeopath.cpp @@ -0,0 +1,413 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoPath : public QObject +{ + Q_OBJECT + +private slots: + void defaultConstructor(); + void listConstructor(); + void assignment(); + + void comparison(); + void type(); + + void path(); + void width(); + void size(); + + void translate_data(); + void translate(); + + void valid_data(); + void valid(); + + void contains_data(); + void contains(); + + void boundingGeoRectangle_data(); + void boundingGeoRectangle(); + + void extendShape(); + void extendShape_data(); +}; + +void tst_QGeoPath::defaultConstructor() +{ + QGeoPath p; + QVERIFY(!p.path().size()); + QCOMPARE(p.width(), qreal(0.0)); + QCOMPARE(p.length(), double(0.0)); +} + +void tst_QGeoPath::listConstructor() +{ + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + coords.append(QGeoCoordinate(3,0)); + + QGeoPath p(coords, 1.0); + QCOMPARE(p.width(), qreal(1.0)); + QCOMPARE(p.path().size(), 3); + + for (const QGeoCoordinate &c : coords) { + QCOMPARE(p.path().contains(c), true); + } +} + +void tst_QGeoPath::assignment() +{ + QGeoPath p1; + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + coords.append(QGeoCoordinate(3,0)); + QGeoPath p2(coords, 1.0); + + QVERIFY(p1 != p2); + + p1 = p2; + QCOMPARE(p1.path(), coords); + QCOMPARE(p1.width(), 1.0); + QCOMPARE(p1, p2); + + // Assign c1 to an area + QGeoShape area = p1; + QCOMPARE(area.type(), p1.type()); + QVERIFY(area == p1); + + // Assign the area back to a bounding circle + QGeoPath p3 = area; + QCOMPARE(p3.path(), coords); + QCOMPARE(p3.width(), 1.0); + + // Check that the copy is not modified when modifying the original. + p1.setWidth(2.0); + QVERIFY(p3.width() != p1.width()); + QVERIFY(p3 != p1); +} + +void tst_QGeoPath::comparison() +{ + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + coords.append(QGeoCoordinate(3,0)); + QList coords2; + coords2.append(QGeoCoordinate(3,1)); + coords2.append(QGeoCoordinate(4,2)); + coords2.append(QGeoCoordinate(3,0)); + QGeoPath c1(coords, qreal(50.0)); + QGeoPath c2(coords, qreal(50.0)); + QGeoPath c3(coords, qreal(35.0)); + QGeoPath c4(coords2, qreal(50.0)); + + QVERIFY(c1 == c2); + QVERIFY(!(c1 != c2)); + + QVERIFY(!(c1 == c3)); + QVERIFY(c1 != c3); + + QVERIFY(!(c1 == c4)); + QVERIFY(c1 != c4); + + QVERIFY(!(c2 == c3)); + QVERIFY(c2 != c3); + + QGeoRectangle b1(QGeoCoordinate(20,20),QGeoCoordinate(10,30)); + QVERIFY(!(c1 == b1)); + QVERIFY(c1 != b1); + + QGeoShape *c2Ptr = &c2; + QVERIFY(c1 == *c2Ptr); + QVERIFY(!(c1 != *c2Ptr)); + + QGeoShape *c3Ptr = &c3; + QVERIFY(!(c1 == *c3Ptr)); + QVERIFY(c1 != *c3Ptr); +} + +void tst_QGeoPath::type() +{ + QGeoPath c; + QCOMPARE(c.type(), QGeoShape::PathType); +} + +void tst_QGeoPath::path() +{ + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + coords.append(QGeoCoordinate(3,0)); + + QGeoPath p; + p.setPath(coords); + QCOMPARE(p.path().size(), 3); + + for (const QGeoCoordinate &c : coords) { + QCOMPARE(p.path().contains(c), true); + } +} + +void tst_QGeoPath::width() +{ + QGeoPath p; + p.setWidth(10.0); + QCOMPARE(p.width(), qreal(10.0)); +} + +void tst_QGeoPath::size() +{ + QList coords; + + QGeoPath p1(coords, 3); + QCOMPARE(p1.size(), coords.size()); + + coords.append(QGeoCoordinate(1,1)); + QGeoPath p2(coords, 3); + QCOMPARE(p2.size(), coords.size()); + + coords.append(QGeoCoordinate(2,2)); + QGeoPath p3(coords, 3); + QCOMPARE(p3.size(), coords.size()); + + coords.append(QGeoCoordinate(3,0)); + QGeoPath p4(coords, 3); + QCOMPARE(p4.size(), coords.size()); + + p4.removeCoordinate(2); + QCOMPARE(p4.size(), coords.size() - 1); + + p4.removeCoordinate(coords.first()); + QCOMPARE(p4.size(), coords.size() - 2); +} + +void tst_QGeoPath::translate_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("lat"); + QTest::addColumn("lon"); + + QTest::newRow("Simple") << QGeoCoordinate(1,1) << QGeoCoordinate(2,2) << + QGeoCoordinate(3,0) << 5.0 << 4.0; + QTest::newRow("Backward") << QGeoCoordinate(1,1) << QGeoCoordinate(2,2) << + QGeoCoordinate(3,0) << -5.0 << -4.0; +} + +void tst_QGeoPath::translate() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(double, lat); + QFETCH(double, lon); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPath p(coords); + + p.translate(lat, lon); + + for (int i = 0; i < p.path().size(); i++) { + QCOMPARE(coords[i].latitude(), p.path()[i].latitude() - lat ); + QCOMPARE(coords[i].longitude(), p.path()[i].longitude() - lon ); + } +} + +void tst_QGeoPath::valid_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("width"); + QTest::addColumn("valid"); + + QTest::newRow("empty coords") << QGeoCoordinate() << QGeoCoordinate() << QGeoCoordinate() << qreal(5.0) << false; + QTest::newRow("invalid coord") << QGeoCoordinate(50, 50) << QGeoCoordinate(60, 60) << QGeoCoordinate(700, 700) << qreal(5.0) << false; + QTest::newRow("bad width") << QGeoCoordinate(10, 10) << QGeoCoordinate(11, 11) << QGeoCoordinate(10, 12) << qreal(-5.0) << true; + QTest::newRow("NaN width") << QGeoCoordinate(10, 10) << QGeoCoordinate(11, 11) << QGeoCoordinate(10, 12) << qreal(qQNaN()) << true; + QTest::newRow("zero width") << QGeoCoordinate(10, 10) << QGeoCoordinate(11, 11) << QGeoCoordinate(10, 12) << qreal(0) << true; + QTest::newRow("good") << QGeoCoordinate(10, 10) << QGeoCoordinate(11, 11) << QGeoCoordinate(10, 12) << qreal(5) << true; +} + +void tst_QGeoPath::valid() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(qreal, width); + QFETCH(bool, valid); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPath p(coords, width); + + QCOMPARE(p.isValid(), valid); + + QGeoShape area = p; + QCOMPARE(area.isValid(), valid); +} + +void tst_QGeoPath::contains_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("width"); + QTest::addColumn("probe"); + QTest::addColumn("result"); + + QList c; + c.append(QGeoCoordinate(1,1)); + c.append(QGeoCoordinate(2,2)); + c.append(QGeoCoordinate(3,0)); + + QTest::newRow("One of the points") << c[0] << c[1] << c[2] << 0.0 << QGeoCoordinate(2, 2) << true; + QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << 0.0 << QGeoCoordinate(0.8, 0.8) << false; + QTest::newRow("Not so far away and large line") << c[0] << c[1] << c[2] << 100000.0 << QGeoCoordinate(0.8, 0.8) << true; +} + +void tst_QGeoPath::contains() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(qreal, width); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, result); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPath p(coords, width); + + QCOMPARE(p.contains(probe), result); + + QGeoShape area = p; + QCOMPARE(area.contains(probe), result); +} + +void tst_QGeoPath::boundingGeoRectangle_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("width"); + QTest::addColumn("probe"); + QTest::addColumn("result"); + + QList c; + c.append(QGeoCoordinate(1,1)); + c.append(QGeoCoordinate(2,2)); + c.append(QGeoCoordinate(3,0)); + + QTest::newRow("One of the points") << c[0] << c[1] << c[2] << 0.0 << QGeoCoordinate(2, 2) << true; + QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << 0.0 << QGeoCoordinate(0, 0) << false; + QTest::newRow("Inside the bounds") << c[0] << c[1] << c[2] << 100.0 << QGeoCoordinate(1, 0) << true; + QTest::newRow("Inside the bounds") << c[0] << c[1] << c[2] << 100.0 << QGeoCoordinate(1.1, 0.1) << true; +} + +void tst_QGeoPath::boundingGeoRectangle() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(qreal, width); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, result); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPath p(coords, width); + + QGeoRectangle box = p.boundingGeoRectangle(); + QCOMPARE(box.contains(probe), result); +} + +void tst_QGeoPath::extendShape() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(qreal, width); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, before); + QFETCH(bool, after); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPath p(coords, width); + + + QCOMPARE(p.contains(probe), before); + p.extendShape(probe); + QCOMPARE(p.contains(probe), after); +} + +void tst_QGeoPath::extendShape_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("width"); + QTest::addColumn("probe"); + QTest::addColumn("before"); + QTest::addColumn("after"); + + QList c; + c.append(QGeoCoordinate(1,1)); + c.append(QGeoCoordinate(2,2)); + c.append(QGeoCoordinate(3,0)); + + QTest::newRow("One of the points") << c[0] << c[1] << c[2] << 0.0 << QGeoCoordinate(2, 2) << true << true; + QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << 0.0 << QGeoCoordinate(0, 0) << false << true; + QTest::newRow("Not so far away and large line") << c[0] << c[1] << c[2] << 100000.0 << QGeoCoordinate(0.8, 0.8) << true << true; +} + +QTEST_MAIN(tst_QGeoPath) +#include "tst_qgeopath.moc" diff --git a/tests/auto/qgeopolygon/qgeopolygon.pro b/tests/auto/qgeopolygon/qgeopolygon.pro new file mode 100644 index 0000000..f6314ca --- /dev/null +++ b/tests/auto/qgeopolygon/qgeopolygon.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeopolygon + +SOURCES += \ + tst_qgeopolygon.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeopolygon/tst_qgeopolygon.cpp b/tests/auto/qgeopolygon/tst_qgeopolygon.cpp new file mode 100644 index 0000000..12e39f0 --- /dev/null +++ b/tests/auto/qgeopolygon/tst_qgeopolygon.cpp @@ -0,0 +1,402 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoPolygon : public QObject +{ + Q_OBJECT + +private slots: + void defaultConstructor(); + void listConstructor(); + void assignment(); + + void comparison(); + void type(); + + void path(); + void size(); + + void translate_data(); + void translate(); + + void valid_data(); + void valid(); + + void contains_data(); + void contains(); + + void boundingGeoRectangle_data(); + void boundingGeoRectangle(); + + void extendShape(); + void extendShape_data(); +}; + +void tst_QGeoPolygon::defaultConstructor() +{ + QGeoPolygon p; + QVERIFY(!p.path().size()); + QVERIFY(!p.size()); + QVERIFY(!p.isValid()); + QVERIFY(p.isEmpty()); +} + +void tst_QGeoPolygon::listConstructor() +{ + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + QGeoPolygon p2(coords); + QCOMPARE(p2.path().size(), 2); + QCOMPARE(p2.size(), 2); + QVERIFY(!p2.isValid()); // a polygon can't have only 2 coords + QVERIFY(!p2.isEmpty()); + + coords.append(QGeoCoordinate(3,0)); + + QGeoPolygon p(coords); + QCOMPARE(p.path().size(), 3); + QCOMPARE(p.size(), 3); + QVERIFY(p.isValid()); + QVERIFY(!p.isEmpty()); + + + for (const QGeoCoordinate &c : coords) { + QCOMPARE(p.path().contains(c), true); + QCOMPARE(p.containsCoordinate(c), true); + } +} + +void tst_QGeoPolygon::assignment() +{ + QGeoPolygon p1; + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + coords.append(QGeoCoordinate(3,0)); + QGeoPolygon p2(coords); + + QVERIFY(p1 != p2); + + p1 = p2; + QCOMPARE(p1.path(), coords); + QCOMPARE(p1, p2); + + // Assign c1 to an area + QGeoShape area = p1; + QCOMPARE(area.type(), p1.type()); + QVERIFY(area == p1); + + // Assign the area back to a polygon + QGeoPolygon p3 = area; + QCOMPARE(p3.path(), coords); + QVERIFY(p3 == p1); + + // Check that the copy is not modified when modifying the original. + p1.addCoordinate(QGeoCoordinate(4,0)); + QVERIFY(p3 != p1); +} + +void tst_QGeoPolygon::comparison() +{ + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + coords.append(QGeoCoordinate(3,0)); + QList coords2; + coords2.append(QGeoCoordinate(3,1)); + coords2.append(QGeoCoordinate(4,2)); + coords2.append(QGeoCoordinate(3,0)); + QGeoPolygon c1(coords); + QGeoPolygon c2(coords); + QGeoPolygon c3(coords2); + + QVERIFY(c1 == c2); + QVERIFY(!(c1 != c2)); + + QVERIFY(!(c1 == c3)); + QVERIFY(c1 != c3); + + QVERIFY(!(c2 == c3)); + QVERIFY(c2 != c3); + + QGeoRectangle b1(QGeoCoordinate(20,20),QGeoCoordinate(10,30)); + QVERIFY(!(c1 == b1)); + QVERIFY(c1 != b1); + + QGeoShape *c2Ptr = &c2; + QVERIFY(c1 == *c2Ptr); + QVERIFY(!(c1 != *c2Ptr)); + + QGeoShape *c3Ptr = &c3; + QVERIFY(!(c1 == *c3Ptr)); + QVERIFY(c1 != *c3Ptr); +} + +void tst_QGeoPolygon::type() +{ + QGeoPolygon c; + QCOMPARE(c.type(), QGeoShape::PolygonType); +} + +void tst_QGeoPolygon::path() +{ + QList coords; + coords.append(QGeoCoordinate(1,1)); + coords.append(QGeoCoordinate(2,2)); + coords.append(QGeoCoordinate(3,0)); + + QGeoPolygon p; + p.setPath(coords); + QCOMPARE(p.path().size(), 3); + QCOMPARE(p.size(), 3); + + for (const QGeoCoordinate &c : coords) { + QCOMPARE(p.path().contains(c), true); + QCOMPARE(p.containsCoordinate(c), true); + } +} + +void tst_QGeoPolygon::size() +{ + QList coords; + + QGeoPolygon p1(coords); + QCOMPARE(p1.size(), coords.size()); + + coords.append(QGeoCoordinate(1,1)); + QGeoPolygon p2(coords); + QCOMPARE(p2.size(), coords.size()); + + coords.append(QGeoCoordinate(2,2)); + QGeoPolygon p3(coords); + QCOMPARE(p3.size(), coords.size()); + + coords.append(QGeoCoordinate(3,0)); + QGeoPolygon p4(coords); + QCOMPARE(p4.size(), coords.size()); + + p4.removeCoordinate(2); + QCOMPARE(p4.size(), coords.size() - 1); + + p4.removeCoordinate(coords.first()); + QCOMPARE(p4.size(), coords.size() - 2); +} + +void tst_QGeoPolygon::translate_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("lat"); + QTest::addColumn("lon"); + + QTest::newRow("Simple") << QGeoCoordinate(1,1) << QGeoCoordinate(2,2) << + QGeoCoordinate(3,0) << 5.0 << 4.0; + QTest::newRow("Backward") << QGeoCoordinate(1,1) << QGeoCoordinate(2,2) << + QGeoCoordinate(3,0) << -5.0 << -4.0; +} + +void tst_QGeoPolygon::translate() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(double, lat); + QFETCH(double, lon); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPolygon p(coords); + + p.translate(lat, lon); + + for (int i = 0; i < p.path().size(); i++) { + QCOMPARE(coords[i].latitude(), p.path()[i].latitude() - lat ); + QCOMPARE(coords[i].longitude(), p.path()[i].longitude() - lon ); + } +} + +void tst_QGeoPolygon::valid_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("valid"); + + QTest::newRow("empty coords") << QGeoCoordinate() << QGeoCoordinate() << QGeoCoordinate() << false; + QTest::newRow("invalid coord") << QGeoCoordinate(50, 50) << QGeoCoordinate(60, 60) << QGeoCoordinate(700, 700) << false; + QTest::newRow("good") << QGeoCoordinate(10, 10) << QGeoCoordinate(11, 11) << QGeoCoordinate(10, 12) << true; +} + +void tst_QGeoPolygon::valid() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(bool, valid); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPolygon p(coords); + + QCOMPARE(p.isValid(), valid); + + QGeoShape area = p; + QCOMPARE(area.isValid(), valid); +} + +void tst_QGeoPolygon::contains_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("probe"); + QTest::addColumn("result"); + + QList c; + c.append(QGeoCoordinate(1,1)); + c.append(QGeoCoordinate(2,2)); + c.append(QGeoCoordinate(3,0)); + + QTest::newRow("One of the points") << c[0] << c[1] << c[2] << QGeoCoordinate(2, 2) << true; + QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << QGeoCoordinate(0.8, 0.8) << false; + QTest::newRow("Not so far away and large line") << c[0] << c[1] << c[2] << QGeoCoordinate(0.8, 0.8) << false; + QTest::newRow("Inside") << c[0] << c[1] << c[2] << QGeoCoordinate(2.0, 1.0) << true; +} + +void tst_QGeoPolygon::contains() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, result); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPolygon p(coords); + + QCOMPARE(p.contains(probe), result); + + QGeoShape area = p; + QCOMPARE(area.contains(probe), result); +} + +void tst_QGeoPolygon::boundingGeoRectangle_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("probe"); + QTest::addColumn("result"); + + QList c; + c.append(QGeoCoordinate(1,1)); + c.append(QGeoCoordinate(2,2)); + c.append(QGeoCoordinate(3,0)); + + QTest::newRow("One of the points") << c[0] << c[1] << c[2] << QGeoCoordinate(2, 2) << true; + QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << QGeoCoordinate(0, 0) << false; + QTest::newRow("Inside the bounds") << c[0] << c[1] << c[2] << QGeoCoordinate(1, 0) << true; + QTest::newRow("Inside the bounds") << c[0] << c[1] << c[2] << QGeoCoordinate(1.1, 0.1) << true; +} + +void tst_QGeoPolygon::boundingGeoRectangle() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, result); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPolygon p(coords); + + QGeoRectangle box = p.boundingGeoRectangle(); + QCOMPARE(box.contains(probe), result); +} + +void tst_QGeoPolygon::extendShape() +{ + QFETCH(QGeoCoordinate, c1); + QFETCH(QGeoCoordinate, c2); + QFETCH(QGeoCoordinate, c3); + QFETCH(QGeoCoordinate, probe); + QFETCH(bool, before); + QFETCH(bool, after); + + QList coords; + coords.append(c1); + coords.append(c2); + coords.append(c3); + QGeoPolygon p(coords); + + + QCOMPARE(p.contains(probe), before); + p.extendShape(probe); + QCOMPARE(p.contains(probe), after); +} + +void tst_QGeoPolygon::extendShape_data() +{ + QTest::addColumn("c1"); + QTest::addColumn("c2"); + QTest::addColumn("c3"); + QTest::addColumn("probe"); + QTest::addColumn("before"); + QTest::addColumn("after"); + + QList c; + c.append(QGeoCoordinate(1,1)); + c.append(QGeoCoordinate(2,2)); + c.append(QGeoCoordinate(3,0)); + + QTest::newRow("One of the points") << c[0] << c[1] << c[2] << QGeoCoordinate(2, 2) << true << true; + QTest::newRow("Not so far away") << c[0] << c[1] << c[2] << QGeoCoordinate(0, 0) << false << true; + QTest::newRow("Contained point") << c[0] << c[1] << c[2] << QGeoCoordinate(2.0, 1.0) << true << true; +} + +QTEST_MAIN(tst_QGeoPolygon) +#include "tst_qgeopolygon.moc" diff --git a/tests/auto/qgeopositioninfo/qgeopositioninfo.pro b/tests/auto/qgeopositioninfo/qgeopositioninfo.pro new file mode 100644 index 0000000..f928c8b --- /dev/null +++ b/tests/auto/qgeopositioninfo/qgeopositioninfo.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeopositioninfo + +SOURCES += tst_qgeopositioninfo.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeopositioninfo/tst_qgeopositioninfo.cpp b/tests/auto/qgeopositioninfo/tst_qgeopositioninfo.cpp new file mode 100644 index 0000000..f236b39 --- /dev/null +++ b/tests/auto/qgeopositioninfo/tst_qgeopositioninfo.cpp @@ -0,0 +1,385 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include + +#include +#include +#include +#include +#include + +#include + +QT_USE_NAMESPACE + +Q_DECLARE_METATYPE(QGeoPositionInfo::Attribute) + +QByteArray tst_qgeopositioninfo_debug; + +void tst_qgeopositioninfo_messageHandler(QtMsgType type, const QMessageLogContext&, const QString &msg) +{ + switch (type) { + case QtDebugMsg : + tst_qgeopositioninfo_debug = msg.toLocal8Bit(); + break; + default: + break; + } +} + +QList tst_qgeopositioninfo_qrealTestValues() +{ + QList values; + + if (qreal(DBL_MIN) == DBL_MIN) + values << DBL_MIN; + + values << FLT_MIN; + values << -1.0 << 0.0 << 1.0; + values << FLT_MAX; + + if (qreal(DBL_MAX) == DBL_MAX) + values << DBL_MAX; + + return values; +} + +QList tst_qgeopositioninfo_getAttributes() +{ + QList attributes; + attributes << QGeoPositionInfo::Direction + << QGeoPositionInfo::GroundSpeed + << QGeoPositionInfo::VerticalSpeed + << QGeoPositionInfo::MagneticVariation + << QGeoPositionInfo::HorizontalAccuracy + << QGeoPositionInfo::VerticalAccuracy; + return attributes; +} + + +class tst_QGeoPositionInfo : public QObject +{ + Q_OBJECT + +private: + QGeoPositionInfo infoWithAttribute(QGeoPositionInfo::Attribute attribute, qreal value) + { + QGeoPositionInfo info; + info.setAttribute(attribute, value); + return info; + } + + void addTestData_info() + { + QTest::addColumn("info"); + + QTest::newRow("invalid") << QGeoPositionInfo(); + + QTest::newRow("coord") << QGeoPositionInfo(QGeoCoordinate(-27.3422,150.2342), QDateTime()); + QTest::newRow("datetime") << QGeoPositionInfo(QGeoCoordinate(), QDateTime::currentDateTime()); + + QList attributes = tst_qgeopositioninfo_getAttributes(); + QList values = tst_qgeopositioninfo_qrealTestValues(); + for (int i=0; i("coord"); + QTest::addColumn("dateTime"); + QTest::addColumn("valid"); + + QTest::newRow("both null") << QGeoCoordinate() << QDateTime() << false; + QTest::newRow("both valid") << QGeoCoordinate(1,1) << QDateTime::currentDateTime() << true; + QTest::newRow("valid coord") << QGeoCoordinate(1,1) << QDateTime() << false; + QTest::newRow("valid datetime") << QGeoCoordinate() << QDateTime::currentDateTime() << false; + QTest::newRow("valid time but not date == invalid") + << QGeoCoordinate() << QDateTime(QDate(), QTime::currentTime()) << false; + QTest::newRow("valid date but not time == valid due to QDateTime constructor") + << QGeoCoordinate() << QDateTime(QDate::currentDate(), QTime()) << false; + } + + void constructor_copy() + { + QFETCH(QGeoPositionInfo, info); + + QCOMPARE(QGeoPositionInfo(info), info); + } + + void constructor_copy_data() + { + addTestData_info(); + } + + void operator_assign() + { + QFETCH(QGeoPositionInfo, info); + + QGeoPositionInfo info2 = info; + QCOMPARE(info2, info); + } + + void operator_assign_data() + { + addTestData_info(); + } + + void operator_equals() + { + QFETCH(QGeoPositionInfo, info); + + QVERIFY(info == info); + if (info.isValid()) + QCOMPARE(info == QGeoPositionInfo(), false); + } + + void operator_equals_data() + { + addTestData_info(); + } + + void operator_notEquals() + { + QFETCH(QGeoPositionInfo, info); + + QCOMPARE(info != info, false); + if (info.isValid()) + QCOMPARE(info != QGeoPositionInfo(), true); + } + + void operator_notEquals_data() + { + addTestData_info(); + } + + void setDateTime() + { + QFETCH(QDateTime, dateTime); + + QGeoPositionInfo info; + info.setTimestamp(dateTime); + QCOMPARE(info.timestamp(), dateTime); + } + + void setDateTime_data() + { + QTest::addColumn("dateTime"); + QTest::newRow("invalid") << QDateTime(); + QTest::newRow("now") << QDateTime::currentDateTime(); + } + + void dateTime() + { + QGeoPositionInfo info; + QVERIFY(info.timestamp().isNull()); + } + + void setCoordinate() + { + + QFETCH(QGeoCoordinate, coord); + + QGeoPositionInfo info; + info.setCoordinate(coord); + QCOMPARE(info.coordinate(), coord); + } + + void setCoordinate_data() + { + QTest::addColumn("coord"); + + QTest::newRow("invalid") << QGeoCoordinate(); + QTest::newRow("valid") << QGeoCoordinate(30,30); + } + + void attribute() + { + QFETCH(QGeoPositionInfo::Attribute, attribute); + QFETCH(qreal, value); + + QGeoPositionInfo info; + QVERIFY(qIsNaN(info.attribute(attribute))); + + info.setAttribute(attribute, value); + QCOMPARE(info.attribute(attribute), value); + + info.removeAttribute(attribute); + QVERIFY(qIsNaN(info.attribute(attribute))); + } + + void attribute_data() + { + QTest::addColumn("attribute"); + QTest::addColumn("value"); + + QList attributes = tst_qgeopositioninfo_getAttributes(); + QList values = tst_qgeopositioninfo_qrealTestValues(); + for (int i=0; i> inInfo; + QCOMPARE(inInfo, info); + } + + void datastream_data() + { + addTestData_info(); + } + + void debug() + { + QFETCH(QGeoPositionInfo, info); + QFETCH(int, nextValue); + QFETCH(QByteArray, debugStringEnd); + + qInstallMessageHandler(tst_qgeopositioninfo_messageHandler); + qDebug() << info << nextValue; + qInstallMessageHandler(0); + + // use endsWith() so we don't depend on QDateTime's debug() implementation + QVERIFY2(tst_qgeopositioninfo_debug.endsWith(debugStringEnd), + qPrintable(QString::fromLatin1("'%1' does not end with '%2'"). + arg(QLatin1String(tst_qgeopositioninfo_debug), + QLatin1String(debugStringEnd)))); + } + + void debug_data() + { + QTest::addColumn("info"); + QTest::addColumn("nextValue"); + QTest::addColumn("debugStringEnd"); + + QTest::newRow("no values") << QGeoPositionInfo() << 40 + << QString("QGeoCoordinate(?, ?)) 40").toLatin1(); + + QGeoCoordinate coord(1, 1); + QTest::newRow("coord, time") << QGeoPositionInfo(coord, QDateTime::currentDateTime()) + << 40 << QByteArray("QGeoCoordinate(1, 1)) 40"); + + QGeoPositionInfo info; + info.setAttribute(QGeoPositionInfo::Direction, 1.1); + info.setAttribute(QGeoPositionInfo::GroundSpeed, 2.1); + info.setAttribute(QGeoPositionInfo::VerticalSpeed, 3.1); + info.setAttribute(QGeoPositionInfo::MagneticVariation, 4.1); + info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, 5.1); + info.setAttribute(QGeoPositionInfo::VerticalAccuracy, 6.1); + QTest::newRow("all attributes") << info << 40 + << QByteArray("QGeoCoordinate(?, ?), Direction=1.1, GroundSpeed=2.1, VerticalSpeed=3.1, MagneticVariation=4.1, HorizontalAccuracy=5.1, VerticalAccuracy=6.1) 40"); + } +}; + + +QTEST_APPLESS_MAIN(tst_QGeoPositionInfo) +#include "tst_qgeopositioninfo.moc" diff --git a/tests/auto/qgeopositioninfosource/qgeopositioninfosource.pro b/tests/auto/qgeopositioninfosource/qgeopositioninfosource.pro new file mode 100644 index 0000000..ee49425 --- /dev/null +++ b/tests/auto/qgeopositioninfosource/qgeopositioninfosource.pro @@ -0,0 +1,15 @@ +TEMPLATE = app +CONFIG+=testcase +testcase.timeout = 400 # this test is slow +TARGET=tst_qgeopositioninfosource + +HEADERS += ../utils/qlocationtestutils_p.h \ + testqgeopositioninfosource_p.h + +SOURCES += ../utils/qlocationtestutils.cpp \ + testqgeopositioninfosource.cpp \ + tst_qgeopositioninfosource.cpp + +CONFIG -= app_bundle + +QT += positioning testlib diff --git a/tests/auto/qgeopositioninfosource/testqgeopositioninfosource.cpp b/tests/auto/qgeopositioninfosource/testqgeopositioninfosource.cpp new file mode 100644 index 0000000..ce7b4d6 --- /dev/null +++ b/tests/auto/qgeopositioninfosource/testqgeopositioninfosource.cpp @@ -0,0 +1,778 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "testqgeopositioninfosource_p.h" +#include "../utils/qlocationtestutils_p.h" + +Q_DECLARE_METATYPE(QGeoPositionInfoSource::PositioningMethod) +Q_DECLARE_METATYPE(QGeoPositionInfoSource::PositioningMethods) + +#define MAX_WAITING_TIME 50000 + +// Must provide a valid source, unless testing the source +// returned by QGeoPositionInfoSource::createDefaultSource() on a system +// that has no default source +#define CHECK_SOURCE_VALID { \ + if (!m_source) { \ + if (m_testingDefaultSource && QGeoPositionInfoSource::createDefaultSource(0) == 0) \ + QSKIP("No default position source on this system"); \ + else \ + QFAIL("createTestSource() must return a valid source!"); \ + } \ + } + +class MyPositionSource : public QGeoPositionInfoSource +{ + Q_OBJECT +public: + MyPositionSource(QObject *parent = 0) + : QGeoPositionInfoSource(parent) { + } + + QGeoPositionInfo lastKnownPosition(bool /*fromSatellitePositioningMethodsOnly = false*/) const { + return QGeoPositionInfo(); + } + + void setSupportedPositioningMethods(PositioningMethods methods) { + m_methods = methods; + } + + virtual PositioningMethods supportedPositioningMethods() const { + return m_methods; + } + virtual int minimumUpdateInterval() const { + return 0; + } + + virtual void startUpdates() {} + virtual void stopUpdates() {} + + virtual void requestUpdate(int) {} + + Error error() const { return QGeoPositionInfoSource::NoError; } + +private: + PositioningMethods m_methods; +}; + +class DefaultSourceTest : public TestQGeoPositionInfoSource +{ + Q_OBJECT +protected: + QGeoPositionInfoSource *createTestSource() { + return QGeoPositionInfoSource::createSource(QStringLiteral("test.source"), 0); + } +}; + + +TestQGeoPositionInfoSource::TestQGeoPositionInfoSource(QObject *parent) + : QObject(parent) +{ + m_testingDefaultSource = false; +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif +} + +TestQGeoPositionInfoSource *TestQGeoPositionInfoSource::createDefaultSourceTest() +{ + DefaultSourceTest *test = new DefaultSourceTest; + test->m_testingDefaultSource = true; + return test; +} + +void TestQGeoPositionInfoSource::test_slot1() +{ +} + +void TestQGeoPositionInfoSource::test_slot2() +{ + m_testSlot2Called = true; +} + +void TestQGeoPositionInfoSource::base_initTestCase() +{ + +} + +void TestQGeoPositionInfoSource::base_init() +{ + m_source = createTestSource(); + m_testSlot2Called = false; +} + +void TestQGeoPositionInfoSource::base_cleanup() +{ + delete m_source; + m_source = 0; +} + +void TestQGeoPositionInfoSource::base_cleanupTestCase() +{ +} + +void TestQGeoPositionInfoSource::initTestCase() +{ + base_initTestCase(); +} + +void TestQGeoPositionInfoSource::init() +{ + base_init(); +} + +void TestQGeoPositionInfoSource::cleanup() +{ + base_cleanup(); +} + +void TestQGeoPositionInfoSource::cleanupTestCase() +{ + base_cleanupTestCase(); +} + +// TC_ID_3_x_1 +void TestQGeoPositionInfoSource::constructor_withParent() +{ + QObject *parent = new QObject(); + new MyPositionSource(parent); + delete parent; +} + +// TC_ID_3_x_2 +void TestQGeoPositionInfoSource::constructor_noParent() +{ + MyPositionSource *obj = new MyPositionSource(); + delete obj; +} + +void TestQGeoPositionInfoSource::updateInterval() +{ + MyPositionSource s; + QCOMPARE(s.updateInterval(), 0); +} + +void TestQGeoPositionInfoSource::setPreferredPositioningMethods() +{ + QFETCH(QGeoPositionInfoSource::PositioningMethod, supported); + QFETCH(QGeoPositionInfoSource::PositioningMethod, preferred); + QFETCH(QGeoPositionInfoSource::PositioningMethod, resulting); + + MyPositionSource s; + s.setSupportedPositioningMethods(supported); + s.setPreferredPositioningMethods(preferred); + QCOMPARE(s.preferredPositioningMethods(), resulting); +} + +void TestQGeoPositionInfoSource::setPreferredPositioningMethods_data() +{ + QTest::addColumn("supported"); + QTest::addColumn("preferred"); + QTest::addColumn("resulting"); + + QTest::newRow("Sat supported, Sat preferred") + << QGeoPositionInfoSource::SatellitePositioningMethods + << QGeoPositionInfoSource::SatellitePositioningMethods + << QGeoPositionInfoSource::SatellitePositioningMethods; + QTest::newRow("Sat supported, Non-Sat preferred") + << QGeoPositionInfoSource::SatellitePositioningMethods + << QGeoPositionInfoSource::NonSatellitePositioningMethods + << QGeoPositionInfoSource::SatellitePositioningMethods; + QTest::newRow("Sat supported, All preferred") + << QGeoPositionInfoSource::SatellitePositioningMethods + << QGeoPositionInfoSource::AllPositioningMethods + << QGeoPositionInfoSource::SatellitePositioningMethods; + + QTest::newRow("Non-Sat supported, Sat preferred") + << QGeoPositionInfoSource::NonSatellitePositioningMethods + << QGeoPositionInfoSource::SatellitePositioningMethods + << QGeoPositionInfoSource::NonSatellitePositioningMethods; + QTest::newRow("Non-Sat supported, Non-Sat preferred") + << QGeoPositionInfoSource::NonSatellitePositioningMethods + << QGeoPositionInfoSource::NonSatellitePositioningMethods + << QGeoPositionInfoSource::NonSatellitePositioningMethods; + QTest::newRow("Non-Sat supported, All preferred") + << QGeoPositionInfoSource::NonSatellitePositioningMethods + << QGeoPositionInfoSource::AllPositioningMethods + << QGeoPositionInfoSource::NonSatellitePositioningMethods; + + QTest::newRow("All supported, Sat preferred") + << QGeoPositionInfoSource::AllPositioningMethods + << QGeoPositionInfoSource::SatellitePositioningMethods + << QGeoPositionInfoSource::SatellitePositioningMethods; + QTest::newRow("All supported, Non-Sat preferred") + << QGeoPositionInfoSource::AllPositioningMethods + << QGeoPositionInfoSource::NonSatellitePositioningMethods + << QGeoPositionInfoSource::NonSatellitePositioningMethods; + QTest::newRow("All supported, All preferred") + << QGeoPositionInfoSource::AllPositioningMethods + << QGeoPositionInfoSource::AllPositioningMethods + << QGeoPositionInfoSource::AllPositioningMethods; +} + +void TestQGeoPositionInfoSource::preferredPositioningMethods() +{ + MyPositionSource s; + QCOMPARE(s.preferredPositioningMethods(), 0); +} + +//TC_ID_3_x_1 : Create a position source with the given parent that reads from the system's default +// sources of location data +void TestQGeoPositionInfoSource::createDefaultSource() +{ + QObject *parent = new QObject; + + QGeoPositionInfoSource *source = QGeoPositionInfoSource::createDefaultSource(parent); + // now all platforms have the dummy plugin at least + QVERIFY(source != 0); + delete parent; +} + +void TestQGeoPositionInfoSource::setUpdateInterval() +{ + CHECK_SOURCE_VALID; + + QFETCH(int, interval); + QFETCH(int, expectedInterval); + + m_source->setUpdateInterval(interval); + QCOMPARE(m_source->updateInterval(), expectedInterval); +} + +void TestQGeoPositionInfoSource::setUpdateInterval_data() +{ + QTest::addColumn("interval"); + QTest::addColumn("expectedInterval"); + QGeoPositionInfoSource *source = createTestSource(); + int minUpdateInterval = source ? source->minimumUpdateInterval() : -1; + if (source) + delete source; + + QTest::newRow("0") << 0 << 0; + + if (minUpdateInterval > -1) { + QTest::newRow("INT_MIN") << INT_MIN << minUpdateInterval; + QTest::newRow("-1") << -1 << minUpdateInterval; + } + + if (minUpdateInterval > 0) { + QTest::newRow("more than minInterval") << minUpdateInterval + 1 << minUpdateInterval + 1; + QTest::newRow("equal to minInterval") << minUpdateInterval << minUpdateInterval; + } + + if (minUpdateInterval > 1) { + QTest::newRow("less then minInterval") << minUpdateInterval - 1 << minUpdateInterval; + QTest::newRow("in btw zero and minInterval") << 1 << minUpdateInterval; + } +} + +void TestQGeoPositionInfoSource::lastKnownPosition() +{ + CHECK_SOURCE_VALID; + QFETCH(QGeoPositionInfoSource::PositioningMethod, positioningMethod); + QFETCH(bool, lastKnownPositionArgument); + + if ((m_source->supportedPositioningMethods() & positioningMethod) == 0) + QSKIP("Not a supported positioning method for this position source"); + + m_source->setPreferredPositioningMethods(positioningMethod); + + QSignalSpy spy(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy timeout(m_source, SIGNAL(updateTimeout())); + int time_out = 7000; + m_source->setUpdateInterval(time_out); + m_source->startUpdates(); + + // Use QEventLoop instead of qWait() to ensure we stop as soon as a + // position is emitted (otherwise the lastKnownPosition() may have + // changed by the time it is checked) + QEventLoop loop; + QTimer timer; + //simulated CI tests will quickly return -> real GPS tests take 2 minutes for satellite systems + //use a 5 min timeout + timer.setInterval(300000); + connect(m_source, SIGNAL(positionUpdated(QGeoPositionInfo)), + &loop, SLOT(quit())); + connect(&timer, SIGNAL(timeout()), &loop, SLOT(quit())); + timer.start(); + loop.exec(); + + QVERIFY((spy.count() > 0) && (timeout.count() == 0)); + + QList list = spy.takeFirst(); + QGeoPositionInfo info = list.at(0).value(); + QGeoPositionInfo lastPositioninfo = m_source->lastKnownPosition(lastKnownPositionArgument); + + // lastPositioninfo is only gauranteed to be valid in all cases when only using satelite + // positioning methods or when lastKnownPositionArgument is false + if (!lastKnownPositionArgument || + positioningMethod == QGeoPositionInfoSource::SatellitePositioningMethods) { + QVERIFY(lastPositioninfo.isValid()); + } + + if (lastPositioninfo.isValid()) { + QCOMPARE(info.coordinate(), lastPositioninfo.coordinate()); + // On some CI machines the above evenloop code is not sufficient as positionUpdated + // still fires causing last know position and last update to be out of sync. + // To accommodate we check that the time stamps are no more than 1s apart + // ideally they should be the same + // doesn't work: QCOMPARE(info.timestamp(), lastPositioninfo.timestamp()); + const qint64 diff = qAbs(info.timestamp().msecsTo(lastPositioninfo.timestamp())); + QCOMPARE(diff < 1000, true); + + QCOMPARE(info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy), + lastPositioninfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)); + + if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) { + bool isNaN1 = qIsNaN(info.attribute(QGeoPositionInfo::HorizontalAccuracy)); + bool isNaN2 = qIsNaN(lastPositioninfo.attribute(QGeoPositionInfo::HorizontalAccuracy)); + QCOMPARE(isNaN1, isNaN2); + if (!isNaN1) { + QCOMPARE(qFuzzyCompare(info.attribute(QGeoPositionInfo::HorizontalAccuracy), + lastPositioninfo.attribute(QGeoPositionInfo::HorizontalAccuracy)), true); + } + } + + QCOMPARE(info.hasAttribute(QGeoPositionInfo::VerticalAccuracy), + lastPositioninfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy)); + + if (info.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) { + bool isNaN1 = qIsNaN(info.attribute(QGeoPositionInfo::VerticalAccuracy)); + bool isNaN2 = qIsNaN(lastPositioninfo.attribute(QGeoPositionInfo::VerticalAccuracy)); + QCOMPARE(isNaN1, isNaN2); + if (!isNaN1) { + QCOMPARE(qFuzzyCompare(info.attribute(QGeoPositionInfo::VerticalAccuracy), + lastPositioninfo.attribute(QGeoPositionInfo::VerticalAccuracy)), true); + } + } + } + + m_source->stopUpdates(); +} + +void TestQGeoPositionInfoSource::lastKnownPosition_data() +{ + QTest::addColumn("positioningMethod"); + QTest::addColumn("lastKnownPositionArgument"); + + // no good way to determine on MeeGo what are supported. If we ask for all or non-satellites, we + // typically get geoclue-example provider, which is not suitable for this test. + QTest::newRow("all - false") << QGeoPositionInfoSource::AllPositioningMethods << false; + QTest::newRow("all - true") << QGeoPositionInfoSource::AllPositioningMethods << true; + QTest::newRow("satellite - false") << QGeoPositionInfoSource::SatellitePositioningMethods << false; + QTest::newRow("satellite - true") << QGeoPositionInfoSource::SatellitePositioningMethods << true; +} + +void TestQGeoPositionInfoSource::minimumUpdateInterval() +{ + CHECK_SOURCE_VALID; + + QVERIFY(m_source->minimumUpdateInterval() > 0); +} + +//TC_ID_3_x_1 +void TestQGeoPositionInfoSource::startUpdates_testIntervals() +{ + CHECK_SOURCE_VALID; + QSignalSpy spy(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy timeout(m_source, SIGNAL(updateTimeout())); + m_source->setUpdateInterval(7000); + int interval = m_source->updateInterval(); + + m_source->startUpdates(); + + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 9500); + for (int i = 0; i < 6; i++) { + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 1) && (timeout.count() == 0), (interval*2)); + spy.clear(); + } + + m_source->stopUpdates(); +} + + +void TestQGeoPositionInfoSource::startUpdates_testIntervalChangesWhileRunning() +{ + // There are two ways of dealing with an interval change, and we have left it system dependent. + // The interval can be changed will running or after the next update. + // WinCE uses the first method, S60 uses the second method. + + // The minimum interval on the symbian emulator is 5000 msecs, which is why the times in + // this test are as high as they are. + + CHECK_SOURCE_VALID; + + QSignalSpy spy(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy timeout(m_source, SIGNAL(updateTimeout())); + m_source->setUpdateInterval(0); + m_source->startUpdates(); + m_source->setUpdateInterval(0); + + QTRY_VERIFY_WITH_TIMEOUT(spy.count() > 0, 7000); + QCOMPARE(timeout.count(), 0); + spy.clear(); + + m_source->setUpdateInterval(5000); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 2) && (timeout.count() == 0), 15000); + spy.clear(); + + m_source->setUpdateInterval(10000); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 2) && (timeout.count() == 0), 30000); + spy.clear(); + + m_source->setUpdateInterval(5000); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 2) && (timeout.count() == 0), 15000); + spy.clear(); + + m_source->setUpdateInterval(5000); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 2) && (timeout.count() == 0), 15000); + spy.clear(); + + m_source->setUpdateInterval(0); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() > 0) && (timeout.count() == 0), 7000); + spy.clear(); + + m_source->setUpdateInterval(0); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() > 0) && (timeout.count() == 0), 7000); + spy.clear(); + + m_source->stopUpdates(); +} + +//TC_ID_3_x_2 +void TestQGeoPositionInfoSource::startUpdates_testDefaultInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spy(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy timeout(m_source, SIGNAL(updateTimeout())); + m_source->startUpdates(); + for (int i = 0; i < 3; i++) { + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() > 0) && (timeout.count() == 0), 7000); + spy.clear(); + } + m_source->stopUpdates(); +} + +//TC_ID_3_x_3 +void TestQGeoPositionInfoSource::startUpdates_testZeroInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spy(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy timeout(m_source, SIGNAL(updateTimeout())); + m_source->setUpdateInterval(0); + m_source->startUpdates(); + for (int i = 0; i < 3; i++) { + QTRY_VERIFY_WITH_TIMEOUT((spy.count() > 0) && (timeout.count() == 0), 7000); + spy.clear(); + } + m_source->stopUpdates(); +} + +void TestQGeoPositionInfoSource::startUpdates_moreThanOnce() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spy(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy timeout(m_source, SIGNAL(updateTimeout())); + m_source->setUpdateInterval(0); + m_source->startUpdates(); + + m_source->startUpdates(); // check there is no crash + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() > 0) && (timeout.count() == 0), 7000); + + m_source->startUpdates(); // check there is no crash + + m_source->stopUpdates(); +} + +//TC_ID_3_x_1 +void TestQGeoPositionInfoSource::stopUpdates() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spy(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy timeout(m_source, SIGNAL(updateTimeout())); + m_source->setUpdateInterval(7000); + m_source->startUpdates(); + for (int i = 0; i < 2; i++) { + QTRY_VERIFY_WITH_TIMEOUT((spy.count() > 0) && (timeout.count() == 0), 9500); + spy.clear(); + } + m_source->stopUpdates(); + QTest::qWait(9500); + QCOMPARE(spy.count(), 0); + spy.clear(); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + m_source->stopUpdates(); + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 0, 9500); +} + +//TC_ID_3_x_2 +void TestQGeoPositionInfoSource::stopUpdates_withoutStart() +{ + CHECK_SOURCE_VALID; + m_source->stopUpdates(); // check there is no crash +} + +void TestQGeoPositionInfoSource::requestUpdate() +{ + CHECK_SOURCE_VALID; + QFETCH(int, timeout); + QSignalSpy spy(m_source, SIGNAL(updateTimeout())); + m_source->requestUpdate(timeout); + QTRY_COMPARE(spy.count(), 1); +} + +void TestQGeoPositionInfoSource::requestUpdate_data() +{ + QTest::addColumn("timeout"); + QTest::newRow("less than zero") << -1; //TC_ID_3_x_7 +} + +// TC_ID_3_x_1 : Create position source and call requestUpdate with valid timeout value +void TestQGeoPositionInfoSource::requestUpdate_validTimeout() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 7000); +} + +void TestQGeoPositionInfoSource::requestUpdate_defaultTimeout() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->requestUpdate(0); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 7000); +} + +// TC_ID_3_x_2 : Create position source and call requestUpdate with a timeout less than +// minimumupdateInterval +void TestQGeoPositionInfoSource::requestUpdate_timeoutLessThanMinimumInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + m_source->requestUpdate(1); + + QTRY_COMPARE_WITH_TIMEOUT(spyTimeout.count(), 1, 1000); +} + +// TC_ID_3_x_3 : Call requestUpdate() with same value repeatedly +void TestQGeoPositionInfoSource::requestUpdate_repeatedCalls() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 7000); + spyUpdate.clear(); + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 7000); +} + +void TestQGeoPositionInfoSource::requestUpdate_overlappingCalls() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->requestUpdate(7000); + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 7000); +} + +//TC_ID_3_x_4 +void TestQGeoPositionInfoSource::requestUpdateAfterStartUpdates_ZeroInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 7000); + spyUpdate.clear(); + + m_source->requestUpdate(7000); + QTest::qWait(7000); + + QVERIFY((spyUpdate.count() > 0) && (spyTimeout.count() == 0)); + spyUpdate.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), MAX_WAITING_TIME); + + m_source->stopUpdates(); +} + +void TestQGeoPositionInfoSource::requestUpdateAfterStartUpdates_SmallInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->setUpdateInterval(10000); + m_source->startUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() == 1) && (spyTimeout.count() == 0), 20000); + spyUpdate.clear(); + + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() == 1) && (spyTimeout.count() == 0), 7000); + spyUpdate.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() == 1) && (spyTimeout.count() == 0), 20000); + + m_source->stopUpdates(); +} + +void TestQGeoPositionInfoSource::requestUpdateBeforeStartUpdates_ZeroInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->requestUpdate(7000); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() >= 2) && (spyTimeout.count() == 0), 14000); + spyUpdate.clear(); + + QTest::qWait(7000); + + QCOMPARE(spyTimeout.count(), 0); + + m_source->stopUpdates(); +} + +void TestQGeoPositionInfoSource::requestUpdateBeforeStartUpdates_SmallInterval() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyUpdate(m_source, SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(m_source, SIGNAL(updateTimeout())); + + m_source->requestUpdate(7000); + + m_source->setUpdateInterval(10000); + m_source->startUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 7000); + spyUpdate.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() > 0) && (spyTimeout.count() == 0), 20000); + + m_source->stopUpdates(); +} + +void TestQGeoPositionInfoSource::removeSlotForRequestTimeout() +{ + CHECK_SOURCE_VALID; + + bool i = connect(m_source, SIGNAL(updateTimeout()), this, SLOT(test_slot1())); + QVERIFY(i == true); + i = connect(m_source, SIGNAL(updateTimeout()), this, SLOT(test_slot2())); + QVERIFY(i == true); + i = disconnect(m_source, SIGNAL(updateTimeout()), this, SLOT(test_slot1())); + QVERIFY(i == true); + + m_source->requestUpdate(-1); + QTRY_VERIFY_WITH_TIMEOUT((m_testSlot2Called == true), 1000); +} + +void TestQGeoPositionInfoSource::removeSlotForPositionUpdated() +{ + CHECK_SOURCE_VALID; + + bool i = connect(m_source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(test_slot1())); + QVERIFY(i == true); + i = connect(m_source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(test_slot2())); + QVERIFY(i == true); + i = disconnect(m_source, SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(test_slot1())); + QVERIFY(i == true); + + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((m_testSlot2Called == true), 7000); +} + +#include "testqgeopositioninfosource.moc" diff --git a/tests/auto/qgeopositioninfosource/testqgeopositioninfosource_p.h b/tests/auto/qgeopositioninfosource/testqgeopositioninfosource_p.h new file mode 100644 index 0000000..7ee23f8 --- /dev/null +++ b/tests/auto/qgeopositioninfosource/testqgeopositioninfosource_p.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTQGEOPOSITIONINFOSOURCE_P_H +#define TESTQGEOPOSITIONINFOSOURCE_P_H + +#include + +#ifdef TST_GEOCLUEMOCK_ENABLED +#include "geocluemock.h" +#include +#endif + +#include +#include + +QT_BEGIN_NAMESPACE +class QGeoPositionInfoSource; +QT_END_NAMESPACE + +class TestQGeoPositionInfoSource : public QObject +{ + Q_OBJECT + +public: + TestQGeoPositionInfoSource(QObject *parent = 0); + + static TestQGeoPositionInfoSource *createDefaultSourceTest(); + +public slots: + void test_slot1(); + void test_slot2(); + +protected: + virtual QGeoPositionInfoSource *createTestSource() = 0; + + // MUST be called by subclasses if they override respective test slots + void base_initTestCase(); + void base_init(); + void base_cleanup(); + void base_cleanupTestCase(); + +private slots: + void initTestCase(); + void init(); + void cleanup(); + void cleanupTestCase(); + + void constructor_withParent(); + + void constructor_noParent(); + + void updateInterval(); + + void setPreferredPositioningMethods(); + void setPreferredPositioningMethods_data(); + + void preferredPositioningMethods(); + + void createDefaultSource(); + + void setUpdateInterval(); + void setUpdateInterval_data(); + + void lastKnownPosition(); + void lastKnownPosition_data(); + + void minimumUpdateInterval(); + + void startUpdates_testIntervals(); + void startUpdates_testIntervalChangesWhileRunning(); + void startUpdates_testDefaultInterval(); + void startUpdates_testZeroInterval(); + void startUpdates_moreThanOnce(); + + void stopUpdates(); + void stopUpdates_withoutStart(); + + void requestUpdate(); + void requestUpdate_data(); + + void requestUpdate_validTimeout(); + void requestUpdate_defaultTimeout(); + void requestUpdate_timeoutLessThanMinimumInterval(); + void requestUpdate_repeatedCalls(); + void requestUpdate_overlappingCalls(); + + void requestUpdateAfterStartUpdates_ZeroInterval(); + void requestUpdateAfterStartUpdates_SmallInterval(); + void requestUpdateBeforeStartUpdates_ZeroInterval(); + void requestUpdateBeforeStartUpdates_SmallInterval(); + + void removeSlotForRequestTimeout(); + void removeSlotForPositionUpdated(); + +private: + QGeoPositionInfoSource *m_source; + bool m_testingDefaultSource; + bool m_testSlot2Called; +}; + +#endif diff --git a/tests/auto/qgeopositioninfosource/tst_qgeopositioninfosource.cpp b/tests/auto/qgeopositioninfosource/tst_qgeopositioninfosource.cpp new file mode 100644 index 0000000..105778e --- /dev/null +++ b/tests/auto/qgeopositioninfosource/tst_qgeopositioninfosource.cpp @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "testqgeopositioninfosource_p.h" + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + TestQGeoPositionInfoSource *test = TestQGeoPositionInfoSource::createDefaultSourceTest(); + int ret = QTest::qExec(test, argc, argv); + delete test; // keep valgrind happy + return ret; +} diff --git a/tests/auto/qgeorectangle/qgeorectangle.pro b/tests/auto/qgeorectangle/qgeorectangle.pro new file mode 100644 index 0000000..9cb635e --- /dev/null +++ b/tests/auto/qgeorectangle/qgeorectangle.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeorectangle + +SOURCES += \ + tst_qgeorectangle.cpp + +QT += positioning testlib diff --git a/tests/auto/qgeorectangle/tst_qgeorectangle.cpp b/tests/auto/qgeorectangle/tst_qgeorectangle.cpp new file mode 100644 index 0000000..01f0104 --- /dev/null +++ b/tests/auto/qgeorectangle/tst_qgeorectangle.cpp @@ -0,0 +1,2376 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoRectangle : public QObject +{ + Q_OBJECT + +private slots: + void default_constructor(); + void center_constructor(); + void corner_constructor(); + void list_constructor(); + void copy_constructor(); + void assignment(); + void destructor(); + + void equality(); + void equality_data(); + + void isValid(); + void isValid_data(); + + void isEmpty(); + void isEmpty_data(); + + void corners(); + void corners_data(); + + void setCorners(); + + void width(); + void width_data(); + + void height(); + void height_data(); + + void center(); + void center_data(); + + void boundingGeoRectangle(); + void boundingGeoRectangle_data(); + + void containsCoord(); + void containsCoord_data(); + + void containsBoxAndIntersects(); + void containsBoxAndIntersects_data(); + + void translate(); + void translate_data(); + + void unite(); + void unite_data(); + + void extendRectangle(); + void extendRectangle_data(); + + void areaComparison(); + void areaComparison_data(); + + void circleComparison(); + void circleComparison_data(); +}; + +void tst_QGeoRectangle::default_constructor() +{ + QGeoRectangle box; + QCOMPARE(box.topLeft().isValid(), false); + QCOMPARE(box.bottomRight().isValid(), false); +} + +void tst_QGeoRectangle::center_constructor() +{ + QGeoRectangle b1 = QGeoRectangle(QGeoCoordinate(5.0, 5.0), 10.0, 10.0); + + QCOMPARE(b1.topLeft(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(b1.bottomRight(), QGeoCoordinate(0.0, 10.0)); +} + +void tst_QGeoRectangle::corner_constructor() +{ + QGeoRectangle b1 = QGeoRectangle(QGeoCoordinate(10.0, 0.0), + QGeoCoordinate(0.0, 10.0)); + + QCOMPARE(b1.topLeft(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(b1.bottomRight(), QGeoCoordinate(0.0, 10.0)); +} + +void tst_QGeoRectangle::list_constructor() +{ + QList coordinates; + QGeoRectangle b1 = QGeoRectangle(coordinates); + QCOMPARE(b1.isValid(), false); + + coordinates << QGeoCoordinate(10.0, 0.0); + b1 = QGeoRectangle(coordinates); + QCOMPARE(b1.isValid(), true); + QCOMPARE(b1.isEmpty(), true); + + coordinates << QGeoCoordinate(0.0, 10.0) << QGeoCoordinate(0.0, 5.0); + b1 = QGeoRectangle(coordinates); + QCOMPARE(b1.topLeft(), QGeoCoordinate(10.0,0.0)); + QCOMPARE(b1.bottomRight(), QGeoCoordinate(0.0, 10.0)); +} + +void tst_QGeoRectangle::copy_constructor() +{ + QGeoRectangle b1 = QGeoRectangle(QGeoCoordinate(10.0, 0.0), + QGeoCoordinate(0.0, 10.0)); + QGeoRectangle b2 = QGeoRectangle(b1); + + QCOMPARE(b2.topLeft(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(b2.bottomRight(), QGeoCoordinate(0.0, 10.0)); + + b2.setTopLeft(QGeoCoordinate(30.0, 0.0)); + b2.setBottomRight(QGeoCoordinate(0.0, 30.0)); + QCOMPARE(b1.topLeft(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(b1.bottomRight(), QGeoCoordinate(0.0, 10.0)); + + QGeoShape area; + QGeoRectangle areaBox(area); + QVERIFY(!areaBox.isValid()); + QVERIFY(areaBox.isEmpty()); + + QGeoCircle circle; + QGeoRectangle circleBox(circle); + QVERIFY(!circleBox.isValid()); + QVERIFY(circleBox.isEmpty()); +} + +void tst_QGeoRectangle::destructor() +{ + QGeoRectangle *box = new QGeoRectangle(); + delete box; + // checking for a crash +} + +void tst_QGeoRectangle::assignment() +{ + QGeoRectangle b1 = QGeoRectangle(QGeoCoordinate(10.0, 0.0), + QGeoCoordinate(0.0, 10.0)); + QGeoRectangle b2 = QGeoRectangle(QGeoCoordinate(20.0, 0.0), + QGeoCoordinate(0.0, 20.0)); + + QVERIFY(b1 != b2); + + b2 = b1; + QCOMPARE(b2.topLeft(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(b2.bottomRight(), QGeoCoordinate(0.0, 10.0)); + QCOMPARE(b1, b2); + + b2.setTopLeft(QGeoCoordinate(30.0, 0.0)); + b2.setBottomRight(QGeoCoordinate(0.0, 30.0)); + QCOMPARE(b1.topLeft(), QGeoCoordinate(10.0, 0.0)); + QCOMPARE(b1.bottomRight(), QGeoCoordinate(0.0, 10.0)); + + // Assign b1 to an area + QGeoShape area = b1; + QCOMPARE(area.type(), b1.type()); + QVERIFY(area == b1); + + // Assign the area back to a bounding box + QGeoRectangle ba = area; + QCOMPARE(ba.topLeft(), b1.topLeft()); + QCOMPARE(ba.bottomRight(), b1.bottomRight()); + + // Check that the copy is not modified when modifying the original. + b1.setTopLeft(QGeoCoordinate(80, 30)); + QVERIFY(ba.topLeft() != b1.topLeft()); + QVERIFY(ba != b1); +} + +void tst_QGeoRectangle::equality() +{ + QFETCH(QGeoRectangle, box1); + QFETCH(QGeoRectangle, box2); + QFETCH(QGeoShape, area1); + QFETCH(QGeoShape, area2); + QFETCH(bool, equal); + + // compare boxes + QCOMPARE((box1 == box2), equal); + QCOMPARE((box1 != box2), !equal); + + // compare areas + QCOMPARE((area1 == area2), equal); + QCOMPARE((area1 != area2), !equal); + + // compare area to box + QCOMPARE((area1 == box2), equal); + QCOMPARE((area1 != box2), !equal); + + // compare box to area + QCOMPARE((box1 == area2), equal); + QCOMPARE((box1 != area2), !equal); +} + +void tst_QGeoRectangle::equality_data() +{ + QTest::addColumn("box1"); + QTest::addColumn("box2"); + QTest::addColumn("area1"); + QTest::addColumn("area2"); + QTest::addColumn("equal"); + + QGeoCoordinate c1(10, 5); + QGeoCoordinate c2(5, 10); + QGeoCoordinate c3(20, 15); + QGeoCoordinate c4(15, 20); + + QGeoRectangle b1(c1, c2); + QGeoRectangle b2(c3, c4); + QGeoRectangle b3(c3, c2); + QGeoRectangle b4(c1, c3); + QGeoRectangle b5(c1, c2); + + QGeoShape a1(b1); + QGeoShape a2(b2); + QGeoShape a3(b3); + QGeoShape a4(b4); + QGeoShape a5(b5); + + QTest::newRow("all unequal") + << b1 << b2 << a1 << a2 << false; + QTest::newRow("top left unequal") + << b1 << b3 << a1 << a3 << false; + QTest::newRow("bottom right unequal") + << b1 << b4 << a1 << a4 << false; + QTest::newRow("equal") + << b1 << b5 << a1 << a5 << true; +} + +void tst_QGeoRectangle::isValid() +{ + QFETCH(QGeoRectangle, input); + QFETCH(bool, valid); + + QCOMPARE(input.isValid(), valid); + + QGeoShape area = input; + QCOMPARE(area.isValid(), valid); +} + +void tst_QGeoRectangle::isValid_data() +{ + QTest::addColumn("input"); + QTest::addColumn("valid"); + + QGeoCoordinate c0; + QGeoCoordinate c1(10, 5); + QGeoCoordinate c2(5, 10); + + QTest::newRow("both corners invalid") + << QGeoRectangle(c0, c0) << false; + QTest::newRow("top left corner invalid") + << QGeoRectangle(c0, c2) << false; + QTest::newRow("bottom right corner invalid") + << QGeoRectangle(c1, c0) << false; + QTest::newRow("height in wrong order") + << QGeoRectangle(c2, c1) << false; + QTest::newRow("both corners valid") + << QGeoRectangle(c1, c2) << true; +} + +void tst_QGeoRectangle::isEmpty() +{ + QFETCH(QGeoRectangle, input); + QFETCH(bool, empty); + + QCOMPARE(input.isEmpty(), empty); + + QGeoShape area = input; + QCOMPARE(area.isEmpty(), empty); +} + +void tst_QGeoRectangle::isEmpty_data() +{ + QTest::addColumn("input"); + QTest::addColumn("empty"); + + QGeoCoordinate c0; + QGeoCoordinate c1(10, 5); + QGeoCoordinate c2(5, 10); + QGeoCoordinate c3(10, 10); + + QTest::newRow("both corners invalid") + << QGeoRectangle(c0, c0) << true; + QTest::newRow("top left corner invalid") + << QGeoRectangle(c0, c2) << true; + QTest::newRow("bottom right corner invalid") + << QGeoRectangle(c1, c0) << true; + QTest::newRow("zero width") + << QGeoRectangle(c1, c3) << true; + QTest::newRow("zero height") + << QGeoRectangle(c3, c2) << true; + QTest::newRow("zero width and height") + << QGeoRectangle(c1, c1) << true; + QTest::newRow("non-zero width and height") + << QGeoRectangle(c1, c2) << false; +} + +void tst_QGeoRectangle::corners() +{ + QFETCH(QGeoRectangle, box); + QFETCH(QGeoCoordinate, topLeft); + QFETCH(QGeoCoordinate, topRight); + QFETCH(QGeoCoordinate, bottomLeft); + QFETCH(QGeoCoordinate, bottomRight); + + QCOMPARE(box.topLeft(), topLeft); + QCOMPARE(box.topRight(), topRight); + QCOMPARE(box.bottomLeft(), bottomLeft); + QCOMPARE(box.bottomRight(), bottomRight); +} + +void tst_QGeoRectangle::corners_data() +{ + QTest::addColumn("box"); + QTest::addColumn("topLeft"); + QTest::addColumn("topRight"); + QTest::addColumn("bottomLeft"); + QTest::addColumn("bottomRight"); + + QGeoCoordinate c0; + QGeoCoordinate tl(10, 5); + QGeoCoordinate br(5, 10); + QGeoCoordinate tr(10, 10); + QGeoCoordinate bl(5, 5); + + QTest::newRow("both invalid") + << QGeoRectangle(c0, c0) + << c0 + << c0 + << c0 + << c0; + QTest::newRow("top left invalid") + << QGeoRectangle(c0, br) + << c0 + << c0 + << c0 + << br; + QTest::newRow("bottom right invalid") + << QGeoRectangle(tl, c0) + << tl + << c0 + << c0 + << c0; + QTest::newRow("both valid") + << QGeoRectangle(tl, br) + << tl + << tr + << bl + << br; +} + +void tst_QGeoRectangle::setCorners() +{ + QGeoRectangle box(QGeoCoordinate(10.0, 0.0), + QGeoCoordinate(0.0, 10.0)); + + box.setTopLeft(QGeoCoordinate(20.0, -10.0)); + + QCOMPARE(box.topLeft(), QGeoCoordinate(20.0, -10.0)); + QCOMPARE(box.topRight(), QGeoCoordinate(20.0, 10.0)); + QCOMPARE(box.bottomLeft(), QGeoCoordinate(0.0, -10.0)); + QCOMPARE(box.bottomRight(), QGeoCoordinate(0.0, 10.0)); + + box.setTopRight(QGeoCoordinate(30.0, 20.0)); + + QCOMPARE(box.topLeft(), QGeoCoordinate(30.0, -10.0)); + QCOMPARE(box.topRight(), QGeoCoordinate(30.0, 20.0)); + QCOMPARE(box.bottomLeft(), QGeoCoordinate(0.0, -10.0)); + QCOMPARE(box.bottomRight(), QGeoCoordinate(0.0, 20.0)); + + box.setBottomRight(QGeoCoordinate(-10.0, 30.0)); + + QCOMPARE(box.topLeft(), QGeoCoordinate(30.0, -10.0)); + QCOMPARE(box.topRight(), QGeoCoordinate(30.0, 30.0)); + QCOMPARE(box.bottomLeft(), QGeoCoordinate(-10.0, -10.0)); + QCOMPARE(box.bottomRight(), QGeoCoordinate(-10.0, 30.0)); + + box.setBottomLeft(QGeoCoordinate(-20.0, -20.0)); + + QCOMPARE(box.topLeft(), QGeoCoordinate(30.0, -20.0)); + QCOMPARE(box.topRight(), QGeoCoordinate(30.0, 30.0)); + QCOMPARE(box.bottomLeft(), QGeoCoordinate(-20.0, -20.0)); + QCOMPARE(box.bottomRight(), QGeoCoordinate(-20.0, 30.0)); + + +} + +void tst_QGeoRectangle::width() +{ + QFETCH(QGeoRectangle, box); + QFETCH(double, oldWidth); + QFETCH(double, newWidth); + QFETCH(QGeoRectangle, newBox); + + if (qIsNaN(oldWidth)) + QVERIFY(qIsNaN(box.width())); + else + QCOMPARE(box.width(), oldWidth); + + box.setWidth(newWidth); + + QCOMPARE(box, newBox); +} + +void tst_QGeoRectangle::width_data() +{ + QTest::addColumn("box"); + QTest::addColumn("oldWidth"); + QTest::addColumn("newWidth"); + QTest::addColumn("newBox"); + + QTest::newRow("invalid box") + << QGeoRectangle() + << qQNaN() + << 100.0 + << QGeoRectangle(); + + QTest::newRow("0 width -> negative width") + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)) + << 0.0 + << -1.0 + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)); + + QTest::newRow("0 width -> 0 width") + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)) + << 0.0 + << 0.0 + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)); + + QTest::newRow("0 width -> non wrapping width") + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)) + << 0.0 + << 10.0 + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)); + + QTest::newRow("0 width -> wrapping width positive") + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)) + << 0.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(10.0, -5.0), + QGeoCoordinate(5.0, -175.0)); + + QTest::newRow("0 width -> wrapping width negative") + << QGeoRectangle(QGeoCoordinate(10.0, -90.0), + QGeoCoordinate(5.0, -90.0)) + << 0.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, 5.0)); + + QTest::newRow("0 width -> 360 width") + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)) + << 0.0 + << 360.0 + << QGeoRectangle(QGeoCoordinate(10.0, -180.0), + QGeoCoordinate(5.0, 180.0)); + + QTest::newRow("0 width -> 360+ width") + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)) + << 0.0 + << 370.0 + << QGeoRectangle(QGeoCoordinate(10.0, -180.0), + QGeoCoordinate(5.0, 180.0)); + + QTest::newRow("non wrapping width -> negative width") + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)) + << 10.0 + << -1.0 + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)); + + QTest::newRow("non wrapping width -> 0 width") + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)) + << 10.0 + << 0.0 + << QGeoRectangle(QGeoCoordinate(10.0, 90.0), + QGeoCoordinate(5.0, 90.0)); + + QTest::newRow("non wrapping width -> non wrapping width") + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)) + << 10.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(10.0, 80.0), + QGeoCoordinate(5.0, 100.0)); + + QTest::newRow("non wrapping width -> wrapping width positive") + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)) + << 10.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(10.0, -5.0), + QGeoCoordinate(5.0, -175.0)); + + QTest::newRow("non wrapping width -> wrapping width negative") + << QGeoRectangle(QGeoCoordinate(10.0, -95.0), + QGeoCoordinate(5.0, -85.0)) + << 10.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, 5.0)); + + QTest::newRow("non wrapping width -> 360 width") + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)) + << 10.0 + << 360.0 + << QGeoRectangle(QGeoCoordinate(10.0, -180.0), + QGeoCoordinate(5.0, 180.0)); + + QTest::newRow("non wrapping width width -> 360+ width") + << QGeoRectangle(QGeoCoordinate(10.0, 85.0), + QGeoCoordinate(5.0, 95.0)) + << 10.0 + << 370.0 + << QGeoRectangle(QGeoCoordinate(10.0, -180.0), + QGeoCoordinate(5.0, 180.0)); + + QTest::newRow("wrapping width -> negative width") + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, -85.0)) + << 100.0 + << -1.0 + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, -85.0)); + + QTest::newRow("wrapping width -> 0 width") + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, -85.0)) + << 100.0 + << 0.0 + << QGeoRectangle(QGeoCoordinate(10.0, -135.0), + QGeoCoordinate(5.0, -135.0)); + + QTest::newRow("wrapping width -> non wrapping width") + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, -85.0)) + << 100.0 + << 80.0 + << QGeoRectangle(QGeoCoordinate(10.0, -175.0), + QGeoCoordinate(5.0, -95.0)); + + QTest::newRow("wrapping width -> wrapping width") + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, -85.0)) + << 100.0 + << 120.0 + << QGeoRectangle(QGeoCoordinate(10.0, 165.0), + QGeoCoordinate(5.0, -75.0)); + + QTest::newRow("wrapping width -> 360 width") + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, -85.0)) + << 100.0 + << 360.0 + << QGeoRectangle(QGeoCoordinate(10.0, -180.0), + QGeoCoordinate(5.0, 180.0)); + + QTest::newRow("wrapping width width -> 360+ width") + << QGeoRectangle(QGeoCoordinate(10.0, 175.0), + QGeoCoordinate(5.0, -85.0)) + << 100.0 + << 370.0 + << QGeoRectangle(QGeoCoordinate(10.0, -180.0), + QGeoCoordinate(5.0, 180.0)); +} + +void tst_QGeoRectangle::height() +{ + QFETCH(QGeoRectangle, box); + QFETCH(double, oldHeight); + QFETCH(double, newHeight); + QFETCH(QGeoRectangle, newBox); + + if (qIsNaN(oldHeight)) + QVERIFY(qIsNaN(box.height())); + else + QCOMPARE(box.height(), oldHeight); + + box.setHeight(newHeight); + QCOMPARE(box, newBox); +} + +void tst_QGeoRectangle::height_data() +{ + QTest::addColumn("box"); + QTest::addColumn("oldHeight"); + QTest::addColumn("newHeight"); + QTest::addColumn("newBox"); + + QTest::newRow("invalid box") + << QGeoRectangle() + << qQNaN() + << 100.0 + << QGeoRectangle(); + + QTest::newRow("0 height -> negative height") + << QGeoRectangle(QGeoCoordinate(10.0, 5.0), + QGeoCoordinate(10.0, 10.0)) + << 0.0 + << -1.0 + << QGeoRectangle(QGeoCoordinate(10.0, 5.0), + QGeoCoordinate(10.0, 10.0)); + + QTest::newRow("0 height -> 0 height") + << QGeoRectangle(QGeoCoordinate(10.0, 5.0), + QGeoCoordinate(10.0, 10.0)) + << 0.0 + << 0.0 + << QGeoRectangle(QGeoCoordinate(10.0, 5.0), + QGeoCoordinate(10.0, 10.0)); + + QTest::newRow("0 height -> non zero height") + << QGeoRectangle(QGeoCoordinate(10.0, 5.0), + QGeoCoordinate(10.0, 10.0)) + << 0.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(20.0, 5.0), + QGeoCoordinate(0.0, 10.0)); + + QTest::newRow("0 height -> squash top") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(70.0, 70.0)) + << 0.0 + << 60.0 + << QGeoRectangle(QGeoCoordinate(90.0, 30.0), + QGeoCoordinate(50.0, 70.0)); + + QTest::newRow("0 height -> squash bottom") + << QGeoRectangle(QGeoCoordinate(-70.0, 30.0), + QGeoCoordinate(-70.0, 70.0)) + << 0.0 + << 60.0 + << QGeoRectangle(QGeoCoordinate(-50.0, 30.0), + QGeoCoordinate(-90.0, 70.0)); + + QTest::newRow("0 height -> 180") + << QGeoRectangle(QGeoCoordinate(0.0, 5.0), + QGeoCoordinate(0.0, 10.0)) + << 0.0 + << 180.0 + << QGeoRectangle(QGeoCoordinate(90.0, 5.0), + QGeoCoordinate(-90.0, 10.0)); + + QTest::newRow("0 height -> 180 squash top") + << QGeoRectangle(QGeoCoordinate(20.0, 5.0), + QGeoCoordinate(20.0, 10.0)) + << 0.0 + << 180.0 + << QGeoRectangle(QGeoCoordinate(90.0, 5.0), + QGeoCoordinate(-50.0, 10.0)); + + QTest::newRow("0 height -> 180 squash bottom") + << QGeoRectangle(QGeoCoordinate(-20.0, 5.0), + QGeoCoordinate(-20.0, 10.0)) + << 0.0 + << 180.0 + << QGeoRectangle(QGeoCoordinate(50.0, 5.0), + QGeoCoordinate(-90.0, 10.0)); + + QTest::newRow("0 height -> 180+") + << QGeoRectangle(QGeoCoordinate(0.0, 5.0), + QGeoCoordinate(0.0, 10.0)) + << 0.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(90.0, 5.0), + QGeoCoordinate(-90.0, 10.0)); + + QTest::newRow("0 height -> 180+ squash top") + << QGeoRectangle(QGeoCoordinate(20.0, 5.0), + QGeoCoordinate(20.0, 10.0)) + << 0.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(90.0, 5.0), + QGeoCoordinate(-50.0, 10.0)); + + QTest::newRow("0 height -> 180+ squash bottom") + << QGeoRectangle(QGeoCoordinate(-20.0, 5.0), + QGeoCoordinate(-20.0, 10.0)) + << 0.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(50.0, 5.0), + QGeoCoordinate(-90.0, 10.0)); + + QTest::newRow("non zero height -> negative height") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << 40.0 + << -1.0 + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)); + + QTest::newRow("non zero height -> 0 height") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << 40.0 + << 0.0 + << QGeoRectangle(QGeoCoordinate(50.0, 30.0), + QGeoCoordinate(50.0, 70.0)); + + QTest::newRow("non zero height -> non zero height") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << 40.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(60.0, 30.0), + QGeoCoordinate(40.0, 70.0)); + + QTest::newRow("non zero height -> squash top") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << 40.0 + << 100.0 + << QGeoRectangle(QGeoCoordinate(90.0, 30.0), + QGeoCoordinate(10.0, 70.0)); + + QTest::newRow("non zero height -> squash bottom") + << QGeoRectangle(QGeoCoordinate(-30.0, 30.0), + QGeoCoordinate(-70.0, 70.0)) + << 40.0 + << 100.0 + << QGeoRectangle(QGeoCoordinate(-10.0, 30.0), + QGeoCoordinate(-90.0, 70.0)); + + QTest::newRow("non zero height -> 180") + << QGeoRectangle(QGeoCoordinate(20.0, 30.0), + QGeoCoordinate(-20.0, 70.0)) + << 40.0 + << 180.0 + << QGeoRectangle(QGeoCoordinate(90.0, 30.0), + QGeoCoordinate(-90.0, 70.0)); + + QTest::newRow("non zero height -> 180 squash top") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << 40.0 + << 180.0 + << QGeoRectangle(QGeoCoordinate(90.0, 30.0), + QGeoCoordinate(10.0, 70.0)); + + QTest::newRow("non zero height -> 180 squash bottom") + << QGeoRectangle(QGeoCoordinate(-30.0, 30.0), + QGeoCoordinate(-70.0, 70.0)) + << 40.0 + << 180.0 + << QGeoRectangle(QGeoCoordinate(-10.0, 30.0), + QGeoCoordinate(-90.0, 70.0)); + + QTest::newRow("non zero height -> 180+") + << QGeoRectangle(QGeoCoordinate(20.0, 30.0), + QGeoCoordinate(-20.0, 70.0)) + << 40.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(90.0, 30.0), + QGeoCoordinate(-90.0, 70.0)); + + QTest::newRow("non zero height -> 180+ squash top") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << 40.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(90.0, 30.0), + QGeoCoordinate(10.0, 70.0)); + + QTest::newRow("non zero height -> 180+ squash bottom") + << QGeoRectangle(QGeoCoordinate(-30.0, 30.0), + QGeoCoordinate(-70.0, 70.0)) + << 40.0 + << 190.0 + << QGeoRectangle(QGeoCoordinate(-10.0, 30.0), + QGeoCoordinate(-90.0, 70.0)); +} + +void tst_QGeoRectangle::center() +{ + QFETCH(QGeoRectangle, box); + QFETCH(QGeoCoordinate, oldCenter); + QFETCH(QGeoCoordinate, newCenter); + QFETCH(QGeoRectangle, newBox); + + QGeoShape shape = box; + QCOMPARE(box.center(), oldCenter); + QCOMPARE(shape.center(), oldCenter); + box.setCenter(newCenter); + QCOMPARE(box, newBox); +} + +void tst_QGeoRectangle::center_data() +{ + QTest::addColumn("box"); + QTest::addColumn("oldCenter"); + QTest::addColumn("newCenter"); + QTest::addColumn("newBox"); + + QTest::newRow("invalid") + << QGeoRectangle() + << QGeoCoordinate() + << QGeoCoordinate(0.0, 0.0) + << QGeoRectangle(QGeoCoordinate(0.0, 0.0), 0.0, 0.0); + + QTest::newRow("zero width") + << QGeoRectangle(QGeoCoordinate(10.0, 5.0), + QGeoCoordinate(5.0, 5.0)) + << QGeoCoordinate(7.5, 5.0) + << QGeoCoordinate(20.0, 20.0) + << QGeoRectangle(QGeoCoordinate(22.5, 20.0), + QGeoCoordinate(17.5, 20.0)); + + QTest::newRow("360 width") + << QGeoRectangle(QGeoCoordinate(10.0, -180.0), + QGeoCoordinate(5.0, 180.0)) + << QGeoCoordinate(7.5, 0.0) + << QGeoCoordinate(20.0, 20.0) + << QGeoRectangle(QGeoCoordinate(22.5, -180.0), + QGeoCoordinate(17.5, 180.0)); + + QTest::newRow("zero height") + << QGeoRectangle(QGeoCoordinate(5.0, 5.0), + QGeoCoordinate(5.0, 10.0)) + << QGeoCoordinate(5.0, 7.5) + << QGeoCoordinate(20.0, 20.0) + << QGeoRectangle(QGeoCoordinate(20.0, 17.5), + QGeoCoordinate(20.0, 22.5)); + + QTest::newRow("180 height -> move") + << QGeoRectangle(QGeoCoordinate(90.0, 5.0), + QGeoCoordinate(-90.0, 10.0)) + << QGeoCoordinate(0.0, 7.5) + << QGeoCoordinate(0.0, 20.0) + << QGeoRectangle(QGeoCoordinate(90.0, 17.5), + QGeoCoordinate(-90.0, 22.5)); + + QTest::newRow("180 height -> squash top") + << QGeoRectangle(QGeoCoordinate(90.0, 5.0), + QGeoCoordinate(-90.0, 10.0)) + << QGeoCoordinate(0.0, 7.5) + << QGeoCoordinate(-20.0, 20.0) + << QGeoRectangle(QGeoCoordinate(50.0, 17.5), + QGeoCoordinate(-90.0, 22.5)); + + QTest::newRow("180 height -> squash bottom") + << QGeoRectangle(QGeoCoordinate(90.0, 5.0), + QGeoCoordinate(-90.0, 10.0)) + << QGeoCoordinate(0.0, 7.5) + << QGeoCoordinate(20.0, 20.0) + << QGeoRectangle(QGeoCoordinate(90.0, 17.5), + QGeoCoordinate(-50.0, 22.5)); + + QTest::newRow("non wrapping -> non wrapping") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << QGeoCoordinate(50.0, 50.0) + << QGeoCoordinate(10.0, 10.0) + << QGeoRectangle(QGeoCoordinate(30.0, -10.0), + QGeoCoordinate(-10.0, 30.0)); + + QTest::newRow("non wrapping -> wrapping") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << QGeoCoordinate(50.0, 50.0) + << QGeoCoordinate(10.0, 170.0) + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-10.0, -170.0)); + + QTest::newRow("non wrapping -> squash top") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << QGeoCoordinate(50.0, 50.0) + << QGeoCoordinate(80.0, 50.0) + << QGeoRectangle(QGeoCoordinate(90.0, 30.0), + QGeoCoordinate(70.0, 70.0)); + + QTest::newRow("non wrapping -> squash bottom") + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)) + << QGeoCoordinate(50.0, 50.0) + << QGeoCoordinate(-80.0, 50.0) + << QGeoRectangle(QGeoCoordinate(-70.0, 30.0), + QGeoCoordinate(-90.0, 70.0)); + + QTest::newRow("wrapping -> non wrapping") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-10.0, -170.0)) + << QGeoCoordinate(10.0, 170.0) + << QGeoCoordinate(50.0, 50.0) + << QGeoRectangle(QGeoCoordinate(70.0, 30.0), + QGeoCoordinate(30.0, 70.0)); + + QTest::newRow("wrapping -> wrapping") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-10.0, -170.0)) + << QGeoCoordinate(10.0, 170.0) + << QGeoCoordinate(10.0, -170.0) + << QGeoRectangle(QGeoCoordinate(30.0, 170.0), + QGeoCoordinate(-10.0, -150.0)); + + QTest::newRow("wrapping -> squash top") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-10.0, -170.0)) + << QGeoCoordinate(10.0, 170.0) + << QGeoCoordinate(80.0, 170.0) + << QGeoRectangle(QGeoCoordinate(90.0, 150.0), + QGeoCoordinate(70.0, -170.0)); + + QTest::newRow("wrapping -> squash bottom") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-10.0, -170.0)) + << QGeoCoordinate(10.0, 170.0) + << QGeoCoordinate(-80.0, 170.0) + << QGeoRectangle(QGeoCoordinate(-70.0, 150.0), + QGeoCoordinate(-90.0, -170.0)); +} + +void tst_QGeoRectangle::boundingGeoRectangle_data() +{ + QTest::addColumn("rectangle"); + + QGeoRectangle b1(QGeoCoordinate(70, 30), QGeoCoordinate(30, 70)); + QGeoRectangle b2(QGeoCoordinate(70, 150), QGeoCoordinate(30, -170)); + QGeoRectangle b3(QGeoCoordinate(90, 30), QGeoCoordinate(50, 70)); + QGeoRectangle b4(QGeoCoordinate(-50, 30), QGeoCoordinate(-90, 70)); + + QTest::newRow("Box 1") << b1; + QTest::newRow("Box 2") << b2; + QTest::newRow("Box 3") << b3; + QTest::newRow("Box 4") << b4; +} + +void tst_QGeoRectangle::boundingGeoRectangle() +{ + QFETCH(QGeoRectangle, rectangle); + + QGeoRectangle box = rectangle.boundingGeoRectangle(); + QCOMPARE(box, rectangle); +} + +void tst_QGeoRectangle::containsCoord() +{ + QFETCH(QGeoRectangle, box); + QFETCH(QGeoCoordinate, coord); + QFETCH(bool, contains); + + QCOMPARE(box.contains(coord), contains); + + QGeoShape area = box; + QCOMPARE(area.contains(coord), contains); +} + +void tst_QGeoRectangle::containsCoord_data() +{ + QTest::addColumn("box"); + QTest::addColumn("coord"); + QTest::addColumn("contains"); + + QGeoRectangle b1(QGeoCoordinate(70, 30), QGeoCoordinate(30, 70)); + + double lonLO1 = 20.0; + double lonL1 = 30.0; + double lonLI1 = 40.0; + double lonC1 = 50.0; + double lonRI1 = 60.0; + double lonR1 = 70.0; + double lonRO1 = 80.0; + + double latTO1 = 80.0; + double latT1 = 70.0; + double latTI1 = 60.0; + double latC1 = 50.0; + double latBI1 = 40.0; + double latB1 = 30.0; + double latBO1 = 20.0; + + QTest::newRow("non wrapped - in center") + << b1 << QGeoCoordinate(latC1, lonC1) << true; + QTest::newRow("non wrapped - left edge - inside") + << b1 << QGeoCoordinate(latC1, lonLI1) << true; + QTest::newRow("non wrapped - left edge") + << b1 << QGeoCoordinate(latC1, lonL1) << true; + QTest::newRow("non wrapped - left edge - outside") + << b1 << QGeoCoordinate(latC1, lonLO1) << false; + QTest::newRow("non wrapped - right edge - inside") + << b1 << QGeoCoordinate(latC1, lonRI1) << true; + QTest::newRow("non wrapped - right edge") + << b1 << QGeoCoordinate(latC1, lonR1) << true; + QTest::newRow("non wrapped - right edge - outside") + << b1 << QGeoCoordinate(latC1, lonRO1) << false; + QTest::newRow("non wrapped - top edge - inside") + << b1 << QGeoCoordinate(latTI1, lonC1) << true; + QTest::newRow("non wrapped - top edge") + << b1 << QGeoCoordinate(latT1, lonC1) << true; + QTest::newRow("non wrapped - top edge - outside") + << b1 << QGeoCoordinate(latTO1, lonC1) << false; + QTest::newRow("non wrapped - bottom edge - inside") + << b1 << QGeoCoordinate(latBI1, lonC1) << true; + QTest::newRow("non wrapped - bottom edge") + << b1 << QGeoCoordinate(latB1, lonC1) << true; + QTest::newRow("non wrapped - bottom edge - outside") + << b1 << QGeoCoordinate(latBO1, lonC1) << false; + QTest::newRow("non wrapped - top left - inside") + << b1 << QGeoCoordinate(latTI1, lonLI1) << true; + QTest::newRow("non wrapped - top left") + << b1 << QGeoCoordinate(latT1, lonL1) << true; + QTest::newRow("non wrapped - top left - outside") + << b1 << QGeoCoordinate(latTO1, lonLO1) << false; + QTest::newRow("non wrapped - top right - inside") + << b1 << QGeoCoordinate(latTI1, lonRI1) << true; + QTest::newRow("non wrapped - top right") + << b1 << QGeoCoordinate(latT1, lonR1) << true; + QTest::newRow("non wrapped - top right - outside") + << b1 << QGeoCoordinate(latTO1, lonRO1) << false; + QTest::newRow("non wrapped - bottom left - inside") + << b1 << QGeoCoordinate(latBI1, lonLI1) << true; + QTest::newRow("non wrapped - bottom left") + << b1 << QGeoCoordinate(latB1, lonL1) << true; + QTest::newRow("non wrapped - bottom left - outside") + << b1 << QGeoCoordinate(latBO1, lonLO1) << false; + QTest::newRow("non wrapped - bottom right - inside") + << b1 << QGeoCoordinate(latBI1, lonRI1) << true; + QTest::newRow("non wrapped - bottom right") + << b1 << QGeoCoordinate(latB1, lonR1) << true; + QTest::newRow("non wrapped - bottom right - outside") + << b1 << QGeoCoordinate(latBO1, lonRO1) << false; + + QGeoRectangle b2(QGeoCoordinate(70, 150), QGeoCoordinate(30, -170)); + + double lonLO2 = 140.0; + double lonL2 = 150.0; + double lonLI2 = 160.0; + double lonC2 = 170.0; + double lonRI2 = 180.0; + double lonR2 = -170.0; + double lonRO2 = -160.0; + + double latTO2 = 80.0; + double latT2 = 70.0; + double latTI2 = 60.0; + double latC2 = 50.0; + double latBI2 = 40.0; + double latB2 = 30.0; + double latBO2 = 20.0; + + QTest::newRow("wrapped - in center") + << b2 << QGeoCoordinate(latC2, lonC2) << true; + QTest::newRow("wrapped - left edge - inside") + << b2 << QGeoCoordinate(latC2, lonLI2) << true; + QTest::newRow("wrapped - left edge") + << b2 << QGeoCoordinate(latC2, lonL2) << true; + QTest::newRow("wrapped - left edge - outside") + << b2 << QGeoCoordinate(latC2, lonLO2) << false; + QTest::newRow("wrapped - right edge - inside") + << b2 << QGeoCoordinate(latC2, lonRI2) << true; + QTest::newRow("wrapped - right edge") + << b2 << QGeoCoordinate(latC2, lonR2) << true; + QTest::newRow("wrapped - right edge - outside") + << b2 << QGeoCoordinate(latC2, lonRO2) << false; + QTest::newRow("wrapped - top edge - inside") + << b2 << QGeoCoordinate(latTI2, lonC2) << true; + QTest::newRow("wrapped - top edge") + << b2 << QGeoCoordinate(latT2, lonC2) << true; + QTest::newRow("wrapped - top edge - outside") + << b2 << QGeoCoordinate(latTO2, lonC2) << false; + QTest::newRow("wrapped - bottom edge - inside") + << b2 << QGeoCoordinate(latBI2, lonC2) << true; + QTest::newRow("wrapped - bottom edge") + << b2 << QGeoCoordinate(latB2, lonC2) << true; + QTest::newRow("wrapped - bottom edge - outside") + << b2 << QGeoCoordinate(latBO2, lonC2) << false; + QTest::newRow("wrapped - top left - inside") + << b2 << QGeoCoordinate(latTI2, lonLI2) << true; + QTest::newRow("wrapped - top left") + << b2 << QGeoCoordinate(latT2, lonL2) << true; + QTest::newRow("wrapped - top left - outside") + << b2 << QGeoCoordinate(latTO2, lonLO2) << false; + QTest::newRow("wrapped - top right - inside") + << b2 << QGeoCoordinate(latTI2, lonRI2) << true; + QTest::newRow("wrapped - top right") + << b2 << QGeoCoordinate(latT2, lonR2) << true; + QTest::newRow("wrapped - top right - outside") + << b2 << QGeoCoordinate(latTO2, lonRO2) << false; + QTest::newRow("wrapped - bottom left - inside") + << b2 << QGeoCoordinate(latBI2, lonLI2) << true; + QTest::newRow("wrapped - bottom left") + << b2 << QGeoCoordinate(latB2, lonL2) << true; + QTest::newRow("wrapped - bottom left - outside") + << b2 << QGeoCoordinate(latBO2, lonLO2) << false; + QTest::newRow("wrapped - bottom right - inside") + << b2 << QGeoCoordinate(latBI2, lonRI2) << true; + QTest::newRow("wrapped - bottom right") + << b2 << QGeoCoordinate(latB2, lonR2) << true; + QTest::newRow("wrapped - bottom right - outside") + << b2 << QGeoCoordinate(latBO2, lonRO2) << false; + + QGeoRectangle b3(QGeoCoordinate(90, 30), QGeoCoordinate(50, 70)); + + double lonLO3 = 20.0; + double lonL3 = 30.0; + double lonLI3 = 40.0; + double lonC3 = 50.0; + double lonRI3 = 60.0; + double lonR3 = 70.0; + double lonRO3 = 80.0; + + double latT3 = 90.0; + double latTI3 = 80.0; + double latC3 = 70.0; + /* current unused: + double latBI3 = 60.0; + double latB3 = 50.0; + double latBO3 = 40.0; + */ + + QTest::newRow("north pole - in center") + << b3 << QGeoCoordinate(latC3, lonC3) << true; + QTest::newRow("north pole - left edge - inside") + << b3 << QGeoCoordinate(latC3, lonLI3) << true; + QTest::newRow("north pole - left edge") + << b3 << QGeoCoordinate(latC3, lonL3) << true; + QTest::newRow("north pole - left edge - outside") + << b3 << QGeoCoordinate(latC3, lonLO3) << false; + QTest::newRow("north pole - right edge - inside") + << b3 << QGeoCoordinate(latC3, lonRI3) << true; + QTest::newRow("north pole - right edge") + << b3 << QGeoCoordinate(latC3, lonR3) << true; + QTest::newRow("north pole - right edge - outside") + << b3 << QGeoCoordinate(latC3, lonRO3) << false; + QTest::newRow("north pole - top edge - inside") + << b3 << QGeoCoordinate(latTI3, lonC3) << true; + QTest::newRow("north pole - top edge") + << b3 << QGeoCoordinate(latT3, lonC3) << true; + QTest::newRow("north pole - top left - inside") + << b3 << QGeoCoordinate(latTI3, lonLI3) << true; + QTest::newRow("north pole - top left") + << b3 << QGeoCoordinate(latT3, lonL3) << true; + QTest::newRow("north pole - top left - outside") + << b3 << QGeoCoordinate(latT3, lonLO3) << true; + QTest::newRow("north pole - top right - inside") + << b3 << QGeoCoordinate(latTI3, lonRI3) << true; + QTest::newRow("north pole - top right") + << b3 << QGeoCoordinate(latT3, lonR3) << true; + QTest::newRow("north pole - top right - outside") + << b3 << QGeoCoordinate(latT3, lonRO3) << true; + + QGeoRectangle b4(QGeoCoordinate(-50, 30), QGeoCoordinate(-90, 70)); + + double lonLO4 = 20.0; + double lonL4 = 30.0; + double lonLI4 = 40.0; + double lonC4 = 50.0; + double lonRI4 = 60.0; + double lonR4 = 70.0; + double lonRO4 = 80.0; + + /* currently unused: + double latTO4 = -40.0; + double latT4 = -50.0; + double latTI4 = -60.0; + */ + double latC4 = -70.0; + double latBI4 = -80.0; + double latB4 = -90.0; + + QTest::newRow("south pole - in center") + << b4 << QGeoCoordinate(latC4, lonC4) << true; + QTest::newRow("south pole - left edge - inside") + << b4 << QGeoCoordinate(latC4, lonLI4) << true; + QTest::newRow("south pole - left edge") + << b4 << QGeoCoordinate(latC4, lonL4) << true; + QTest::newRow("south pole - left edge - outside") + << b4 << QGeoCoordinate(latC4, lonLO4) << false; + QTest::newRow("south pole - right edge - inside") + << b4 << QGeoCoordinate(latC4, lonRI4) << true; + QTest::newRow("south pole - right edge") + << b4 << QGeoCoordinate(latC4, lonR4) << true; + QTest::newRow("south pole - right edge - outside") + << b4 << QGeoCoordinate(latC4, lonRO4) << false; + QTest::newRow("south pole - bottom edge - inside") + << b4 << QGeoCoordinate(latBI4, lonC4) << true; + QTest::newRow("south pole - bottom edge") + << b4 << QGeoCoordinate(latB4, lonC4) << true; + QTest::newRow("south pole - bottom left - inside") + << b4 << QGeoCoordinate(latBI4, lonLI4) << true; + QTest::newRow("south pole - bottom left") + << b4 << QGeoCoordinate(latB4, lonL4) << true; + QTest::newRow("south pole - bottom left - outside") + << b4 << QGeoCoordinate(latB4, lonLO4) << true; + QTest::newRow("south pole - bottom right - inside") + << b4 << QGeoCoordinate(latBI4, lonRI4) << true; + QTest::newRow("south pole - bottom right") + << b4 << QGeoCoordinate(latB4, lonR4) << true; + QTest::newRow("south pole - bottom right - outside") + << b4 << QGeoCoordinate(latB4, lonRO4) << true; +} + +void tst_QGeoRectangle::containsBoxAndIntersects() +{ + QFETCH(QGeoRectangle, box1); + QFETCH(QGeoRectangle, box2); + QFETCH(bool, contains); + QFETCH(bool, intersects); + + QCOMPARE(box1.contains(box2), contains); + QCOMPARE(box1.intersects(box2), intersects); +} + +void tst_QGeoRectangle::containsBoxAndIntersects_data() +{ + QTest::addColumn("box1"); + QTest::addColumn("box2"); + QTest::addColumn("contains"); + QTest::addColumn("intersects"); + + QGeoRectangle b1(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)); + + QTest::newRow("non wrapped same") + << b1 + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << true << true; + + QTest::newRow("non wrapped smaller") + << b1 + << QGeoRectangle(QGeoCoordinate(20.0, -20.0), + QGeoCoordinate(-20.0, 20.0)) + << true << true; + + QTest::newRow("non wrapped larger") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, -40.0), + QGeoCoordinate(-40.0, 40.0)) + << false << true; + + QTest::newRow("non wrapped outside top") + << b1 + << QGeoRectangle(QGeoCoordinate(80.0, -30.0), + QGeoCoordinate(50.0, 30.0)) + << false << false; + + QTest::newRow("non wrapped outside bottom") + << b1 + << QGeoRectangle(QGeoCoordinate(-50.0, -30.0), + QGeoCoordinate(-80.0, 30.0)) + << false << false; + + QTest::newRow("non wrapped outside left") + << b1 + << QGeoRectangle(QGeoCoordinate(30.0, -80.0), + QGeoCoordinate(-30.0, -50.0)) + << false << false; + + QTest::newRow("non wrapped outside wrapped") + << b1 + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << false << false; + + QTest::newRow("non wrapped outside right") + << b1 + << QGeoRectangle(QGeoCoordinate(30.0, 50.0), + QGeoCoordinate(-30.0, 80.0)) + << false << false; + + QTest::newRow("non wrapped top left cross") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, -40.0), + QGeoCoordinate(20.0, -20.0)) + << false << true; + + QTest::newRow("non wrapped top cross") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, -10.0), + QGeoCoordinate(20.0, 10.0)) + << false << true; + + QTest::newRow("non wrapped top right cross") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, 20.0), + QGeoCoordinate(20.0, 40.0)) + << false << true; + + QTest::newRow("non wrapped left cross") + << b1 + << QGeoRectangle(QGeoCoordinate(10.0, -40.0), + QGeoCoordinate(-10.0, -20.0)) + << false << true; + + QTest::newRow("non wrapped right cross") + << b1 + << QGeoRectangle(QGeoCoordinate(10.0, 20.0), + QGeoCoordinate(-10.0, 40.0)) + << false << true; + + QTest::newRow("non wrapped bottom left cross") + << b1 + << QGeoRectangle(QGeoCoordinate(-20.0, -40.0), + QGeoCoordinate(-40.0, -20.0)) + << false << true; + + QTest::newRow("non wrapped bottom cross") + << b1 + << QGeoRectangle(QGeoCoordinate(-20.0, -10.0), + QGeoCoordinate(-40.0, 10.0)) + << false << true; + + QTest::newRow("non wrapped bottom right cross") + << b1 + << QGeoRectangle(QGeoCoordinate(-20.0, 20.0), + QGeoCoordinate(-40.0, 40.0)) + << false << true; + + QTest::newRow("non wrapped top left touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(50.0, -50.0), + QGeoCoordinate(30.0, -30.0)) + << false << true; + + QTest::newRow("non wrapped top touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(50.0, -10.0), + QGeoCoordinate(30.0, 10.0)) + << false << true; + + QTest::newRow("non wrapped top right touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(50.0, 30.0), + QGeoCoordinate(30.0, 50.0)) + << false << true; + + QTest::newRow("non wrapped left touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(10.0, -50.0), + QGeoCoordinate(-10.0, -30.0)) + << false << true; + + QTest::newRow("non wrapped right touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(10.0, 30.0), + QGeoCoordinate(-10.0, 50.0)) + << false << true; + + QTest::newRow("non wrapped bottom left touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(-30.0, -30.0), + QGeoCoordinate(-50.0, -50.0)) + << false << true; + + QTest::newRow("non wrapped bottom touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(-30.0, -10.0), + QGeoCoordinate(-50.0, 10.0)) + << false << true; + + QTest::newRow("non wrapped bottom right touch outside") + << b1 + << QGeoRectangle(QGeoCoordinate(-30.0, 30.0), + QGeoCoordinate(-50.0, 50.0)) + << false << true; + + QTest::newRow("non wrapped top left touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(10.0, -10.0)) + << true << true; + + QTest::newRow("non wrapped top touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(30.0, -10.0), + QGeoCoordinate(10.0, 10.0)) + << true << true; + + QTest::newRow("non wrapped top right touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(30.0, 10.0), + QGeoCoordinate(10.0, 30.0)) + << true << true; + + QTest::newRow("non wrapped left touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(10.0, -30.0), + QGeoCoordinate(-10.0, -10.0)) + << true << true; + + QTest::newRow("non wrapped right touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(10.0, 10.0), + QGeoCoordinate(-10.0, 30.0)) + << true << true; + + QTest::newRow("non wrapped bottom left touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(-10.0, -30.0), + QGeoCoordinate(-30.0, -10.0)) + << true << true; + + QTest::newRow("non wrapped bottom touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(-10.0, -10.0), + QGeoCoordinate(-30.0, 10.0)) + << true << true; + + QTest::newRow("non wrapped bottom right touch inside") + << b1 + << QGeoRectangle(QGeoCoordinate(-10.0, 10.0), + QGeoCoordinate(-30.0, 30.0)) + << true << true; + + QTest::newRow("non wrapped top lon strip") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, -40.0), + QGeoCoordinate(20.0, 40.0)) + << false << true; + + QTest::newRow("non wrapped center lon strip") + << b1 + << QGeoRectangle(QGeoCoordinate(10.0, -40.0), + QGeoCoordinate(-10.0, 40.0)) + << false << true; + + QTest::newRow("non wrapped bottom lon strip") + << b1 + << QGeoRectangle(QGeoCoordinate(-20.0, -40.0), + QGeoCoordinate(-40.0, 40.0)) + << false << true; + + QTest::newRow("non wrapped left lat strip") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, -40.0), + QGeoCoordinate(-40.0, -20.0)) + << false << true; + + QTest::newRow("non wrapped center lat strip") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, -10.0), + QGeoCoordinate(-40.0, 10.0)) + << false << true; + + QTest::newRow("non wrapped right lat strip") + << b1 + << QGeoRectangle(QGeoCoordinate(40.0, 20.0), + QGeoCoordinate(-40.0, 40.0)) + << false << true; + + QGeoRectangle b2(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)); + + QTest::newRow("wrapped same") + << b2 + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << true << true; + + QTest::newRow("wrapped smaller") + << b2 + << QGeoRectangle(QGeoCoordinate(20.0, 160.0), + QGeoCoordinate(-20.0, -160.0)) + << true << true; + + QTest::newRow("wrapped larger") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, 140.0), + QGeoCoordinate(-40.0, -140.0)) + << false << true; + + QTest::newRow("wrapped outside top") + << b2 + << QGeoRectangle(QGeoCoordinate(80.0, 150.0), + QGeoCoordinate(50.0, -150.0)) + << false << false; + + QTest::newRow("wrapped outside bottom") + << b2 + << QGeoRectangle(QGeoCoordinate(-50.0, 150.0), + QGeoCoordinate(-80.0, -150.0)) + << false << false; + + QTest::newRow("wrapped outside left") + << b2 + << QGeoRectangle(QGeoCoordinate(30.0, 70.0), + QGeoCoordinate(-30.0, 130.0)) + << false << false; + + QTest::newRow("wrapped outside right") + << b2 + << QGeoRectangle(QGeoCoordinate(30.0, -130.0), + QGeoCoordinate(-30.0, -70.0)) + << false << false; + + QTest::newRow("wrapped top left cross") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, 140.0), + QGeoCoordinate(20.0, 160.0)) + << false << true; + + QTest::newRow("wrapped top cross") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, 170.0), + QGeoCoordinate(20.0, -170.0)) + << false << true; + + QTest::newRow("wrapped top right cross") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, -160.0), + QGeoCoordinate(20.0, -140.0)) + << false << true; + + QTest::newRow("wrapped left cross") + << b2 + << QGeoRectangle(QGeoCoordinate(10.0, 140.0), + QGeoCoordinate(-10.0, 160.0)) + << false << true; + + QTest::newRow("wrapped right cross") + << b2 + << QGeoRectangle(QGeoCoordinate(10.0, -160.0), + QGeoCoordinate(-10.0, -140.0)) + << false << true; + + QTest::newRow("wrapped bottom left cross") + << b2 + << QGeoRectangle(QGeoCoordinate(-20.0, 140.0), + QGeoCoordinate(-40.0, 160.0)) + << false << true; + + QTest::newRow("wrapped bottom cross") + << b2 + << QGeoRectangle(QGeoCoordinate(-20.0, 170.0), + QGeoCoordinate(-40.0, -170.0)) + << false << true; + + QTest::newRow("wrapped bottom right cross") + << b2 + << QGeoRectangle(QGeoCoordinate(-20.0, -160.0), + QGeoCoordinate(-40.0, -140.0)) + << false << true; + + QTest::newRow("wrapped top left touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(50.0, 130.0), + QGeoCoordinate(30.0, 150.0)) + << false << true; + + QTest::newRow("wrapped top touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(50.0, 170.0), + QGeoCoordinate(30.0, -170.0)) + << false << true; + + QTest::newRow("wrapped top right touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(50.0, -150.0), + QGeoCoordinate(30.0, -130.0)) + << false << true; + + QTest::newRow("wrapped left touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(10.0, 130.0), + QGeoCoordinate(-10.0, 150.0)) + << false << true; + + QTest::newRow("wrapped right touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(10.0, -150.0), + QGeoCoordinate(-10.0, -130.0)) + << false << true; + + QTest::newRow("wrapped bottom left touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(-30.0, 150.0), + QGeoCoordinate(-50.0, 130.0)) + << false << true; + + QTest::newRow("wrapped bottom touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(-30.0, 170.0), + QGeoCoordinate(-50.0, -170.0)) + << false << true; + + QTest::newRow("wrapped bottom right touch outside") + << b2 + << QGeoRectangle(QGeoCoordinate(-30.0, -150.0), + QGeoCoordinate(-50.0, -130.0)) + << false << true; + + QTest::newRow("wrapped top left touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(10.0, 170.0)) + << true << true; + + QTest::newRow("wrapped top touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(30.0, 170.0), + QGeoCoordinate(10.0, -170.0)) + << true << true; + + QTest::newRow("wrapped top right touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(30.0, -170.0), + QGeoCoordinate(10.0, -150.0)) + << true << true; + + QTest::newRow("wrapped left touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(10.0, 150.0), + QGeoCoordinate(-10.0, 170.0)) + << true << true; + + QTest::newRow("wrapped right touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(10.0, -170.0), + QGeoCoordinate(-10.0, -150.0)) + << true << true; + + QTest::newRow("wrapped bottom left touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(-10.0, 150.0), + QGeoCoordinate(-30.0, 170.0)) + << true << true; + + QTest::newRow("wrapped bottom touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(-10.0, 170.0), + QGeoCoordinate(-30.0, -170.0)) + << true << true; + + QTest::newRow("wrapped bottom right touch inside") + << b2 + << QGeoRectangle(QGeoCoordinate(-10.0, -170.0), + QGeoCoordinate(-30.0, -150.0)) + << true << true; + + QTest::newRow("wrapped top lon strip") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, 140.0), + QGeoCoordinate(20.0, -140.0)) + << false << true; + + QTest::newRow("wrapped center lon strip") + << b2 + << QGeoRectangle(QGeoCoordinate(10.0, 140.0), + QGeoCoordinate(-10.0, -140.0)) + << false << true; + + QTest::newRow("wrapped bottom lon strip") + << b2 + << QGeoRectangle(QGeoCoordinate(-20.0, 140.0), + QGeoCoordinate(-40.0, -140.0)) + << false << true; + + QTest::newRow("wrapped left lat strip") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, 140.0), + QGeoCoordinate(-40.0, 160.0)) + << false << true; + + QTest::newRow("wrapped center lat strip") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, 170.0), + QGeoCoordinate(-40.0, -170.0)) + << false << true; + + QTest::newRow("wrapped right lat strip") + << b2 + << QGeoRectangle(QGeoCoordinate(40.0, -160.0), + QGeoCoordinate(-40.0, -140.0)) + << false << true; + + QTest::newRow("north pole touching") + << QGeoRectangle(QGeoCoordinate(90.0, 20.0), + QGeoCoordinate(40.0, 40.0)) + << QGeoRectangle(QGeoCoordinate(90.0, 60.0), + QGeoCoordinate(30.0, 80.0)) + << false << true; + + QTest::newRow("south pole touching") + << QGeoRectangle(QGeoCoordinate(40.0, 20.0), + QGeoCoordinate(-90.0, 40.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 60.0), + QGeoCoordinate(-90.0, 80.0)) + << false << true; +} + +void tst_QGeoRectangle::translate() +{ + QFETCH(QGeoRectangle, box); + QFETCH(double, degreesLatitude); + QFETCH(double, degreesLongitude); + QFETCH(QGeoRectangle, newBox); + + QGeoRectangle test = box.translated(degreesLatitude, degreesLongitude); + QCOMPARE(test, newBox); + box.translate(degreesLatitude, degreesLongitude); + QCOMPARE(box, newBox); + +} + +void tst_QGeoRectangle::translate_data() +{ + QTest::addColumn("box"); + QTest::addColumn("degreesLatitude"); + QTest::addColumn("degreesLongitude"); + QTest::addColumn("newBox"); + + QTest::newRow("invalid") + << QGeoRectangle() + << 20.0 + << 20.0 + << QGeoRectangle(); + + QTest::newRow("360 width") + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)) + << 20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(50.0, -180.0), + QGeoCoordinate(-10.0, 180.0)); + + QTest::newRow("180 height") + << QGeoRectangle(QGeoCoordinate(90.0, -30.0), + QGeoCoordinate(-90.0, 30.0)) + << 20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(90.0, -10.0), + QGeoCoordinate(-90.0, 50.0)); + + QTest::newRow("non wrapping -> non wrapping") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << 20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(50.0, -10.0), + QGeoCoordinate(-10.0, 50.0)); + + QTest::newRow("non wrapping -> wrapping") + << QGeoRectangle(QGeoCoordinate(30.0, 110.0), + QGeoCoordinate(-30.0, 170.0)) + << 20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(50.0, 130.0), + QGeoCoordinate(-10.0, -170.0)); + + QTest::newRow("non wrapping -> north clip") + << QGeoRectangle(QGeoCoordinate(80.0, -30.0), + QGeoCoordinate(20.0, 30.0)) + << 20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(90.0, -10.0), + QGeoCoordinate(30.0, 50.0)); + + QTest::newRow("non wrapping -> south clip") + << QGeoRectangle(QGeoCoordinate(-20.0, -30.0), + QGeoCoordinate(-80.0, 30.0)) + << -20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(-30.0, -10.0), + QGeoCoordinate(-90.0, 50.0)); + + QTest::newRow("wrapping -> non wrapping") + << QGeoRectangle(QGeoCoordinate(30.0, 130.0), + QGeoCoordinate(-30.0, -170.0)) + << 20.0 + << -20.0 + << QGeoRectangle(QGeoCoordinate(50.0, 110.0), + QGeoCoordinate(-10.0, 170.0)); + + QTest::newRow("wrapping -> wrapping") + << QGeoRectangle(QGeoCoordinate(30.0, 130.0), + QGeoCoordinate(-30.0, -170.0)) + << 20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(50.0, 150.0), + QGeoCoordinate(-10.0, -150.0)); + + QTest::newRow("wrapping -> north clip") + << QGeoRectangle(QGeoCoordinate(80.0, 130.0), + QGeoCoordinate(20.0, -170.0)) + << 20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(90.0, 150.0), + QGeoCoordinate(30.0, -150.0)); + + QTest::newRow("wrapping -> south clip") + << QGeoRectangle(QGeoCoordinate(-20.0, 130.0), + QGeoCoordinate(-80.0, -170.0)) + << -20.0 + << 20.0 + << QGeoRectangle(QGeoCoordinate(-30.0, 150.0), + QGeoCoordinate(-90.0, -150.0)); +} + +void tst_QGeoRectangle::unite() +{ + QFETCH(QGeoRectangle, in1); + QFETCH(QGeoRectangle, in2); + QFETCH(QGeoRectangle, out); + + QCOMPARE(in1.united(in2), out); + QCOMPARE(in2.united(in1), out); + + QCOMPARE(in1 | in2, out); + QCOMPARE(in2 | in1, out); + + QGeoRectangle united1 = QGeoRectangle(in1); + united1 |= in2; + QCOMPARE(united1, out); + + QGeoRectangle united2 = QGeoRectangle(in2); + united2 |= in1; + QCOMPARE(united2, out); +} + +void tst_QGeoRectangle::unite_data() +{ + QTest::addColumn("in1"); + QTest::addColumn("in2"); + QTest::addColumn("out"); + + QTest::newRow("central and taller") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(50.0, -30.0), + QGeoCoordinate(-50.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(50.0, -30.0), + QGeoCoordinate(-50.0, 30.0)); + + QTest::newRow("central and 180 high") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(90.0, -30.0), + QGeoCoordinate(-90.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(90.0, -30.0), + QGeoCoordinate(-90.0, 30.0)); + + QTest::newRow("central and non overlapping higher") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(60.0, -30.0), + QGeoCoordinate(50.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(60.0, -30.0), + QGeoCoordinate(-30.0, 30.0)); + + QTest::newRow("central and overlapping higher") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(60.0, -30.0), + QGeoCoordinate(0.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(60.0, -30.0), + QGeoCoordinate(-30.0, 30.0)); + + QTest::newRow("central and touching higher") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(60.0, -30.0), + QGeoCoordinate(30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(60.0, -30.0), + QGeoCoordinate(-30.0, 30.0)); + + QTest::newRow("central and non overlapping lower") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(-50.0, -30.0), + QGeoCoordinate(-60.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-60.0, 30.0)); + + QTest::newRow("central and overlapping lower") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(0.0, -30.0), + QGeoCoordinate(-60.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-60.0, 30.0)); + + QTest::newRow("central and touching lower") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(-30.0, -30.0), + QGeoCoordinate(-60.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-60.0, 30.0)); + + QTest::newRow("non wrapping central and wider") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -50.0), + QGeoCoordinate(-30.0, 50.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -50.0), + QGeoCoordinate(-30.0, 50.0)); + + QTest::newRow("non wrapping central and 360 width") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)); + + QTest::newRow("non wrapping central and non overlapping non wrapping left") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -110.0), + QGeoCoordinate(-30.0, -50.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -110.0), + QGeoCoordinate(-30.0, 30.0)); + + QTest::newRow("non wrapping central and overlapping non wrapping left") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -80.0), + QGeoCoordinate(-30.0, -20.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -80.0), + QGeoCoordinate(-30.0, 30.0)); + + QTest::newRow("non wrapping central and touching non wrapping left") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -90.0), + QGeoCoordinate(-30.0, -30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -90.0), + QGeoCoordinate(-30.0, 30.0)); + + QTest::newRow("non wrapping central and non overlapping non wrapping right") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 50.0), + QGeoCoordinate(-30.0, 110.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 110.0)); + + QTest::newRow("non wrapping central and overlapping non wrapping right") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 20.0), + QGeoCoordinate(-30.0, 80.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 80.0)); + + QTest::newRow("non wrapping central and touching non wrapping right") + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 30.0), + QGeoCoordinate(-30.0, 90.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 90.0)); + + QTest::newRow("wrapping and wider") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 130.0), + QGeoCoordinate(-30.0, -130.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 130.0), + QGeoCoordinate(-30.0, -130.0)); + + QTest::newRow("wrapping and 360 width") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)); + + QTest::newRow("wrapping and non overlapping right") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -130.0), + QGeoCoordinate(-30.0, -70.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -70.0)); + + QTest::newRow("wrapping and overlapping right") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -160.0), + QGeoCoordinate(-30.0, -70.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -70.0)); + + QTest::newRow("wrapping and touching right") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -150.0), + QGeoCoordinate(-30.0, -90.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -90.0)); + + QTest::newRow("wrapping and non overlapping left") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 70.0), + QGeoCoordinate(-30.0, 130.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 70.0), + QGeoCoordinate(-30.0, -150.0)); + + QTest::newRow("wrapping and overlapping left") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 100.0), + QGeoCoordinate(-30.0, 160.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 100.0), + QGeoCoordinate(-30.0, -150.0)); + + QTest::newRow("wrapping and touching left") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 90.0), + QGeoCoordinate(-30.0, 150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 90.0), + QGeoCoordinate(-30.0, -150.0)); + + QTest::newRow("wrapping and non overlapping center") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -30.0), + QGeoCoordinate(-30.0, 30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)); + + QTest::newRow("wrapping and overlapping center") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -160.0), + QGeoCoordinate(-30.0, 160.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)); + + QTest::newRow("wrapping and touching center") + << QGeoRectangle(QGeoCoordinate(30.0, 150.0), + QGeoCoordinate(-30.0, -150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -150.0), + QGeoCoordinate(-30.0, 150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)); + + QTest::newRow("small gap over zero line") + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, -10.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 10.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20.0)); + + QTest::newRow("small gap before zero line") + << QGeoRectangle(QGeoCoordinate(30.0, -40.0), + QGeoCoordinate(-30.0, -30.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, -10.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -40.0), + QGeoCoordinate(-30.0, -10.0)); + + QTest::newRow("small gap after zero line") + << QGeoRectangle(QGeoCoordinate(30.0, 10.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 30.0), + QGeoCoordinate(-30.0, 40.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 10.0), + QGeoCoordinate(-30.0, 40.0)); + + QTest::newRow("small gap over dateline") + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, 170.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -170.0), + QGeoCoordinate(-30.0, -160.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, -160.0)); + + QTest::newRow("small gap before dateline") + << QGeoRectangle(QGeoCoordinate(30.0, 140.0), + QGeoCoordinate(-30.0, 150.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, 170.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 140.0), + QGeoCoordinate(-30.0, 170.0)); + + QTest::newRow("small gap after dateline") + << QGeoRectangle(QGeoCoordinate(30.0, -170.0), + QGeoCoordinate(-30.0, -160.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -150.0), + QGeoCoordinate(-30.0, -140.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -170.0), + QGeoCoordinate(-30.0, -140.0)); + + QTest::newRow("90-degree inner gap over zero line") + << QGeoRectangle(QGeoCoordinate(30.0, -55.0), + QGeoCoordinate(-30.0, -45.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 45.0), + QGeoCoordinate(-30.0, 55.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -55.0), + QGeoCoordinate(-30.0, 55.0)); + + QTest::newRow("90-degree inner gap before zero line") + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, -10.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -65.0), + QGeoCoordinate(-30.0, -55.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -65.0), + QGeoCoordinate(-30.0, -10.0)); + + QTest::newRow("90-degree inner gap after zero line") + << QGeoRectangle(QGeoCoordinate(30.0, 65.0), + QGeoCoordinate(-30.0, 75.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 10.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 10.0), + QGeoCoordinate(-30.0, 75.0)); + + QTest::newRow("90-degree inner gap over dateline") + << QGeoRectangle(QGeoCoordinate(30.0, 125.0), + QGeoCoordinate(-30.0, 135.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -135.0), + QGeoCoordinate(-30.0, -125.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 125.0), + QGeoCoordinate(-30.0, -125.0)); + + QTest::newRow("90-degree inner gap before dateline") + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, 170.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 50.0), + QGeoCoordinate(-30.0, 60.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 50.0), + QGeoCoordinate(-30.0, 170.0)); + + QTest::newRow("90-degree inner gap after dateline") + << QGeoRectangle(QGeoCoordinate(30.0, -170.0), + QGeoCoordinate(-30.0, -160.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -60.0), + QGeoCoordinate(-30.0, -50.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -170.0), + QGeoCoordinate(-30.0, -50.0)); + + QTest::newRow("180-degree inner gap centered on zero line") + << QGeoRectangle(QGeoCoordinate(30.0, -100.0), + QGeoCoordinate(-30.0, -90.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 90.0), + QGeoCoordinate(-30.0, 100.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 90.0), + QGeoCoordinate(-30.0, -90.0)); + + QTest::newRow("180-degree outer gap cenetered on zero line") + << QGeoRectangle(QGeoCoordinate(30.0, -90.0), + QGeoCoordinate(-30.0, -80.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 80.0), + QGeoCoordinate(-30.0, 90.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -90.0), + QGeoCoordinate(-30.0, 90.0)); + + QTest::newRow("180-degree shift centered on zero line") + << QGeoRectangle(QGeoCoordinate(30.0, -100.0), + QGeoCoordinate(-30.0, -80.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 80.0), + QGeoCoordinate(-30.0, 100.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)); + + QTest::newRow("180-degree inner gap centered on dateline") + << QGeoRectangle(QGeoCoordinate(30.0, 80.0), + QGeoCoordinate(-30.0, 90.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -90.0), + QGeoCoordinate(-30.0, -80.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -90.0), + QGeoCoordinate(-30.0, 90.0)); + + QTest::newRow("180-degree outer gap centered on dateline") + << QGeoRectangle(QGeoCoordinate(30.0, 90.0), + QGeoCoordinate(-30.0, 100.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -100.0), + QGeoCoordinate(-30.0, -90.0)) + << QGeoRectangle(QGeoCoordinate(30.0, 90.0), + QGeoCoordinate(-30.0, -90.0)); + + QTest::newRow("180-degree shift centered on dateline") + << QGeoRectangle(QGeoCoordinate(30.0, 80.0), + QGeoCoordinate(-30.0, 100.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -100.0), + QGeoCoordinate(-30.0, -80.0)) + << QGeoRectangle(QGeoCoordinate(30.0, -180.0), + QGeoCoordinate(-30.0, 180.0)); +} + + +void tst_QGeoRectangle::extendRectangle() +{ + QFETCH(QGeoRectangle, box); + QFETCH(QGeoCoordinate, coord); + QFETCH(QGeoRectangle, out); + + box.extendRectangle(coord); + QCOMPARE(box, out); +} + +void tst_QGeoRectangle::extendRectangle_data() +{ + QTest::addColumn("box"); + QTest::addColumn("coord"); + QTest::addColumn("out"); + + QTest::newRow("valid rect - invalid coordinate") + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoCoordinate(100.0, 190.0) + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20)); + QTest::newRow("invalid rect - valid coordinate") + << QGeoRectangle() + << QGeoCoordinate(10.0, 10.0) + << QGeoRectangle(); + QTest::newRow("inside rect - not wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoCoordinate(10.0, 10.0) + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20)); + QTest::newRow("lat outside rect - not wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoCoordinate(40.0, 10.0) + << QGeoRectangle(QGeoCoordinate(40.0, -20.0), + QGeoCoordinate(-30.0, 20)); + QTest::newRow("positive lon outside rect - not wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoCoordinate(10.0, 40.0) + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 40)); + QTest::newRow("negative lon outside rect - not wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, -20.0), + QGeoCoordinate(-30.0, 20.0)) + << QGeoCoordinate(10.0, -40.0) + << QGeoRectangle(QGeoCoordinate(30.0, -40.0), + QGeoCoordinate(-30.0, 20.0)); + QTest::newRow("inside rect - wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, -160.0)) + << QGeoCoordinate(10.0, -170.0) + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, -160.0)); + QTest::newRow("lat outside rect - wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, -160.0)) + << QGeoCoordinate(-40.0, -170.0) + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-40.0, -160.0)); + QTest::newRow("positive lon outside rect - wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, -160.0)) + << QGeoCoordinate(10.0, 140.0) + << QGeoRectangle(QGeoCoordinate(30.0, 140.0), + QGeoCoordinate(-30.0, -160.0)); + QTest::newRow("negative lon outside rect - wrapped") + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, -160.0)) + << QGeoCoordinate(10.0, -140.0) + << QGeoRectangle(QGeoCoordinate(30.0, 160.0), + QGeoCoordinate(-30.0, -140.0)); + QTest::newRow("extending over 180 degree line eastward") + << QGeoRectangle(QGeoCoordinate(30.0, 130.0), + QGeoCoordinate(-30.0, 160.0)) + << QGeoCoordinate(10.0, -170.0) + << QGeoRectangle(QGeoCoordinate(30.0, 130.0), + QGeoCoordinate(-30.0, -170)); + QTest::newRow("extending over -180 degree line westward") + << QGeoRectangle(QGeoCoordinate(30.0, -160.0), + QGeoCoordinate(-30.0, -130.0)) + << QGeoCoordinate(10.0, 170.0) + << QGeoRectangle(QGeoCoordinate(30.0, 170.0), + QGeoCoordinate(-30.0, -130)); +} + +void tst_QGeoRectangle::areaComparison_data() +{ + QTest::addColumn("area"); + QTest::addColumn("box"); + QTest::addColumn("equal"); + + QGeoRectangle b1(QGeoCoordinate(10.0, 0.0), QGeoCoordinate(0.0, 10.0)); + QGeoRectangle b2(QGeoCoordinate(20.0, 0.0), QGeoCoordinate(0.0, 20.0)); + QGeoCircle c(QGeoCoordinate(0.0, 0.0), 10); + + QTest::newRow("default constructed") << QGeoShape() << QGeoRectangle() << false; + QTest::newRow("b1 b1") << QGeoShape(b1) << b1 << true; + QTest::newRow("b1 b2") << QGeoShape(b1) << b2 << false; + QTest::newRow("b2 b1") << QGeoShape(b2) << b1 << false; + QTest::newRow("b2 b2") << QGeoShape(b2) << b2 << true; + QTest::newRow("c b1") << QGeoShape(c) << b1 << false; +} + +void tst_QGeoRectangle::areaComparison() +{ + QFETCH(QGeoShape, area); + QFETCH(QGeoRectangle, box); + QFETCH(bool, equal); + + QCOMPARE((area == box), equal); + QCOMPARE((area != box), !equal); + + QCOMPARE((box == area), equal); + QCOMPARE((box != area), !equal); +} + +void tst_QGeoRectangle::circleComparison_data() +{ + QTest::addColumn("circle"); + QTest::addColumn("box"); + QTest::addColumn("equal"); + + QGeoRectangle b(QGeoCoordinate(10.0, 0.0), QGeoCoordinate(0.0, 10.0)); + QGeoCircle c(QGeoCoordinate(0.0, 0.0), 10); + + QTest::newRow("default constructed") << QGeoCircle() << QGeoRectangle() << false; + QTest::newRow("c b") << c << b << false; +} + +void tst_QGeoRectangle::circleComparison() +{ + QFETCH(QGeoCircle, circle); + QFETCH(QGeoRectangle, box); + QFETCH(bool, equal); + + QCOMPARE((circle == box), equal); + QCOMPARE((circle != box), !equal); + + QCOMPARE((box == circle), equal); + QCOMPARE((box != circle), !equal); +} + +QTEST_MAIN(tst_QGeoRectangle) +#include "tst_qgeorectangle.moc" + diff --git a/tests/auto/qgeoroute/qgeoroute.pro b/tests/auto/qgeoroute/qgeoroute.pro new file mode 100644 index 0000000..e62adcf --- /dev/null +++ b/tests/auto/qgeoroute/qgeoroute.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeoroute + +# Input +HEADERS += tst_qgeoroute.h +SOURCES += tst_qgeoroute.cpp + +QT += location-private testlib diff --git a/tests/auto/qgeoroute/tst_qgeoroute.cpp b/tests/auto/qgeoroute/tst_qgeoroute.cpp new file mode 100644 index 0000000..709597f --- /dev/null +++ b/tests/auto/qgeoroute/tst_qgeoroute.cpp @@ -0,0 +1,306 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_qgeoroute.h" +#include "../geotestplugin/qgeoroutingmanagerengine_test.h" + + +tst_QGeoRoute::tst_QGeoRoute() +{ +} + +void tst_QGeoRoute::initTestCase() +{ +} + +void tst_QGeoRoute::cleanupTestCase() +{ +} + +void tst_QGeoRoute::init() +{ + qgeoroute = new QGeoRoute(); + qgeocoordinate = new QGeoCoordinate(); +} + +void tst_QGeoRoute::cleanup() +{ + delete qgeoroute; + delete qgeocoordinate; +} + +void tst_QGeoRoute::constructor() +{ + QString empty = ""; + QGeoRectangle *boundingbox = new QGeoRectangle(); + + QCOMPARE(qgeoroute->bounds(), *boundingbox); + QCOMPARE(qgeoroute->distance(), qreal(0.0)); + QCOMPARE(qgeoroute->path().size(), 0); + QCOMPARE(qgeoroute->routeId(), empty); + QCOMPARE(qgeoroute->travelTime(), 0); + + delete boundingbox; +} + +void tst_QGeoRoute::copy_constructor() +{ + QGeoRoute *qgeoroutecopy = new QGeoRoute(*qgeoroute); + QCOMPARE(*qgeoroute, *qgeoroutecopy); + + // CoW + qreal distance = qgeoroute->distance(); + qgeoroutecopy->setDistance(distance + 10.0); + + QVERIFY(*qgeoroute == *qgeoroutecopy); // QGeoRoute uses a QExplicitlySharedDataPointer. no implicit detach() + + delete qgeoroutecopy; +} + +void tst_QGeoRoute::destructor() +{ + QGeoRoute *qgeoroutecopy; + + qgeoroutecopy = new QGeoRoute(); + delete qgeoroutecopy; + + qgeoroutecopy = new QGeoRoute(*qgeoroute); + delete qgeoroutecopy; +} + +void tst_QGeoRoute::bounds() +{ + qgeocoordinate->setLatitude(13.3851); + qgeocoordinate->setLongitude(52.5312); + + QGeoRectangle *qgeoboundingbox = new QGeoRectangle(*qgeocoordinate,0.4,0.4); + + qgeoroute->setBounds(*qgeoboundingbox); + + QCOMPARE(qgeoroute->bounds(), *qgeoboundingbox); + + qgeoboundingbox->setWidth(23.1); + + QVERIFY(qgeoroute->bounds().width() != qgeoboundingbox->width()); + + delete qgeoboundingbox; +} + +void tst_QGeoRoute::distance() +{ + qreal distance = 0.0; + + qgeoroute->setDistance(distance); + QCOMPARE(qgeoroute->distance(), distance); + + distance = 34.4324; + QVERIFY(qgeoroute->distance() != distance); + + qgeoroute->setDistance(distance); + QCOMPARE(qgeoroute->distance(), distance); +} + +void tst_QGeoRoute::path() +{ + QFETCH(QList, coordinates); + + QList path; + + for (int i = 0; i < coordinates.size(); i += 2) { + path.append(QGeoCoordinate(coordinates.at(i),coordinates.at(i+1))); + } + + qgeoroute->setPath(path); + + QList pathRetrieved = qgeoroute->path(); + QCOMPARE(pathRetrieved, path); + + for (int i = 0; i < pathRetrieved.size(); i++) { + QCOMPARE(pathRetrieved.at(i), path.at(i)); + } +} + +void tst_QGeoRoute::path_data() +{ + QTest::addColumn >("coordinates"); + + QList coordinates; + + coordinates << 0.0 << 0.0; + QTest::newRow("path1") << coordinates ; + + coordinates << -23.23 << 54.45345; + QTest::newRow("path2") << coordinates ; + + coordinates << -85.4324 << -121.343; + QTest::newRow("path3") << coordinates ; + + coordinates << 1.323 << 12.323; + QTest::newRow("path4") << coordinates ; + + coordinates << 1324.323 << 143242.323; + QTest::newRow("path5") << coordinates ; +} + +void tst_QGeoRoute::request() +{ + qgeocoordinate->setLatitude(65.654); + qgeocoordinate->setLongitude(0.4324); + + QGeoCoordinate *qgeocoordinatecopy = new QGeoCoordinate(34.54 , -21.32); + + QList path; + path.append(*qgeocoordinate); + path.append(*qgeocoordinatecopy); + + qgeorouterequest = new QGeoRouteRequest(path); + + qgeoroute->setRequest(*qgeorouterequest); + + QCOMPARE(qgeoroute->request(), *qgeorouterequest); + + QGeoCoordinate *qgeocoordinatecopy2 = new QGeoCoordinate(4.7854 , -121.32); + path.append(*qgeocoordinatecopy2); + + QGeoRouteRequest *qgeorouterequestcopy = new QGeoRouteRequest(path); + + QVERIFY(qgeoroute->request() != *qgeorouterequestcopy); + + delete qgeocoordinatecopy; + delete qgeocoordinatecopy2; + delete qgeorouterequest; + delete qgeorouterequestcopy; +} + +void tst_QGeoRoute::routeId() +{ + QString text = "routeId 4504"; + + qgeoroute->setRouteId(text); + + QCOMPARE(qgeoroute->routeId(), text); + + text = "routeId 1111"; + QVERIFY(qgeoroute->routeId() != text); + +} + +void tst_QGeoRoute::firstrouteSegments() +{ + qgeoroutesegment = new QGeoRouteSegment(); + qgeoroutesegment->setDistance(35.453); + qgeoroutesegment->setTravelTime(56); + + qgeoroute->setFirstRouteSegment(*qgeoroutesegment); + + QCOMPARE(qgeoroute->firstRouteSegment(), *qgeoroutesegment); + + QGeoRouteSegment *qgeoroutesegmentcopy = new QGeoRouteSegment (); + qgeoroutesegmentcopy->setDistance(435.432); + qgeoroutesegmentcopy->setTravelTime(786); + + QVERIFY(qgeoroute->firstRouteSegment() != *qgeoroutesegmentcopy); + + delete qgeoroutesegment; + delete qgeoroutesegmentcopy; + +} + +void tst_QGeoRoute::travelMode() +{ + QFETCH(QGeoRouteRequest::TravelMode, mode); + + qgeoroute->setTravelMode(mode); + QCOMPARE(qgeoroute->travelMode(), mode); +} +void tst_QGeoRoute::travelMode_data() +{ + QTest::addColumn("mode"); + + QTest::newRow("mode1") << QGeoRouteRequest::CarTravel; + QTest::newRow("mode2") << QGeoRouteRequest::PedestrianTravel; + QTest::newRow("mode3") << QGeoRouteRequest::BicycleTravel; + QTest::newRow("mode4") << QGeoRouteRequest::PublicTransitTravel; + QTest::newRow("mode5") << QGeoRouteRequest::TruckTravel; +} + +void tst_QGeoRoute::travelTime() +{ + int time = 0; + qgeoroute->setTravelTime(time); + + QCOMPARE (qgeoroute->travelTime(), time); + + time = 35; + + QVERIFY (qgeoroute->travelTime() != time); + + qgeoroute->setTravelTime(time); + QCOMPARE (qgeoroute->travelTime(), time); +} + +void tst_QGeoRoute::operators() +{ + QGeoRoute *qgeoroutecopy = new QGeoRoute(*qgeoroute); + + QVERIFY(qgeoroute->operator ==(*qgeoroutecopy)); + QVERIFY(!qgeoroute->operator !=(*qgeoroutecopy)); + + qgeoroute->setDistance(543.324); // QExplicitlySharedDataPointer does not detach implicitly. + qgeoroute->setRouteId("RouteId 111"); + qgeoroute->setTravelMode(QGeoRouteRequest::PedestrianTravel); + qgeoroute->setTravelTime(10); + + qgeoroutecopy->setDistance(12.21); + qgeoroutecopy->setRouteId("RouteId 666"); + qgeoroutecopy->setTravelMode(QGeoRouteRequest::BicycleTravel); + qgeoroutecopy->setTravelTime(99); + + QEXPECT_FAIL("", "QGeoRoute equality operators broken", Continue); + QVERIFY(!(qgeoroute->operator ==(*qgeoroutecopy))); + QEXPECT_FAIL("", "QGeoRoute equality operators broken", Continue); + QVERIFY(qgeoroute->operator !=(*qgeoroutecopy)); + + *qgeoroutecopy = qgeoroutecopy->operator =(*qgeoroute); + QVERIFY(qgeoroute->operator ==(*qgeoroutecopy)); + QVERIFY(!qgeoroute->operator !=(*qgeoroutecopy)); + + + QGeoRouteAlt rAlt; + QGeoRoute r; + QCOMPARE(rAlt.travelTime(), 123456); + QCOMPARE(r.travelTime(), 0); + r = rAlt; + QCOMPARE(r.travelTime(), 123456); + + delete qgeoroutecopy; +} + + + +QTEST_APPLESS_MAIN(tst_QGeoRoute); diff --git a/tests/auto/qgeoroute/tst_qgeoroute.h b/tests/auto/qgeoroute/tst_qgeoroute.h new file mode 100644 index 0000000..ace9095 --- /dev/null +++ b/tests/auto/qgeoroute/tst_qgeoroute.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_QGEOROUTE_H +#define TST_QGEOROUTE_H + +#include +#include +#include + +#include +#include +#include +#include +#include + + +QT_USE_NAMESPACE + +class tst_QGeoRoute : public QObject +{ + Q_OBJECT + +public: + tst_QGeoRoute(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + //Start unit test for QGeoRoute + void constructor(); + void copy_constructor(); + void destructor(); + void bounds(); + void distance(); + void path(); + void path_data(); + void request(); + void routeId(); + void firstrouteSegments(); + void travelMode(); + void travelMode_data(); + void travelTime(); + void operators(); + //End Unit Test for QGeoRoute + +private: + QGeoRoute *qgeoroute; + QGeoRectangle *qgeoboundingbox; + QGeoCoordinate *qgeocoordinate; + QGeoRouteRequest *qgeorouterequest; + QGeoRouteSegment *qgeoroutesegment; + + +}; + +Q_DECLARE_METATYPE( QList); +Q_DECLARE_METATYPE( QList); +Q_DECLARE_METATYPE( QGeoRouteRequest::TravelMode); + + +#endif // TST_QGEOROUTE_H diff --git a/tests/auto/qgeoroutereply/qgeoroutereply.pro b/tests/auto/qgeoroutereply/qgeoroutereply.pro new file mode 100644 index 0000000..29f8480 --- /dev/null +++ b/tests/auto/qgeoroutereply/qgeoroutereply.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeoroutereply + +# Input +HEADERS += tst_qgeoroutereply.h +SOURCES += tst_qgeoroutereply.cpp + +QT += location testlib diff --git a/tests/auto/qgeoroutereply/tst_qgeoroutereply.cpp b/tests/auto/qgeoroutereply/tst_qgeoroutereply.cpp new file mode 100644 index 0000000..de406b4 --- /dev/null +++ b/tests/auto/qgeoroutereply/tst_qgeoroutereply.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_qgeoroutereply.h" + +QT_USE_NAMESPACE + +void tst_QGeoRouteReply::initTestCase() +{ + qgeocoordinate1 = new QGeoCoordinate(43.5435 , 76.342); + qgeocoordinate2 = new QGeoCoordinate(-43.5435 , 176.342); + qgeocoordinate3 = new QGeoCoordinate(-13.5435 , +76.342); + + waypoints.append(*qgeocoordinate1); + waypoints.append(*qgeocoordinate2); + waypoints.append(*qgeocoordinate3); + + qgeorouterequest = new QGeoRouteRequest(waypoints); + reply = new SubRouteReply(*qgeorouterequest); +} + +void tst_QGeoRouteReply::cleanupTestCase() +{ + delete qgeocoordinate1; + delete qgeocoordinate2; + delete qgeocoordinate3; + delete qgeorouterequest; + delete reply; +} + +void tst_QGeoRouteReply::init() +{ + qRegisterMetaType(); + signalerror = new QSignalSpy(reply, SIGNAL(error(QGeoRouteReply::Error,QString))); + signalfinished = new QSignalSpy(reply, SIGNAL(finished())); +} + +void tst_QGeoRouteReply::cleanup() +{ + delete signalerror; + delete signalfinished; +} + +void tst_QGeoRouteReply::constructor() +{ + QVERIFY(!reply->isFinished()); + QCOMPARE(reply->error(), QGeoRouteReply::NoError); + QCOMPARE(reply->request(), *qgeorouterequest); + + QVERIFY(signalerror->isValid()); + QVERIFY(signalfinished->isValid()); + + QCOMPARE(signalerror->count(),0); + QCOMPARE(signalfinished->count(),0); +} + +void tst_QGeoRouteReply::constructor_error() +{ + QFETCH(QGeoRouteReply::Error,error); + QFETCH(QString,msg); + + QVERIFY(signalerror->isValid()); + QVERIFY(signalfinished->isValid()); + + QGeoRouteReply *qgeoroutereplycopy = new QGeoRouteReply(error, msg, 0); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 0); + + QVERIFY(qgeoroutereplycopy->isFinished()); + QCOMPARE(qgeoroutereplycopy->error(), error); + QCOMPARE(qgeoroutereplycopy->errorString(), msg); + + delete qgeoroutereplycopy; +} + +void tst_QGeoRouteReply::constructor_error_data() +{ + QTest::addColumn("error"); + QTest::addColumn("msg"); + + QTest::newRow("error1") << QGeoRouteReply::NoError << "No error."; + QTest::newRow("error2") << QGeoRouteReply::EngineNotSetError << "Engine Not Set Error."; + QTest::newRow("error3") << QGeoRouteReply::CommunicationError << "Communication Error."; + QTest::newRow("error4") << QGeoRouteReply::ParseError << "Parse Error."; + QTest::newRow("error5") << QGeoRouteReply::UnsupportedOptionError << "Unsupported Option Error."; + QTest::newRow("error6") << QGeoRouteReply::UnknownError << "Unknown Error."; + +} + +void tst_QGeoRouteReply::destructor() +{ + QGeoRouteReply *qgeoroutereplycopy; + + QFETCH(QGeoRouteReply::Error, error); + QFETCH(QString, msg); + + qgeoroutereplycopy = new QGeoRouteReply(error, msg, 0); + delete qgeoroutereplycopy; + +} + +void tst_QGeoRouteReply::destructor_data() +{ + tst_QGeoRouteReply::constructor_error_data(); +} + +void tst_QGeoRouteReply::routes() +{ + QList routes; + QGeoRoute *route1 = new QGeoRoute(); + QGeoRoute *route2 = new QGeoRoute(); + + route1->setDistance(15.12); + route2->setDistance(20.12); + + routes.append(*route1); + routes.append(*route2); + + reply->callSetRoutes(routes); + + QList routescopy; + routescopy = reply->routes(); + QCOMPARE(routescopy,routes); + + QCOMPARE(routescopy.at(0).distance(),route1->distance()); + QCOMPARE(routescopy.at(1).distance(),route2->distance()); + + delete route1; + delete route2; +} + +void tst_QGeoRouteReply::finished() +{ + QVERIFY(signalerror->isValid()); + QVERIFY(signalfinished->isValid()); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 0); + + reply->callSetFinished(true); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 1); + + reply->callSetFinished(false); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 1); + + reply->callSetFinished(true); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 2); +} + +void tst_QGeoRouteReply::abort() +{ + QVERIFY(signalerror->isValid()); + QVERIFY(signalfinished->isValid()); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 0); + + reply->abort(); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 0); + + reply->abort(); + reply->callSetFinished(false); + reply->abort(); + + QCOMPARE(signalerror->count(), 0); + QCOMPARE(signalfinished->count(), 0); +} + +void tst_QGeoRouteReply::error() +{ + QFETCH(QGeoRouteReply::Error, error); + QFETCH(QString, msg); + + QVERIFY(signalerror->isValid()); + QVERIFY(signalfinished->isValid()); + QCOMPARE(signalerror->count(), 0); + + reply->callSetError(error,msg); + + QCOMPARE(signalerror->count(), 1); + QCOMPARE(signalfinished->count(), 1); + QCOMPARE(reply->errorString(), msg); + QCOMPARE(reply->error(), error); +} + +void tst_QGeoRouteReply::error_data() +{ + QTest::addColumn("error"); + QTest::addColumn("msg"); + + QTest::newRow("error1") << QGeoRouteReply::NoError << "No error."; + QTest::newRow("error2") << QGeoRouteReply::EngineNotSetError << "Engine Not Set Error."; + QTest::newRow("error3") << QGeoRouteReply::CommunicationError << "Communication Error."; + QTest::newRow("error4") << QGeoRouteReply::ParseError << "Parse Error."; + QTest::newRow("error5") << QGeoRouteReply::UnsupportedOptionError << "Unsupported Option Error."; + QTest::newRow("error6") << QGeoRouteReply::UnknownError << "Unknown Error."; +} + +void tst_QGeoRouteReply::request() +{ + SubRouteReply *rr = new SubRouteReply(*qgeorouterequest); + + QCOMPARE(rr->request(), *qgeorouterequest); + + delete rr; +} + +QTEST_APPLESS_MAIN(tst_QGeoRouteReply); diff --git a/tests/auto/qgeoroutereply/tst_qgeoroutereply.h b/tests/auto/qgeoroutereply/tst_qgeoroutereply.h new file mode 100644 index 0000000..aa08d72 --- /dev/null +++ b/tests/auto/qgeoroutereply/tst_qgeoroutereply.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_QGEOROUTEREPLY_H +#define TST_QGEOROUTEREPLY_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +QT_USE_NAMESPACE +class SubRouteReply :public QGeoRouteReply +{ + Q_OBJECT +public: + SubRouteReply(QGeoRouteRequest request):QGeoRouteReply(request) {} + void callSetError(QGeoRouteReply::Error error, QString msg) {setError(error,msg);} + void callSetFinished(bool finished) {setFinished(finished);} + void callSetRoutes(const QList & routes ) {setRoutes(routes);} +}; + +class tst_QGeoRouteReply :public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + //Start Unit Test for QGeoRouteReply +private slots: + void constructor(); + void constructor_error(); + void constructor_error_data(); + void destructor(); + void destructor_data(); + void routes(); + void finished(); + void abort(); + void error(); + void error_data(); + void request(); + //End Unit Test for QGeoRouteReply + + +private: + QGeoCoordinate *qgeocoordinate1; + QGeoCoordinate *qgeocoordinate2; + QGeoCoordinate *qgeocoordinate3; + QGeoRouteRequest *qgeorouterequest; + QSignalSpy *signalerror; + QSignalSpy *signalfinished; + QList waypoints; + SubRouteReply* reply; +}; + +Q_DECLARE_METATYPE( QList); +Q_DECLARE_METATYPE( QList); +Q_DECLARE_METATYPE( QList); +Q_DECLARE_METATYPE( QGeoRouteReply::Error); + +#endif // TST_QGEOROUTEREPLY_H + diff --git a/tests/auto/qgeorouterequest/qgeorouterequest.pro b/tests/auto/qgeorouterequest/qgeorouterequest.pro new file mode 100644 index 0000000..1fc897c --- /dev/null +++ b/tests/auto/qgeorouterequest/qgeorouterequest.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeorouterequest + +# Input +HEADERS += tst_qgeorouterequest.h +SOURCES += tst_qgeorouterequest.cpp + +QT += location testlib diff --git a/tests/auto/qgeorouterequest/tst_qgeorouterequest.cpp b/tests/auto/qgeorouterequest/tst_qgeorouterequest.cpp new file mode 100644 index 0000000..d29e3d4 --- /dev/null +++ b/tests/auto/qgeorouterequest/tst_qgeorouterequest.cpp @@ -0,0 +1,351 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_qgeorouterequest.h" + +#include + +QT_USE_NAMESPACE + +tst_QGeoRouteRequest::tst_QGeoRouteRequest() +{ +} + +void tst_QGeoRouteRequest::initTestCase() +{ +} + +void tst_QGeoRouteRequest::cleanupTestCase() +{ +} + +void tst_QGeoRouteRequest::init() +{ + qgeocoordinate = new QGeoCoordinate(); + qgeoboundingbox = new QGeoRectangle(); + qgeorouterequest = new QGeoRouteRequest(); +} + +void tst_QGeoRouteRequest::cleanup() +{ + delete qgeocoordinate; + delete qgeoboundingbox; + delete qgeorouterequest; +} + +void tst_QGeoRouteRequest::constructor_waypoints() +{ + QGeoCoordinate *qgeocoord1 = new QGeoCoordinate(43.5435, 76.342); + QGeoCoordinate *qgeocoord2 = new QGeoCoordinate(-43.5435, 176.342); + QGeoCoordinate *qgeocoord3 = new QGeoCoordinate(-13.5435, +76.342); + + QList waypoints; + waypoints.append(*qgeocoord1); + waypoints.append(*qgeocoord2); + waypoints.append(*qgeocoord3); + + QGeoRouteRequest *copy = new QGeoRouteRequest(waypoints); + + QCOMPARE(copy->waypoints(), waypoints); + QCOMPARE(copy->numberAlternativeRoutes(), 0); + QCOMPARE(copy->routeOptimization(), QGeoRouteRequest::FastestRoute); + QCOMPARE(copy->segmentDetail(), QGeoRouteRequest::BasicSegmentData); + QCOMPARE(copy->travelModes(), QGeoRouteRequest::CarTravel); + QCOMPARE(copy->maneuverDetail(), QGeoRouteRequest::BasicManeuvers); + + delete qgeocoord1; + delete qgeocoord2; + delete qgeocoord3; + delete copy; +} + +void tst_QGeoRouteRequest::constructor_orig_dest() +{ + QGeoCoordinate *qgeocoord1 = new QGeoCoordinate(43.5435, 76.342); + QGeoCoordinate *qgeocoord2 = new QGeoCoordinate(-43.5435, 176.342); + + QGeoRouteRequest *copy = new QGeoRouteRequest(*qgeocoord1, *qgeocoord2); + + QList waypoints; + waypoints.append(*qgeocoord1); + waypoints.append(*qgeocoord2); + + QCOMPARE(copy->waypoints(), waypoints); + QCOMPARE(copy->numberAlternativeRoutes(), 0); + QCOMPARE(copy->routeOptimization(), QGeoRouteRequest::FastestRoute); + QCOMPARE(copy->segmentDetail(), QGeoRouteRequest::BasicSegmentData); + QCOMPARE(copy->travelModes(), QGeoRouteRequest::CarTravel); + QCOMPARE(copy->maneuverDetail(), QGeoRouteRequest::BasicManeuvers); + + delete qgeocoord1; + delete qgeocoord2; + delete copy; +} + +void tst_QGeoRouteRequest::copy_constructor() +{ + QGeoRouteRequest *qgeorouterequestcopy = new QGeoRouteRequest(*qgeorouterequest); + QCOMPARE(*qgeorouterequest, *qgeorouterequestcopy); + delete qgeorouterequestcopy; +} + +void tst_QGeoRouteRequest::destructor() +{ + QGeoRouteRequest *qgeorouterequestcopy; + + qgeorouterequestcopy = new QGeoRouteRequest(); + delete qgeorouterequestcopy; + + qgeorouterequestcopy = new QGeoRouteRequest(*qgeorouterequest); + delete qgeorouterequestcopy; +} + +void tst_QGeoRouteRequest::excludeAreas() +{ + qgeocoordinate->setLatitude(13.3851); + qgeocoordinate->setLongitude(52.5312); + + QGeoCoordinate *qgeocoordinatecopy = new QGeoCoordinate(34.324 , -110.32); + + QGeoRectangle *qgeoboundingboxcopy = new QGeoRectangle(*qgeocoordinate, 0.4, 0.4); + QGeoRectangle *qgeoboundingboxcopy2 = new QGeoRectangle(*qgeocoordinatecopy, 1.2, 0.9); + QList areas; + areas.append(*qgeoboundingboxcopy); + areas.append(*qgeoboundingboxcopy2); + + qgeorouterequest->setExcludeAreas(areas); + + QCOMPARE(qgeorouterequest->excludeAreas(), areas); + + delete qgeoboundingboxcopy; + delete qgeoboundingboxcopy2; + delete qgeocoordinatecopy; +} + +void tst_QGeoRouteRequest::numberAlternativeRoutes() +{ + qgeorouterequest->setNumberAlternativeRoutes(0); + QCOMPARE(qgeorouterequest->numberAlternativeRoutes(), 0); + + qgeorouterequest->setNumberAlternativeRoutes(24); + QCOMPARE(qgeorouterequest->numberAlternativeRoutes(), 24); + + qgeorouterequest->setNumberAlternativeRoutes(-12); + + QCOMPARE(qgeorouterequest->numberAlternativeRoutes(), 0); +} + +void tst_QGeoRouteRequest::routeOptimization() +{ + QFETCH(QGeoRouteRequest::RouteOptimization, optimization); + + QCOMPARE(qgeorouterequest->routeOptimization(),QGeoRouteRequest::FastestRoute); + + qgeorouterequest->setRouteOptimization(optimization); + QCOMPARE(qgeorouterequest->routeOptimization(), optimization); +} + +void tst_QGeoRouteRequest::routeOptimization_data() +{ + QTest::addColumn("optimization"); + + QTest::newRow("optimization1") << QGeoRouteRequest::FastestRoute; + QTest::newRow("optimization2") << QGeoRouteRequest::ShortestRoute; + QTest::newRow("optimization3") << QGeoRouteRequest::MostEconomicRoute; + QTest::newRow("optimization4") << QGeoRouteRequest::MostScenicRoute; + +} + +void tst_QGeoRouteRequest::segmentDetail() +{ + QFETCH(QGeoRouteRequest::SegmentDetail, detail); + + QCOMPARE(qgeorouterequest->segmentDetail(), QGeoRouteRequest::BasicSegmentData); + + qgeorouterequest->setSegmentDetail(detail); + QCOMPARE(qgeorouterequest->segmentDetail(), detail); +} + +void tst_QGeoRouteRequest::segmentDetail_data() +{ + QTest::addColumn("detail"); + + QTest::newRow("detail1") << QGeoRouteRequest::NoSegmentData; + QTest::newRow("detail2") << QGeoRouteRequest::BasicSegmentData; +} + +void tst_QGeoRouteRequest::travelModes() +{ + QFETCH(QGeoRouteRequest::TravelMode,mode); + + QCOMPARE(qgeorouterequest->travelModes(), QGeoRouteRequest::CarTravel); + + qgeorouterequest->setTravelModes(mode); + QCOMPARE(qgeorouterequest->travelModes(), mode); +} + +void tst_QGeoRouteRequest::travelModes_data() +{ + QTest::addColumn("mode"); + + QTest::newRow("mode1") << QGeoRouteRequest::CarTravel; + QTest::newRow("mode2") << QGeoRouteRequest::PedestrianTravel; + QTest::newRow("mode3") << QGeoRouteRequest::BicycleTravel; + QTest::newRow("mode4") << QGeoRouteRequest::PublicTransitTravel; + QTest::newRow("mode5") << QGeoRouteRequest::TruckTravel; +} + +void tst_QGeoRouteRequest::waypoints() +{ + QFETCH(QList, coordinates); + + QList waypoints; + + for (int i = 0; i < coordinates.size(); i += 2) { + waypoints.append(QGeoCoordinate(coordinates.at(i), coordinates.at(i+1))); + } + + qgeorouterequest->setWaypoints(waypoints); + + QList waypointsRetrieved = qgeorouterequest->waypoints(); + QCOMPARE(waypointsRetrieved, waypoints); + + for (int i=0; i < waypointsRetrieved.size(); i++) { + QCOMPARE(waypointsRetrieved.at(i), waypoints.at(i)); + } +} + +void tst_QGeoRouteRequest::waypoints_data() +{ + QTest::addColumn >("coordinates"); + + QList coordinates; + + coordinates << 0.0 << 0.0; + QTest::newRow("path1") << coordinates ; + + coordinates << -23.23 << 54.45345; + QTest::newRow("path2") << coordinates ; + + coordinates << -85.4324 << -121.343; + QTest::newRow("path3") << coordinates ; + + coordinates << 1.323 << 12.323; + QTest::newRow("path4") << coordinates ; + + coordinates << 1324.323 << 143242.323; + QTest::newRow("path5") << coordinates ; +} + +void tst_QGeoRouteRequest::maneuverDetail() +{ + QFETCH(QGeoRouteRequest::ManeuverDetail,maneuver); + + QCOMPARE(qgeorouterequest->maneuverDetail(), QGeoRouteRequest::BasicManeuvers); + + qgeorouterequest->setManeuverDetail(maneuver); + QCOMPARE(qgeorouterequest->maneuverDetail(), maneuver); +} + +void tst_QGeoRouteRequest::maneuverDetail_data() +{ + QTest::addColumn("maneuver"); + + QTest::newRow("maneuver1") << QGeoRouteRequest::NoManeuvers; + QTest::newRow("maneuver2") << QGeoRouteRequest::BasicManeuvers; + +} + +void tst_QGeoRouteRequest::featureWeight_data() +{ + QTest::addColumn("type"); + QTest::addColumn("checkTypes"); + QTest::addColumn("initialWeight"); + QTest::addColumn("newWeight"); + QTest::addColumn("expectWeight"); + + QTest::newRow("NoFeature") << QGeoRouteRequest::NoFeature << false + << QGeoRouteRequest::NeutralFeatureWeight + << QGeoRouteRequest::PreferFeatureWeight + << QGeoRouteRequest::NeutralFeatureWeight; + QTest::newRow("TollFeature") << QGeoRouteRequest::TollFeature << true + << QGeoRouteRequest::NeutralFeatureWeight + << QGeoRouteRequest::PreferFeatureWeight + << QGeoRouteRequest::PreferFeatureWeight; + QTest::newRow("HighwayFeature") << QGeoRouteRequest::HighwayFeature << true + << QGeoRouteRequest::NeutralFeatureWeight + << QGeoRouteRequest::RequireFeatureWeight + << QGeoRouteRequest::RequireFeatureWeight; +} + +void tst_QGeoRouteRequest::featureWeight() +{ + QFETCH(QGeoRouteRequest::FeatureType, type); + QFETCH(bool, checkTypes); + QFETCH(QGeoRouteRequest::FeatureWeight, initialWeight); + QFETCH(QGeoRouteRequest::FeatureWeight, newWeight); + QFETCH(QGeoRouteRequest::FeatureWeight, expectWeight); + + QCOMPARE(qgeorouterequest->featureWeight(type), initialWeight); + qgeorouterequest->setFeatureWeight(type, newWeight); + QCOMPARE(qgeorouterequest->featureWeight(type), expectWeight); + + if (checkTypes) + QVERIFY(qgeorouterequest->featureTypes().contains(type)); +} + +void tst_QGeoRouteRequest::extraParameters_data() +{ + QTest::addColumn("extraParameters"); + + QVariantMap params; + QTest::newRow("Empty") << params; + + const QVariantMap param1 = {{"property1", QVariant(42)} , {"property2", QVariant("42")} , {"property3", QVariant("42.0")}}; + params["param1"] = param1; + + QTest::newRow("One param") << params; + + const QVariantMap param2 = {{"property1", QVariant(43)} , {"property2", QVariant("43")} , {"property3", QVariant("43.0")}}; + params["param2"] = param2; + + QTest::newRow("Two params") << params; +} + +void tst_QGeoRouteRequest::extraParameters() +{ + typedef QVariantMap ParameterType; + QFETCH(ParameterType , extraParameters); + QVariantMap emptyParams; + qgeorouterequest->setExtraParameters(extraParameters); + QCOMPARE(qgeorouterequest->extraParameters(), extraParameters); + qgeorouterequest->setExtraParameters(emptyParams); + QCOMPARE(qgeorouterequest->extraParameters(), emptyParams); +} + +QTEST_APPLESS_MAIN(tst_QGeoRouteRequest); diff --git a/tests/auto/qgeorouterequest/tst_qgeorouterequest.h b/tests/auto/qgeorouterequest/tst_qgeorouterequest.h new file mode 100644 index 0000000..12506cf --- /dev/null +++ b/tests/auto/qgeorouterequest/tst_qgeorouterequest.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_QGEOROUTEREQUEST_H +#define TST_QGEOROUTEREQUEST_H + +#include +#include +#include + +#include +#include +#include + + +QT_USE_NAMESPACE + +class tst_QGeoRouteRequest : public QObject +{ + Q_OBJECT + +public: + tst_QGeoRouteRequest(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + //Start Unit Test for QGeoRouteRequest + void constructor_waypoints(); + void constructor_orig_dest(); + void copy_constructor(); + void destructor(); + void excludeAreas(); + void numberAlternativeRoutes(); + void routeOptimization(); + void routeOptimization_data(); + void segmentDetail(); + void segmentDetail_data(); + void travelModes(); + void travelModes_data(); + void waypoints(); + void waypoints_data(); + void maneuverDetail(); + void maneuverDetail_data(); + void featureWeight(); + void featureWeight_data(); + void extraParameters(); + void extraParameters_data(); + //End Unit Test for QGeoRouteRequest + +private: + QGeoCoordinate *qgeocoordinate; + QGeoRectangle *qgeoboundingbox; + QGeoRouteRequest *qgeorouterequest; + +}; + +Q_DECLARE_METATYPE( QList); +Q_DECLARE_METATYPE( QGeoRouteRequest::RouteOptimization ); +Q_DECLARE_METATYPE( QGeoRouteRequest::SegmentDetail ); +Q_DECLARE_METATYPE( QGeoRouteRequest::TravelMode ); +Q_DECLARE_METATYPE( QGeoRouteRequest::FeatureType); +Q_DECLARE_METATYPE( QGeoRouteRequest::FeatureWeight); +Q_DECLARE_METATYPE( QGeoRouteRequest::ManeuverDetail ); + +#endif // TST_QGEOROUTEREQUEST_H + diff --git a/tests/auto/qgeoroutesegment/qgeoroutesegment.pro b/tests/auto/qgeoroutesegment/qgeoroutesegment.pro new file mode 100644 index 0000000..413363f --- /dev/null +++ b/tests/auto/qgeoroutesegment/qgeoroutesegment.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeoroutesegment + +# Input +HEADERS += tst_qgeoroutesegment.h +SOURCES += tst_qgeoroutesegment.cpp + +QT += location testlib diff --git a/tests/auto/qgeoroutesegment/tst_qgeoroutesegment.cpp b/tests/auto/qgeoroutesegment/tst_qgeoroutesegment.cpp new file mode 100644 index 0000000..ebe2b56 --- /dev/null +++ b/tests/auto/qgeoroutesegment/tst_qgeoroutesegment.cpp @@ -0,0 +1,302 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "tst_qgeoroutesegment.h" + +QT_USE_NAMESPACE + +tst_QGeoRouteSegment::tst_QGeoRouteSegment() +{ +} + +void tst_QGeoRouteSegment::initTestCase() +{ +} + +void tst_QGeoRouteSegment::cleanupTestCase() +{ +} + +void tst_QGeoRouteSegment::init() +{ + qgeocoordinate = new QGeoCoordinate(); + qgeoroutesegment = new QGeoRouteSegment(); + qgeomaneuver = new QGeoManeuver(); +} + +void tst_QGeoRouteSegment::cleanup() +{ + delete qgeocoordinate; + delete qgeoroutesegment; +} + +void tst_QGeoRouteSegment::constructor() +{ + QVERIFY(!qgeoroutesegment->isValid()); + QCOMPARE(qgeoroutesegment->distance(), qreal(0.0)); + QCOMPARE(qgeoroutesegment->maneuver(),*qgeomaneuver); + QCOMPARE(qgeoroutesegment->travelTime(),0); +} + +void tst_QGeoRouteSegment::copy_constructor() +{ + QGeoRouteSegment *qgeoroutesegmentcopy = new QGeoRouteSegment(*qgeoroutesegment); + + QCOMPARE(*qgeoroutesegment, *qgeoroutesegmentcopy); + + QVERIFY(!qgeoroutesegmentcopy->isValid()); + QCOMPARE(qgeoroutesegmentcopy->distance(), qreal(0.0)); + QCOMPARE(qgeoroutesegmentcopy->maneuver(), *qgeomaneuver); + QCOMPARE(qgeoroutesegmentcopy->travelTime(), 0); + + delete qgeoroutesegmentcopy; +} + +void tst_QGeoRouteSegment::destructor() +{ + QGeoRouteSegment *qgeoroutesegmentcopy; + + qgeoroutesegmentcopy = new QGeoRouteSegment(); + delete qgeoroutesegmentcopy; + + qgeoroutesegmentcopy = new QGeoRouteSegment(*qgeoroutesegment); + delete qgeoroutesegmentcopy; +} + + +void tst_QGeoRouteSegment::travelTime() +{ + QFETCH(int, traveltime); + + QGeoRouteSegment sgmt; + QVERIFY(!sgmt.isValid()); + + sgmt.setTravelTime(traveltime); + + QVERIFY(sgmt.isValid()); + QCOMPARE(sgmt.travelTime(), traveltime); +} + +void tst_QGeoRouteSegment::travelTime_data() +{ + QTest::addColumn("traveltime"); + + QTest::newRow("travel1") << 0; + QTest::newRow("travel2") << -50; + QTest::newRow("travel3") << 324556; +} + +void tst_QGeoRouteSegment::distance() +{ + QFETCH(qreal, distance); + + QGeoRouteSegment sgmt; + QVERIFY(!sgmt.isValid()); + + sgmt.setDistance(distance); + + QVERIFY(sgmt.isValid()); + QCOMPARE(sgmt.distance(), distance); +} + +void tst_QGeoRouteSegment::distance_data() +{ + QTest::addColumn("distance"); + + QTest::newRow("distance1") << qreal(0.0) ; + QTest::newRow("distance2") << qreal(-50.3435) ; + QTest::newRow("distance3") << qreal(324556.543534) ; +} + + +void tst_QGeoRouteSegment::path() +{ + QFETCH(QList, coordinates); + + QGeoRouteSegment sgmt; + QVERIFY(!sgmt.isValid()); + + QList path; + + for (int i = 0; i < coordinates.size(); i += 2) { + path.append(QGeoCoordinate(coordinates.at(i), coordinates.at(i+1))); + } + + sgmt.setPath(path); + QVERIFY(sgmt.isValid()); + + QList pathretrieved = sgmt.path(); + QCOMPARE(pathretrieved, path); + + for (int i = 0; i < pathretrieved.size(); i++) { + QCOMPARE(pathretrieved.at(i), path.at(i)); + } +} + +void tst_QGeoRouteSegment::path_data() +{ + QTest::addColumn >("coordinates"); + + QList coordinates; + + coordinates << 0.0 << 0.0; + QTest::newRow("path1") << coordinates; + + coordinates << -23.23 << 54.45345; + QTest::newRow("path2") << coordinates; + + coordinates << -85.4324 << -121.343; + QTest::newRow("path3") << coordinates; + + coordinates << 1.323 << 12.323; + QTest::newRow("path4") << coordinates; + + coordinates << 1324.323 << 143242.323; + QTest::newRow("path5") << coordinates; +} + +void tst_QGeoRouteSegment::nextroutesegment() +{ + QGeoRouteSegment sgmt; + QVERIFY(!sgmt.isValid()); + + QGeoRouteSegment *qgeoroutesegmentcopy = new QGeoRouteSegment(); + qgeoroutesegmentcopy->setDistance(45.34); + + sgmt.setNextRouteSegment(*qgeoroutesegmentcopy); + + QVERIFY(sgmt.isValid()); + QCOMPARE(sgmt.nextRouteSegment(), *qgeoroutesegmentcopy); + + QCOMPARE((sgmt.nextRouteSegment()).distance(), + qgeoroutesegmentcopy->distance()); + delete qgeoroutesegmentcopy; + +} + +void tst_QGeoRouteSegment::maneuver() +{ + QGeoRouteSegment sgmt; + QVERIFY(!sgmt.isValid()); + + sgmt.setManeuver(*qgeomaneuver); + QCOMPARE(sgmt.maneuver(), *qgeomaneuver); + QVERIFY(sgmt.isValid()); +} + +void tst_QGeoRouteSegment::operators() +{ + //Create a copy and see that they are the same + QGeoRouteSegment *qgeoroutesegmentcopy = new QGeoRouteSegment(*qgeoroutesegment); + QGeoRouteSegment *trueSgmtCopy = new QGeoRouteSegment(); + + QVERIFY(qgeoroutesegment->operator ==(*qgeoroutesegmentcopy)); + QVERIFY(!qgeoroutesegment->operator !=(*qgeoroutesegmentcopy)); + QVERIFY(!qgeoroutesegment->isValid()); + QVERIFY(!qgeoroutesegmentcopy->isValid()); + + //Same segment ? content is the same but pointer different + QVERIFY(qgeoroutesegment->operator ==(*trueSgmtCopy)); + QVERIFY(!qgeoroutesegment->operator !=(*trueSgmtCopy)); + QVERIFY(!trueSgmtCopy->isValid()); + + QFETCH(double, distance); + QFETCH(int, traveltime); + QFETCH(QList, coordinates); + + QList path; + + for (int i = 0; i < coordinates.size(); i += 2) { + path.append(QGeoCoordinate(coordinates.at(i), coordinates.at(i+1))); + } + + qgeoroutesegment->setDistance(distance); + qgeoroutesegment->setTravelTime(traveltime); + qgeoroutesegment->setPath(path); + + trueSgmtCopy->setDistance(distance); + trueSgmtCopy->setTravelTime(traveltime); + trueSgmtCopy->setPath(path); + + QCOMPARE(qgeoroutesegment->distance(), distance); + QCOMPARE(qgeoroutesegment->travelTime(), traveltime); + QCOMPARE(qgeoroutesegment->path(), path); + + QCOMPARE(qgeoroutesegmentcopy->distance(), distance); + QCOMPARE(qgeoroutesegmentcopy->travelTime(), traveltime); + QCOMPARE(qgeoroutesegmentcopy->path(), path); + + QCOMPARE(trueSgmtCopy->distance(), distance); + QCOMPARE(trueSgmtCopy->travelTime(), traveltime); + QCOMPARE(trueSgmtCopy->path(), path); + + //Same as based off copy constructor (d-pointer shared) + QVERIFY(qgeoroutesegment->operator==(*qgeoroutesegmentcopy)); + QVERIFY(!qgeoroutesegment->operator!=(*qgeoroutesegmentcopy)); + + //Same as based off same content although different d-pointer + QVERIFY(qgeoroutesegment->operator==(*trueSgmtCopy)); + QVERIFY(!qgeoroutesegment->operator!=(*trueSgmtCopy)); + + const int newTravelTime = 111; + trueSgmtCopy->setTravelTime(newTravelTime); + QCOMPARE(trueSgmtCopy->travelTime(), newTravelTime); + + //different d-pointer and different content + QVERIFY(!qgeoroutesegment->operator==(*trueSgmtCopy)); + QVERIFY(qgeoroutesegment->operator!=(*trueSgmtCopy)); + + //Assign one segment to the other and test that they are the same again + *qgeoroutesegmentcopy = qgeoroutesegmentcopy->operator =(*qgeoroutesegment); + QVERIFY(qgeoroutesegment->operator==(*qgeoroutesegmentcopy)); + QVERIFY(!qgeoroutesegment->operator!=(*qgeoroutesegmentcopy)); + + *qgeoroutesegmentcopy = qgeoroutesegmentcopy->operator =(*trueSgmtCopy); + QVERIFY(trueSgmtCopy->operator==(*qgeoroutesegmentcopy)); + QVERIFY(!trueSgmtCopy->operator!=(*qgeoroutesegmentcopy)); + + delete trueSgmtCopy; + delete qgeoroutesegmentcopy; +} + +void tst_QGeoRouteSegment::operators_data() +{ + QTest::addColumn("distance"); + QTest::addColumn("traveltime"); + QTest::addColumn >("coordinates"); + + QList coordinates; + + coordinates << 0.0 << 0.0 << 23.234 << -121.767 << 8.43534 << 32.789; + QTest::newRow("set1") << 143545.43 << 45665 << coordinates; + + coordinates << 42.343 << -38.657; + QTest::newRow("set2") << 745654.43 << 786585 << coordinates; +} + +QTEST_APPLESS_MAIN(tst_QGeoRouteSegment); diff --git a/tests/auto/qgeoroutesegment/tst_qgeoroutesegment.h b/tests/auto/qgeoroutesegment/tst_qgeoroutesegment.h new file mode 100644 index 0000000..628293d --- /dev/null +++ b/tests/auto/qgeoroutesegment/tst_qgeoroutesegment.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TST_QGEOROUTESEGMENT_H +#define TST_QGEOROUTESEGMENT_H + +#include +#include +#include + +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoRouteSegment : public QObject +{ + Q_OBJECT + +public: + tst_QGeoRouteSegment(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + + //Start unit test for QGeoRouteSegment + void constructor(); + void copy_constructor(); + void destructor(); + void travelTime(); + void travelTime_data(); + void distance(); + void distance_data(); + void path(); + void path_data(); + void maneuver(); + void nextroutesegment(); + void operators(); + void operators_data(); + //End Unit Test for QGeoRouteSegment + +private: + + QGeoCoordinate *qgeocoordinate; + QGeoRouteSegment *qgeoroutesegment; + QGeoManeuver *qgeomaneuver; + +}; + +Q_DECLARE_METATYPE( QList); + +#endif // TST_GEOROUTESEGMENT_H + diff --git a/tests/auto/qgeoroutexmlparser/fixtures.qrc b/tests/auto/qgeoroutexmlparser/fixtures.qrc new file mode 100644 index 0000000..8288ea0 --- /dev/null +++ b/tests/auto/qgeoroutexmlparser/fixtures.qrc @@ -0,0 +1,6 @@ + + + route1.xml + route2.xml + + diff --git a/tests/auto/qgeoroutexmlparser/qgeoroutexmlparser.pro b/tests/auto/qgeoroutexmlparser/qgeoroutexmlparser.pro new file mode 100644 index 0000000..f9da965 --- /dev/null +++ b/tests/auto/qgeoroutexmlparser/qgeoroutexmlparser.pro @@ -0,0 +1,13 @@ +CONFIG += testcase +TARGET = tst_qgeoroutexmlparser + +plugin.path = ../../../src/plugins/geoservices/nokia/ + +SOURCES += tst_qgeoroutexmlparser.cpp \ + $$plugin.path/qgeoroutexmlparser.cpp +HEADERS += $$plugin.path/qgeoroutexmlparser.h +INCLUDEPATH += $$plugin.path +RESOURCES += fixtures.qrc + +QT += location testlib + diff --git a/tests/auto/qgeoroutexmlparser/route1.xml b/tests/auto/qgeoroutexmlparser/route1.xml new file mode 100644 index 0000000..a66f4b4 --- /dev/null +++ b/tests/auto/qgeoroutexmlparser/route1.xml @@ -0,0 +1 @@ +2012-02-21T02:51:16.997+01:002012-02-21T02:50:00.025+010046415962012-02-21T02:50:02.008+0100143512012-02-21T02:50:02.008+010045802011Q2_DICIrouteserver,9.1-2011.12.20-hotfix6.2.11.151routing-route-service,6.2.11.2REMuJj4AAAAzMzMzM5M7wIlBYOXQImNAAAAAQEGTO8AAAACg0CJjQAAAAAAAAPB_AAAAAAAA8H9PWzP4XI_C9Sjc7QBv5_up8YKMAQEAAIAr3O0AAwAAgPKCjAEBAAAAAADA_wEAAAAAAMD_gTMrH2wAy2s1l8HnuszFwwBp-130852017-27.5752144153.0879669-27.575153.088stopOver+130731232-27.4650097153.0230255-27.465153.023stopOverfastestNowcarenabled-27.5752144,153.0879669 -27.5752602,153.0882263 -27.5753899,153.0891418 -27.5754299,153.089447 -27.5755901,153.0905914 -27.5756607,153.0911255 -27.5757198,153.091568 -27.5757504,153.0918121 -27.5757809,153.0920258 -27.5752907,153.0918121 -27.5749302,153.0916748 -27.5740795,153.0913239 -27.5734901,153.091095 -27.5731201,153.0909119 -27.5728893,153.0908051 -27.5723991,153.0906067 -27.5722103,153.0904999 -27.5719509,153.0903625 -27.5717106,153.0902557 -27.5714703,153.0901337 -27.5711498,153.0900116 -27.5710392,153.0898895 -27.5704498,153.0897064 -27.5699806,153.0894012 -27.5698109,153.0892334 -27.5696507,153.0890503 -27.5693398,153.0885468 -27.5685005,153.0870361 -27.5683002,153.0866852 -27.5681305,153.0865479 -27.5675297,153.0855865 -27.5662403,153.0835724 -27.5645599,153.0809937 -27.5645103,153.0809174 -27.5644493,153.0808258 -27.5643902,153.080719 -27.5632801,153.0790863 -27.5629597,153.0786438 -27.5626793,153.0782623 -27.5623398,153.0778046 -27.5618191,153.0771027 -27.5615292,153.0767517 -27.56147,153.0766754 -27.5613995,153.0765839 -27.5612907,153.0764771 -27.5612202,153.0764008 -27.5611191,153.0762939 -27.5605106,153.0756226 -27.5587692,153.0739288 -27.5560703,153.0716248 -27.5530605,153.0691223 -27.5518303,153.0680542 -27.5516701,153.0679169 -27.5500202,153.0665436 -27.5492992,153.0659485 -27.5465107,153.0635986 -27.5454407,153.0627136 -27.5446301,153.062027 -27.5444107,153.0618439 -27.5436707,153.0612946 -27.5428104,153.0607452 -27.5418606,153.0602264 -27.5410309,153.0598297 -27.5407505,153.0596924 -27.5401402,153.0594635 -27.5396996,153.0593262 -27.5380306,153.0588379 -27.5377598,153.0587616 -27.5372505,153.0586548 -27.5360794,153.0584106 -27.5345192,153.058075 -27.5335693,153.0578766 -27.5287495,153.0568542 -27.5279198,153.0566559 -27.5272694,153.0564575 -27.52668,153.0562439 -27.5265102,153.0561676 -27.5260201,153.055954 -27.5256195,153.0557709 -27.5253601,153.0556335 -27.5246906,153.0552826 -27.5240498,153.0548706 -27.5235901,153.0545044 -27.5233498,153.0543213 -27.5231991,153.0542145 -27.5209904,153.052475 -27.5201492,153.0517731 -27.5198994,153.0515747 -27.5198002,153.0514832 -27.5197201,153.0514374 -27.5196705,153.0513916 -27.5188999,153.0507507 -27.51824,153.0500336 -27.5175304,153.0490723 -27.5173092,153.0487061 -27.5169201,153.0479431 -27.5146999,153.0434113 -27.5142193,153.0424347 -27.5138206,153.0417633 -27.5135002,153.0413666 -27.5131302,153.0409546 -27.5129299,153.0407715 -27.5126896,153.0405731 -27.5122299,153.0402832 -27.5098591,153.0390167 -27.5095406,153.0388641 -27.5092793,153.0387726 -27.5090008,153.038681 -27.5088291,153.0386353 -27.5083199,153.0385437 -27.5077591,153.0385132 -27.5072899,153.0385437 -27.5068703,153.0386047 -27.5041294,153.0391083 -27.50317,153.0392914 -27.5017509,153.0395508 -27.5009995,153.0397034 -27.5007401,153.0397186 -27.5005703,153.0397339 -27.500351,153.0397339 -27.5001106,153.0397339 -27.4993591,153.0396729 -27.4989605,153.039566 -27.4988594,153.0395508 -27.49823,153.0393066 -27.4977093,153.0390167 -27.4974995,153.0388794 -27.4974003,153.0388031 -27.4970894,153.0386047 -27.4968204,153.0384064 -27.4967098,153.0383148 -27.4961395,153.0379028 -27.4959297,153.037735 -27.4958897,153.0377045 -27.4957695,153.0376129 -27.4948807,153.0368958 -27.4944096,153.0365143 -27.4940605,153.0361481 -27.4939404,153.0359955 -27.4934292,153.0354004 -27.4932709,153.0351868 -27.4930305,153.0348358 -27.4929104,153.0346222 -27.4927597,153.0343933 -27.4926701,153.0342712 -27.4923592,153.033844 -27.4920998,153.0335999 -27.4918404,153.0334167 -27.4914608,153.0332336 -27.4911499,153.0330963 -27.4907398,153.0330048 -27.4899998,153.0329742 -27.48983,153.0329437 -27.4895992,153.0329132 -27.48946,153.0328827 -27.4889603,153.0328369 -27.4886894,153.0327454 -27.4882698,153.032608 -27.4881802,153.0325928 -27.4880905,153.0325623 -27.4880009,153.032547 -27.4879494,153.0325165 -27.4859409,153.0318909 -27.4850197,153.031601 -27.4841309,153.031311 -27.4839497,153.03125 -27.4838505,153.0312195 -27.4837608,153.031189 -27.4836502,153.0311432 -27.4832993,153.0309906 -27.4827995,153.0307312 -27.4824696,153.0305176 -27.4823093,153.0303955 -27.4822197,153.0303345 -27.4821301,153.0302429 -27.4794807,153.0281525 -27.4792404,153.0279846 -27.4785404,153.0274506 -27.4784508,153.0273743 -27.4780502,153.0270691 -27.4775295,153.0266724 -27.4773006,153.026474 -27.4769592,153.0261993 -27.4766197,153.0259247 -27.4762897,153.0256653 -27.4761295,153.0255432 -27.4759293,153.0253906 -27.4754696,153.0250244 -27.4750309,153.0246735 -27.4747601,153.0244446 -27.4745407,153.0242767 -27.4742794,153.0240784 -27.4738808,153.0237732 -27.4737606,153.0236969 -27.4730091,153.0231171 -27.4729099,153.0230408 -27.4728107,153.0229645 -27.47258,153.0227966 -27.4724197,153.0226593 -27.4721909,153.0224609 -27.4719391,153.0222473 -27.4718609,153.022171 -27.4718399,153.0221558 -27.4718094,153.0221252 -27.4717293,153.0220337 -27.4714603,153.0217743 -27.4713306,153.021637 -27.47122,153.0215912 -27.4705105,153.0209045 -27.4703693,153.0207672 -27.4703102,153.0206757 -27.4700508,153.0203094 -27.4700203,153.0202637 -27.46982,153.0201416 -27.46978,153.0201263 -27.4697304,153.0201263 -27.4696808,153.0201263 -27.4696007,153.0201569 -27.4695091,153.0202332 -27.4692593,153.0205231 -27.4690895,153.0207825 -27.4690208,153.0209045 -27.46875,153.0212402 -27.4686909,153.0213165 -27.4685402,153.0215149 -27.4682808,153.0218353 -27.4678001,153.0224457 -27.46772,153.0225525 -27.4676704,153.0226288 -27.4676208,153.0226898 -27.4674492,153.0229187 -27.4673805,153.0230103 -27.46735,153.023056 -27.4669609,153.0235596 -27.4666996,153.0239258 -27.4664497,153.0242615 -27.4662895,153.0244598 -27.4661503,153.0246735 -27.4660702,153.024765 -27.4658794,153.0247192 -27.4658508,153.024704 -27.4657001,153.024704 -27.4656696,153.024704 -27.4654598,153.0247345 -27.4650002,153.0249329 -27.4648304,153.0250244 -27.4649105,153.024765 -27.4649601,153.0246735 -27.4653893,153.0240021 -27.4654293,153.0239258 -27.4655704,153.0236359 -27.46562,153.0235291 -27.4656105,153.0233459 -27.4655991,153.0231628 -27.4652405,153.0231628 -27.4652004,153.0231476 -27.4651394,153.0230408 -27.4650898,153.0230103 -27.4650307,153.0230103 -27.4640903,153.0233917-27.4640903153.0201263-27.5757809153.0920258-130852017-27.5752144153.0879669-27.575153.088stopOver+130731232-27.4650097153.0230255-27.465153.023stopOver15895.0812.2-27.5752144153.0879669Head toward Logan Rd (95) on Padstow Rd (56). Go for 0.3 miles.24.2403.0-130852017forward-27.5757809153.0920258Turn left onto Logan Rd (95). Go for 0.3 miles.54.8546.0+130851941left-27.5711498153.0900116Take exit #14/M3/City onto M3, Pacific Mtwy. Go for 8.5 miles.556.513578.0+778753942lightLeft-27.4713306153.021637Take exit #2/Turbot Street to the right onto Turbot St. Go for 0.5 miles.65.5840.0+778426174lightRight-27.4660702153.024765Turn left onto Edward St. Go for 450 feet.27.9141.0+840906308left-27.4648304153.0250244Turn sharp left onto Wickham Ter. Go for 0.1 miles.42.8206.0-842383988hardLeft-27.4655991153.0231628Turn right onto Bartley St. Go for 0.1 miles.40.6181.0+130731232right-27.4650097153.0230255Arrive at Bartley St. The trip takes 10 miles and 14 mins.0.00.0forward-130852017-27.5752144,153.0879669 -27.5752602,153.088226326.016.670.016.671.6
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    -848019289-27.5752602,153.0882263 -27.5753899,153.089141891.016.670.016.675.5
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    -848019288-27.5753899,153.0891418 -27.5754299,153.08944730.016.670.016.671.8
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    -848019287-27.5754299,153.089447 -27.5755901,153.0905914114.016.670.016.676.8
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    -848019299-27.5755901,153.0905914 -27.5756607,153.091125553.016.670.016.673.2
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    -848019298-27.5756607,153.0911255 -27.5757198,153.09156844.016.670.016.672.6
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    -736514351-27.5757198,153.091568 -27.5757504,153.091812124.016.670.016.671.4
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    -736514350-27.5757504,153.0918121 -27.5757809,153.092025821.016.670.016.671.3
    AUQueenslandBrisbaneEight Mile PlainsPadstow Rd
    +130851941-27.5757809,153.0920258 -27.5752907,153.0918121 -27.5749302,153.0916748100.019.440.018.065.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130851942-27.5749302,153.0916748 -27.5740795,153.0913239100.019.440.018.065.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130851862-27.5740795,153.0913239 -27.5734901,153.09109569.019.440.018.063.8
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130851861-27.5734901,153.091095 -27.5731201,153.090911944.019.440.018.062.4
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918831-27.5731201,153.0909119 -27.5728893,153.090805127.019.440.018.061.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918832-27.5728893,153.0908051 -27.5723991,153.0906067 -27.5722103,153.090499981.019.440.018.064.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918827-27.5722103,153.0904999 -27.5719509,153.090362531.019.440.018.061.7
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918833-27.5719509,153.0903625 -27.5717106,153.090255728.019.440.018.061.6
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918834-27.5717106,153.0902557 -27.5714703,153.090133729.019.440.018.061.6
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130757981-27.5714703,153.0901337 -27.5711498,153.090011637.019.440.018.062.0
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +778753942-27.5711498,153.0900116 -27.5710392,153.0898895 -27.5704498,153.0897064 -27.5699806,153.0894012 -27.5698109,153.0892334 -27.5696507,153.0890503 -27.5693398,153.0885468256.027.780.018.0614.2
    AUQueenslandBrisbaneEight Mile Plains
    +778753943-27.5693398,153.0885468 -27.5685005,153.0870361 -27.5683002,153.0866852 -27.5681305,153.0865479240.027.780.026.399.1
    AUQueenslandBrisbaneEight Mile Plains
    +842194179-27.5681305,153.0865479 -27.5675297,153.0855865115.027.780.026.394.4
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +842194180-27.5675297,153.0855865 -27.5662403,153.0835724 -27.5645599,153.0809937 -27.5645103,153.0809174569.027.780.026.3921.6
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180257-27.5645103,153.0809174 -27.5644493,153.0808258 -27.5643902,153.08071923.027.780.026.390.9
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy, Riverside Expy
    +778513858-27.5643902,153.080719 -27.5632801,153.0790863 -27.5629597,153.0786438 -27.5626793,153.0782623 -27.5623398,153.0778046366.027.780.026.3913.9
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +130848965-27.5623398,153.0778046 -27.5618191,153.077102790.027.780.026.393.4
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180252-27.5618191,153.0771027 -27.5615292,153.0767517 -27.56147,153.076675457.027.780.026.392.2
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180251-27.56147,153.0766754 -27.5613995,153.076583911.027.780.026.390.4
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180253-27.5613995,153.0765839 -27.5612907,153.0764771 -27.5612202,153.076400826.027.780.026.391.0
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180254-27.5612202,153.0764008 -27.5611191,153.0762939 -27.5605106,153.0756226 -27.5587692,153.0739288 -27.5560703,153.0716248 -27.5530605,153.06912231157.027.780.026.3943.8
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +782455208-27.5530605,153.0691223 -27.5518303,153.0680542172.027.780.026.396.5
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +782455209-27.5518303,153.0680542 -27.5516701,153.067916922.027.780.026.390.8
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +130752000-27.5516701,153.0679169 -27.5500202,153.0665436228.027.780.026.398.6
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +762719178-27.5500202,153.0665436 -27.5492992,153.065948599.027.780.026.393.8
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +762719179-27.5492992,153.0659485 -27.5465107,153.0635986387.027.780.026.3914.7
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +130852721-27.5465107,153.0635986 -27.5454407,153.0627136147.027.780.026.395.6
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +782455204-27.5454407,153.0627136 -27.5446301,153.062027112.027.780.026.394.2
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +851414677-27.5446301,153.062027 -27.5444107,153.0618439 -27.5436707,153.0612946 -27.5428104,153.0607452 -27.5418606,153.0602264 -27.5410309,153.0598297456.027.780.026.3917.3
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +851414678-27.5410309,153.0598297 -27.5407505,153.0596924 -27.5401402,153.0594635105.027.780.026.394.0
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +828675832-27.5401402,153.0594635 -27.5396996,153.0593262 -27.5380306,153.0588379242.027.780.026.399.2
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +828675833-27.5380306,153.0588379 -27.5377598,153.058761631.027.780.026.391.2
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +821207667-27.5377598,153.0587616 -27.5372505,153.0586548 -27.5360794,153.0584106190.027.780.026.397.2
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +821207668-27.5360794,153.0584106 -27.5345192,153.058075176.027.780.026.396.7
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +851414665-27.5345192,153.058075 -27.5335693,153.0578766107.027.780.026.394.1
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +851414666-27.5335693,153.0578766 -27.5287495,153.0568542 -27.5279198,153.0566559 -27.5272694,153.0564575 -27.52668,153.0562439 -27.5265102,153.0561676803.027.780.026.3930.4
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +782385629-27.5265102,153.0561676 -27.5260201,153.055954 -27.5256195,153.0557709106.027.780.026.394.0
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +130853483-27.5256195,153.0557709 -27.5253601,153.0556335 -27.5246906,153.0552826 -27.5240498,153.0548706 -27.5235901,153.0545044258.027.780.026.399.8
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +772093015-27.5235901,153.0545044 -27.5233498,153.054321332.027.780.026.391.2
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +772093016-27.5233498,153.0543213 -27.5231991,153.054214519.027.780.026.390.7
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +736390885-27.5231991,153.0542145 -27.5209904,153.052475299.027.780.026.3911.3
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +782385634-27.5209904,153.052475 -27.5201492,153.0517731116.027.780.026.394.4
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +782385635-27.5201492,153.0517731 -27.5198994,153.051574733.027.780.026.391.3
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +778520137-27.5198994,153.0515747 -27.5198002,153.051483214.027.780.026.390.5
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +791180780-27.5198002,153.0514832 -27.5197201,153.05143749.027.780.026.390.3
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +791180781-27.5197201,153.0514374 -27.5196705,153.0513916 -27.5188999,153.0507507 -27.51824,153.0500336 -27.5175304,153.0490723 -27.5173092,153.0487061 -27.5169201,153.0479431 -27.5146999,153.0434113979.027.780.026.3937.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +130853660-27.5146999,153.0434113 -27.5142193,153.0424347 -27.5138206,153.0417633 -27.5135002,153.0413666 -27.5131302,153.0409546 -27.5129299,153.0407715329.027.780.026.3912.5
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +833000842-27.5129299,153.0407715 -27.5126896,153.0405731 -27.5122299,153.0402832 -27.5098591,153.0390167 -27.5095406,153.0388641 -27.5092793,153.0387726452.027.780.026.3917.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +833000843-27.5092793,153.0387726 -27.5090008,153.038681 -27.5088291,153.0386353 -27.5083199,153.0385437109.027.780.026.394.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +130853471-27.5083199,153.0385437 -27.5077591,153.0385132 -27.5072899,153.0385437114.027.780.026.394.3
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +130853513-27.5072899,153.0385437 -27.5068703,153.0386047 -27.5041294,153.0391083355.027.780.026.3913.5
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +782459444-27.5041294,153.0391083 -27.50317,153.0392914108.027.780.026.394.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +782459445-27.50317,153.0392914 -27.5017509,153.0395508159.027.780.026.396.0
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +779045467-27.5017509,153.0395508 -27.5009995,153.0397034 -27.5007401,153.0397186113.027.780.026.394.3
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +779045468-27.5007401,153.0397186 -27.5005703,153.0397339 -27.500351,153.039733943.022.220.022.221.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +821196745-27.500351,153.0397339 -27.5001106,153.0397339 -27.4993591,153.0396729 -27.4989605,153.039566156.022.220.022.227.0
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +821196746-27.4989605,153.039566 -27.4988594,153.0395508 -27.49823,153.039306685.022.220.022.223.8
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +812250074-27.49823,153.0393066 -27.4977093,153.0390167 -27.4974995,153.038879491.022.220.022.224.1
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +826731481-27.4974995,153.0388794 -27.4974003,153.038803113.022.220.022.220.6
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +826731482-27.4974003,153.0388031 -27.4970894,153.038604739.022.220.022.221.8
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +130846576-27.4970894,153.0386047 -27.4968204,153.038406435.022.220.022.221.6
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +130738021-27.4968204,153.0384064 -27.4967098,153.038314815.022.220.022.220.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +778479274-27.4967098,153.0383148 -27.4961395,153.037902875.022.220.022.223.4
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +778479275-27.4961395,153.0379028 -27.4959297,153.03773528.022.220.022.221.3
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851379612-27.4959297,153.037735 -27.4958897,153.0377045 -27.4957695,153.037612921.022.220.022.220.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851460191-27.4957695,153.0376129 -27.4948807,153.0368958121.022.220.022.225.4
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851460192-27.4948807,153.0368958 -27.4944096,153.0365143 -27.4940605,153.0361481 -27.4939404,153.0359955137.022.220.022.226.2
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851414374-27.4939404,153.0359955 -27.4934292,153.0354004 -27.4932709,153.0351868109.022.220.022.224.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +812250623-27.4932709,153.0351868 -27.4930305,153.034835843.022.220.022.221.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +802502378-27.4930305,153.0348358 -27.4929104,153.034622224.022.220.022.221.1
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +811756237-27.4929104,153.0346222 -27.4927597,153.034393328.022.220.022.221.3
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +811756238-27.4927597,153.0343933 -27.4926701,153.034271215.022.220.022.220.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +840727547-27.4926701,153.0342712 -27.4923592,153.033844 -27.4920998,153.0335999 -27.4918404,153.0334167 -27.4914608,153.0332336 -27.4911499,153.0330963 -27.4907398,153.0330048255.022.220.022.2211.5
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +840727548-27.4907398,153.0330048 -27.4899998,153.032974282.022.220.022.223.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851414359-27.4899998,153.0329742 -27.48983,153.0329437 -27.4895992,153.0329132 -27.48946,153.032882760.022.220.022.222.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851414360-27.48946,153.0328827 -27.4889603,153.032836955.022.220.022.222.5
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +130846600-27.4889603,153.0328369 -27.4886894,153.032745431.022.220.022.221.4
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +791180777-27.4886894,153.0327454 -27.4882698,153.032608 -27.4881802,153.032592858.022.220.022.222.6
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +792771312-27.4881802,153.0325928 -27.4880905,153.0325623 -27.4880009,153.03254720.022.220.022.220.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +791180779-27.4880009,153.032547 -27.4879494,153.0325165 -27.4859409,153.0318909 -27.4850197,153.031601344.022.220.022.2215.5
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +778398386-27.4850197,153.031601 -27.4841309,153.031311102.022.220.022.224.6
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +792296089-27.4841309,153.031311 -27.4839497,153.0312521.022.220.022.220.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +792296090-27.4839497,153.03125 -27.4838505,153.031219511.022.220.022.220.5
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +792296087-27.4838505,153.0312195 -27.4837608,153.03118910.022.220.022.220.5
    AUQueenslandBrisbaneKangaroo PointM3, Pacific Mtwy
    +792296088-27.4837608,153.031189 -27.4836502,153.031143213.022.220.022.220.6
    AUQueenslandBrisbaneKangaroo PointM3, Pacific Mtwy
    +130846658-27.4836502,153.0311432 -27.4832993,153.030990641.022.220.022.221.8
    AUQueenslandBrisbaneKangaroo PointM3, Pacific Mtwy
    +130846657-27.4832993,153.0309906 -27.4827995,153.0307312 -27.4824696,153.0305176103.022.220.022.224.6
    AUQueenslandBrisbaneKangaroo PointM3, Pacific Mtwy, Riverside Expy
    +831959706-27.4824696,153.0305176 -27.4823093,153.0303955 -27.4822197,153.030334533.022.220.022.221.5
    AUQueenslandBrisbaneKangaroo PointCaptain Cook Brg, M3, Pacific Mtwy, Riverside Expy
    +831959707-27.4822197,153.0303345 -27.4821301,153.0302429 -27.4794807,153.0281525373.022.220.022.2216.8
    AUQueenslandBrisbaneKangaroo PointCaptain Cook Brg, M3, Pacific Mtwy, Riverside Expy
    +132965050-27.4794807,153.0281525 -27.4792404,153.027984631.022.220.022.221.4
    AUQueenslandBrisbaneKangaroo PointCaptain Cook Brg, M3, Pacific Mtwy, Riverside Expy
    +779153921-27.4792404,153.0279846 -27.4785404,153.0274506 -27.4784508,153.0273743106.022.220.022.224.8
    AUQueenslandBrisbaneM3, Pacific Mtwy, Captain Cook Brg, Riverside Expy
    +779153922-27.4784508,153.0273743 -27.4780502,153.027069153.022.220.022.222.4
    AUQueenslandBrisbaneM3, Pacific Mtwy, Captain Cook Brg, Riverside Expy
    +778599510-27.4780502,153.0270691 -27.4775295,153.026672469.022.220.022.223.1
    AUQueenslandBrisbaneM3, Pacific Mtwy, Captain Cook Brg, Riverside Expy
    +792648393-27.4775295,153.0266724 -27.4773006,153.02647432.019.440.018.061.8
    AUQueenslandBrisbaneM3, Pacific Mtwy, Captain Cook Brg, Riverside Expy
    +792648394-27.4773006,153.026474 -27.4769592,153.026199346.019.440.018.062.5
    AUQueenslandBrisbaneM3, Pacific Mtwy, Captain Cook Brg, Riverside Expy
    +792648392-27.4769592,153.0261993 -27.4766197,153.025924746.019.440.018.062.5
    AUQueenslandBrisbaneM3, Pacific Mtwy, Captain Cook Brg, Riverside Expy
    +130851656-27.4766197,153.0259247 -27.4762897,153.025665344.019.440.018.062.4
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +778829848-27.4762897,153.0256653 -27.4761295,153.025543221.019.440.018.061.2
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +779144197-27.4761295,153.0255432 -27.4759293,153.025390626.019.440.018.061.4
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +779144198-27.4759293,153.0253906 -27.4754696,153.025024462.019.440.018.063.4
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +821196747-27.4754696,153.0250244 -27.4750309,153.024673559.019.440.018.063.3
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +821196748-27.4750309,153.0246735 -27.4747601,153.024444637.019.440.018.062.0
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +779144211-27.4747601,153.0244446 -27.4745407,153.024276729.019.440.018.061.6
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +779144212-27.4745407,153.0242767 -27.4742794,153.024078435.019.440.018.061.9
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +130851752-27.4742794,153.0240784 -27.4738808,153.023773253.019.440.018.062.9
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +132965668-27.4738808,153.0237732 -27.4737606,153.023696915.019.440.018.060.8
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +778426177-27.4737606,153.0236969 -27.4730091,153.0231171101.019.440.018.065.6
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +778426178-27.4730091,153.0231171 -27.4729099,153.023040813.019.440.018.060.7
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +778426179-27.4729099,153.0230408 -27.4728107,153.022964513.019.440.018.060.7
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +841489920-27.4728107,153.0229645 -27.47258,153.0227966 -27.4724197,153.022659352.019.440.018.062.9
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +841489921-27.4724197,153.0226593 -27.4721909,153.022460932.019.440.018.061.8
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +841489915-27.4721909,153.0224609 -27.4719391,153.022247335.019.440.018.061.9
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +778398393-27.4719391,153.0222473 -27.4718609,153.02217111.019.440.018.060.6
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +782442370-27.4718609,153.022171 -27.4718399,153.02215582.019.440.018.060.1
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +782442371-27.4718399,153.0221558 -27.4718094,153.02212524.019.440.018.060.2
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +778398394-27.4718094,153.0221252 -27.4717293,153.022033712.019.440.018.060.7
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +841489916-27.4717293,153.0220337 -27.4714603,153.021774339.019.440.018.062.2
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +841489917-27.4714603,153.0217743 -27.4713306,153.02163719.019.440.018.061.1
    AUQueenslandBrisbaneM3, Pacific Mtwy, Riverside Expy
    +778426174-27.4713306,153.021637 -27.47122,153.0215912 -27.4705105,153.0209045117.016.670.016.677.0
    AUQueenslandBrisbane
    +778426175-27.4705105,153.0209045 -27.4703693,153.0207672 -27.4703102,153.020675731.016.670.016.671.9
    AUQueenslandBrisbane
    +840910687-27.4703102,153.0206757 -27.4700508,153.020309446.016.670.016.672.8
    AUQueenslandBrisbane
    +840910688-27.4700508,153.0203094 -27.4700203,153.0202637 -27.46982,153.0201416 -27.46978,153.0201263 -27.4697304,153.0201263 -27.4696808,153.020126346.016.670.016.672.8
    AUQueenslandBrisbane
    +779153927-27.4696808,153.0201263 -27.4696007,153.02015699.016.670.016.670.5
    AUQueenslandBrisbane
    +778594823-27.4696007,153.0201569 -27.4695091,153.0202332 -27.4692593,153.0205231 -27.4690895,153.0207825 -27.4690208,153.020904598.016.670.016.675.9
    AUQueenslandBrisbane
    +840910525-27.4690208,153.0209045 -27.46875,153.021240244.016.670.016.672.6
    AUQueenslandBrisbaneTurbot St
    +840910526-27.46875,153.0212402 -27.4686909,153.02131659.016.670.016.670.5
    AUQueenslandBrisbaneTurbot St
    +130731944-27.4686909,153.0213165 -27.4685402,153.021514925.016.670.016.671.5
    AUQueenslandBrisbaneTurbot St
    +840909575-27.4685402,153.0215149 -27.4682808,153.021835342.016.670.016.672.5
    AUQueenslandBrisbaneTurbot St
    +840909576-27.4682808,153.0218353 -27.4678001,153.022445780.016.670.016.674.8
    AUQueenslandBrisbaneTurbot St
    +130851044-27.4678001,153.0224457 -27.46772,153.022552513.016.670.016.670.8
    AUQueenslandBrisbaneTurbot St
    +840909577-27.46772,153.0225525 -27.4676704,153.02262889.016.670.016.670.5
    AUQueenslandBrisbaneTurbot St
    +840910689-27.4676704,153.0226288 -27.4676208,153.02268988.016.670.016.670.5
    AUQueenslandBrisbaneTurbot St
    +840910690-27.4676208,153.0226898 -27.4674492,153.022918729.016.670.016.671.7
    AUQueenslandBrisbaneTurbot St
    +744461076-27.4674492,153.0229187 -27.4673805,153.023010311.016.670.016.670.7
    AUQueenslandBrisbaneTurbot St
    +779115654-27.4673805,153.0230103 -27.46735,153.0230565.016.670.016.670.3
    AUQueenslandBrisbaneTurbot St
    +840906306-27.46735,153.023056 -27.4669609,153.023559665.016.670.016.673.9
    AUQueenslandBrisbaneTurbot St
    +840906305-27.4669609,153.0235596 -27.4666996,153.023925846.016.670.016.672.8
    AUQueenslandBrisbaneTurbot St
    +779143470-27.4666996,153.0239258 -27.4664497,153.024261543.016.670.016.672.6
    AUQueenslandBrisbaneTurbot St
    +779143471-27.4664497,153.0242615 -27.4662895,153.024459826.016.670.016.671.6
    AUQueenslandBrisbaneTurbot St
    +779114892-27.4662895,153.0244598 -27.4661503,153.024673526.016.670.016.671.6
    AUQueenslandBrisbaneTurbot St
    +779114893-27.4661503,153.0246735 -27.4660702,153.02476512.016.670.016.670.7
    AUQueenslandBrisbaneTurbot St
    +840906308-27.4660702,153.024765 -27.4658794,153.024719221.013.890.09.722.2
    AUQueenslandBrisbaneEdward St
    +840906309-27.4658794,153.0247192 -27.4658508,153.024704 -27.4657001,153.024704 -27.4656696,153.02470423.013.890.09.722.4
    AUQueenslandBrisbaneEdward St
    +840906307-27.4656696,153.024704 -27.4654598,153.024734523.013.890.09.722.4
    AUQueenslandBrisbaneEdward St
    +840906312-27.4654598,153.0247345 -27.4650002,153.024932954.013.890.09.725.6
    AUQueenslandBrisbaneSpring HillEdward St
    +840906313-27.4650002,153.0249329 -27.4648304,153.025024420.013.890.09.722.1
    AUQueenslandBrisbaneSpring HillEdward St
    -842383988-27.4648304,153.0250244 -27.4649105,153.024765 -27.4649601,153.024673537.013.890.09.723.8
    AUQueenslandBrisbaneSpring HillWickham Ter
    -842383987-27.4649601,153.0246735 -27.4653893,153.024002181.013.890.09.728.3
    AUQueenslandBrisbaneSpring HillWickham Ter
    -779110719-27.4653893,153.0240021 -27.4654293,153.02392588.013.890.09.720.8
    AUQueenslandBrisbaneSpring HillWickham Ter
    -779110718-27.4654293,153.0239258 -27.4655704,153.0236359 -27.46562,153.023529144.013.890.09.724.5
    AUQueenslandBrisbaneSpring HillWickham Ter
    +779110714-27.46562,153.0235291 -27.4656105,153.0233459 -27.4655991,153.023162836.013.890.09.723.7
    AUQueenslandBrisbaneSpring HillWickham Ter
    +130731232-27.4655991,153.0231628 -27.4652405,153.0231628 -27.4652004,153.0231476 -27.4651394,153.0230408 -27.4650898,153.0230103 -27.4650307,153.0230103 -27.4640903,153.0233917181.013.890.09.7218.6
    AUQueenslandBrisbaneSpring HillBartley St
    15895.0816.0812.0motorway
    \ No newline at end of file diff --git a/tests/auto/qgeoroutexmlparser/route2.xml b/tests/auto/qgeoroutexmlparser/route2.xml new file mode 100644 index 0000000..60cd1de --- /dev/null +++ b/tests/auto/qgeoroutexmlparser/route2.xml @@ -0,0 +1 @@ +2012-02-21T04:31:31.963+01:002012-02-21T04:29:00.020+010046413282012-02-21T04:29:02.066+0100146312012-02-21T04:29:02.066+010049962011Q2_DICIrouteserver,9.1-2011.12.20-hotfix6.2.11.148routing-route-service,6.2.11.2REMuh0MAAAAVHcnlP5Q7wEJg5dAiI2NAAAAA4ECUO8AAAADAIiNjQAAAAAAAAPB_AAAAAAAA8H8lPMsHR9jw9ErZ7QDDMJkqGIWMAQEAAABT2e0AAwAAABiFjAEBAAAAAADA_wEAAAAAAMD_4djU4GNxAstrNZeB_hDlxQbgt2cxF1g+130759717-27.5791149153.0979919-27.5791153.098stopOver-130730440-27.4622307153.0397949-27.4622153.0398stopOverfastestNowcarenabled-27.5791149,153.0979919 -27.5790405,153.0978851 -27.5786304,153.0973816 -27.57798,153.0966034 -27.5778008,153.0964661 -27.5776005,153.0963745 -27.5773792,153.096344 -27.5764599,153.0965271 -27.5764999,153.0963898 -27.5763397,153.0951233 -27.5762405,153.0945129 -27.5761204,153.0936279 -27.5759907,153.0927582 -27.5759201,153.0922546 -27.5758991,153.0921021 -27.5757809,153.0920258 -27.5752907,153.0918121 -27.5749302,153.0916748 -27.5740795,153.0913239 -27.5734901,153.091095 -27.5731201,153.0909119 -27.5728893,153.0908051 -27.5723991,153.0906067 -27.5722103,153.0904999 -27.5719509,153.0903625 -27.5717106,153.0902557 -27.5714703,153.0901337 -27.5711498,153.0900116 -27.5710392,153.0898895 -27.5704498,153.0897064 -27.5699806,153.0894012 -27.5698109,153.0892334 -27.5696507,153.0890503 -27.5693398,153.0885468 -27.5685005,153.0870361 -27.5683002,153.0866852 -27.5681305,153.0865479 -27.5675297,153.0855865 -27.5662403,153.0835724 -27.5645599,153.0809937 -27.5645103,153.0809174 -27.5644493,153.0808258 -27.5643902,153.080719 -27.5632801,153.0790863 -27.5629597,153.0786438 -27.5626793,153.0782623 -27.5623398,153.0778046 -27.5618191,153.0771027 -27.5615292,153.0767517 -27.56147,153.0766754 -27.5613995,153.0765839 -27.5612907,153.0764771 -27.5612202,153.0764008 -27.5611191,153.0762939 -27.5605106,153.0756226 -27.5587692,153.0739288 -27.5560703,153.0716248 -27.5530605,153.0691223 -27.5518303,153.0680542 -27.5516701,153.0679169 -27.5500202,153.0665436 -27.5492992,153.0659485 -27.5465107,153.0635986 -27.5454407,153.0627136 -27.5446301,153.062027 -27.5444107,153.0618439 -27.5436707,153.0612946 -27.5428104,153.0607452 -27.5418606,153.0602264 -27.5410309,153.0598297 -27.5407505,153.0596924 -27.5401402,153.0594635 -27.5396996,153.0593262 -27.5380306,153.0588379 -27.5377598,153.0587616 -27.5372505,153.0586548 -27.5360794,153.0584106 -27.5345192,153.058075 -27.5335693,153.0578766 -27.5287495,153.0568542 -27.5279198,153.0566559 -27.5272694,153.0564575 -27.52668,153.0562439 -27.5265102,153.0561676 -27.5260201,153.055954 -27.5256195,153.0557709 -27.5253601,153.0556335 -27.5246906,153.0552826 -27.5240498,153.0548706 -27.5235901,153.0545044 -27.5233498,153.0543213 -27.5231991,153.0542145 -27.5209904,153.052475 -27.5201492,153.0517731 -27.5198994,153.0515747 -27.5198002,153.0514832 -27.5197201,153.0514374 -27.5196705,153.0513916 -27.5188999,153.0507507 -27.51824,153.0500336 -27.5175304,153.0490723 -27.5173092,153.0487061 -27.5169201,153.0479431 -27.5146999,153.0434113 -27.5142193,153.0424347 -27.5138206,153.0417633 -27.5135002,153.0413666 -27.5131302,153.0409546 -27.5129299,153.0407715 -27.5126896,153.0405731 -27.5122299,153.0402832 -27.5098591,153.0390167 -27.5095406,153.0388641 -27.5092793,153.0387726 -27.5090008,153.038681 -27.5088291,153.0386353 -27.5083199,153.0385437 -27.5077591,153.0385132 -27.5072899,153.0385437 -27.5068703,153.0386047 -27.5041294,153.0391083 -27.50317,153.0392914 -27.5017509,153.0395508 -27.5009995,153.0397034 -27.5007401,153.0397186 -27.5005703,153.0397339 -27.500351,153.0397339 -27.5001106,153.0397339 -27.4993591,153.0396729 -27.4989605,153.039566 -27.4988594,153.0395508 -27.49823,153.0393066 -27.4977093,153.0390167 -27.4974995,153.0388794 -27.4974003,153.0388031 -27.4970894,153.0386047 -27.4968204,153.0384064 -27.4967098,153.0383148 -27.4961395,153.0379028 -27.4959297,153.037735 -27.4958897,153.0377045 -27.4957695,153.0376129 -27.4948807,153.0368958 -27.4944096,153.0365143 -27.4940605,153.0361481 -27.4939404,153.0359955 -27.4934292,153.0354004 -27.4932709,153.0351868 -27.4930305,153.0348358 -27.4929104,153.0346222 -27.4927597,153.0343933 -27.4926701,153.0342712 -27.4923592,153.033844 -27.4920998,153.0335999 -27.4918404,153.0334167 -27.4914608,153.0332336 -27.4911499,153.0330963 -27.4907398,153.0330048 -27.4899998,153.0329742 -27.48983,153.0329437 -27.4895992,153.0329132 -27.48946,153.0328827 -27.4889603,153.0328369 -27.48876,153.0327148 -27.4883404,153.0325317 -27.4881706,153.0324554 -27.4880695,153.0324249 -27.4879494,153.0323639 -27.4862995,153.0316925 -27.4859505,153.0314331 -27.4858799,153.0313721 -27.4858608,153.0312653 -27.4857903,153.0307465 -27.4857407,153.0303955 -27.4856205,153.0303345 -27.4855099,153.030304 -27.4854107,153.0303192 -27.4853191,153.0303192 -27.4852009,153.0303345 -27.4847908,153.0304108 -27.4846802,153.030426 -27.4845104,153.0305481 -27.4840393,153.0306396 -27.4839497,153.0306854 -27.48386,153.030777 -27.4838104,153.0309143 -27.4838409,153.0311584 -27.4838505,153.0312195 -27.4838696,153.0313873 -27.4839096,153.0316467 -27.4839706,153.032074 -27.4839993,153.0322571 -27.4840908,153.0328674 -27.4842205,153.033844 -27.4843693,153.0347748 -27.4844799,153.0354462 -27.4845104,153.0355835 -27.4845505,153.0358429 -27.4842796,153.0358429 -27.4836693,153.0358734 -27.4832802,153.0358429 -27.4829197,153.0358429 -27.48283,153.0358429 -27.4825592,153.0358429 -27.4824104,153.0358429 -27.4821491,153.0358276 -27.4819107,153.0358124 -27.4816303,153.0358124 -27.4814301,153.0358124 -27.4812508,153.0358124 -27.4805794,153.0357819 -27.4801197,153.0357666 -27.4793091,153.0357513 -27.4792404,153.0357513 -27.47855,153.0357361 -27.4782295,153.0357361 -27.4780293,153.0357513 -27.4774399,153.0357208 -27.4769306,153.0357056 -27.4765701,153.0357056 -27.4764309,153.0357056 -27.4762497,153.0356903 -27.4758797,153.0356445 -27.4758091,153.0356445 -27.4757595,153.0356445 -27.4756107,153.0355835 -27.4752903,153.0355835 -27.4749699,153.035553 -27.4743099,153.035553 -27.4732399,153.0355377 -27.4727306,153.0355225 -27.4721909,153.0355225 -27.4716606,153.0355072 -27.4712791,153.0355072 -27.4712505,153.0355072 -27.4712105,153.0355225 -27.4710503,153.0355835 -27.4709301,153.0356293 -27.4706802,153.035675 -27.4706097,153.035675 -27.4704609,153.0357056 -27.4702797,153.0357971 -27.4698505,153.0358429 -27.4694595,153.0358734 -27.4692192,153.0358734 -27.4685898,153.0358887 -27.4680004,153.0359039 -27.4671707,153.0358582 -27.4659309,153.0358124 -27.4658508,153.0358124 -27.4657001,153.0358124 -27.4648304,153.0357819 -27.4647808,153.0357819 -27.4627304,153.0357056 -27.4624996,153.0357056 -27.4624195,153.0357056 -27.4622898,153.0357056 -27.4619904,153.0357056 -27.4617596,153.0356598 -27.4615402,153.0356445 -27.4613094,153.035614 -27.4611397,153.035553 -27.4609203,153.0354004 -27.4607296,153.0351868 -27.4606895,153.0351563 -27.4605103,153.0349731 -27.4603901,153.0348511 -27.4602909,153.0347595 -27.4601593,153.034729 -27.4600105,153.0347443 -27.4598408,153.0348969 -27.4597702,153.0349731 -27.4596596,153.03508 -27.4593391,153.0354309 -27.4590492,153.0357361 -27.4593506,153.0361176 -27.4595604,153.036377 -27.4598408,153.0367432 -27.45998,153.0369263 -27.4601803,153.0371857 -27.4603996,153.0374603 -27.4608307,153.0379944 -27.4615307,153.0388947 -27.4622307,153.0397949-27.4590492153.030304-27.5791149153.0979919+130759717-27.5791149153.0979919-27.5791153.098stopOver-130730440-27.4622307153.0397949-27.4622153.0398stopOver17283.0998.2-27.5791149153.0979919Head toward Electronics St on McKechnie Dr. Go for 0.2 miles.47.3361.0+130759717forward-27.5764599153.0965271Turn left onto Miles Platting Rd (56). Go for 0.2 miles.34.2375.0+767490982left-27.5759907153.0927582Turn right onto Logan Rd (95). Go for 0.4 miles.54.6626.0+130759055right-27.5711498153.0900116Take exit #14/M3/City onto M3, Pacific Mtwy. Go for 7 miles.446.011321.0+778753942lightLeft-27.4889603153.0328369Take exit #2/41/Stanley Street/South Bank onto Stanley St (41). Go for 0.3 miles.30.6468.0+791185396lightLeft-27.4857407153.0303955Turn right onto Allen St toward Vulture Street. Go for 400 feet.22.5118.0+130735264right-27.4846802153.030426Bear right to stay on Allen St. Go for 350 feet.11.5114.0+130735018lightRight-27.4838104153.0309143Bear right onto Vulture St (41). Go for 0.3 miles.38.7489.0-130856418lightRight-27.4845505153.0358429Turn left onto Main St (15). Go for 0.6 miles.99.0966.0+828228207left-27.4757595153.0356445Continue on Main St (15) at exit #2. Go for 1 mile.94.31572.0+811754670forward-27.4615402153.0356445Take right ramp onto McLachlan St toward 25/Eagle Farm/Brisbane Airport. Go for 0.2 miles.23.5343.0+828880864lightRight-27.4590492153.0357361Turn right onto Brunswick St. Go for 0.3 miles.95.9530.0-130729663right-27.4622307153.0397949Your destination on Brunswick St is on the left. The trip takes 11 miles and 17 mins.0.00.0forward+130759717-27.5791149,153.0979919 -27.5790405,153.0978851 -27.5786304,153.097381680.013.890.09.728.2
    AUQueenslandBrisbaneEight Mile PlainsMcKechnie Dr
    +130851874-27.5786304,153.0973816 -27.57798,153.0966034 -27.5778008,153.0964661 -27.5776005,153.0963745 -27.5773792,153.096344178.013.890.09.7218.3
    AUQueenslandBrisbaneEight Mile PlainsMcKechnie Dr
    +130851875-27.5773792,153.096344 -27.5764599,153.0965271103.013.890.09.7210.6
    AUQueenslandBrisbaneEight Mile PlainsMcKechnie Dr
    +767490982-27.5764599,153.0965271 -27.5764999,153.0963898 -27.5763397,153.0951233140.016.670.016.678.4
    AUQueenslandBrisbaneEight Mile PlainsMiles Platting Rd
    +767490983-27.5763397,153.0951233 -27.5762405,153.094512961.016.670.016.673.7
    AUQueenslandBrisbaneEight Mile PlainsMiles Platting Rd
    +130759112-27.5762405,153.0945129 -27.5761204,153.093627988.016.670.016.675.3
    AUQueenslandBrisbaneEight Mile PlainsMiles Platting Rd
    +130759082-27.5761204,153.0936279 -27.5759907,153.092758286.016.670.016.675.2
    AUQueenslandBrisbaneEight Mile PlainsMiles Platting Rd
    +130759055-27.5759907,153.0927582 -27.5759201,153.092254650.016.670.016.673.0
    AUQueenslandBrisbaneEight Mile PlainsMiles Platting Rd
    +130759043-27.5759201,153.0922546 -27.5758991,153.092102115.016.670.016.670.9
    AUQueenslandBrisbaneEight Mile Plains
    +130759031-27.5758991,153.0921021 -27.5757809,153.092025815.019.440.018.060.8
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130851941-27.5757809,153.0920258 -27.5752907,153.0918121 -27.5749302,153.0916748100.019.440.018.065.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130851942-27.5749302,153.0916748 -27.5740795,153.0913239100.019.440.018.065.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130851862-27.5740795,153.0913239 -27.5734901,153.09109569.019.440.018.063.8
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130851861-27.5734901,153.091095 -27.5731201,153.090911944.019.440.018.062.4
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918831-27.5731201,153.0909119 -27.5728893,153.090805127.019.440.018.061.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918832-27.5728893,153.0908051 -27.5723991,153.0906067 -27.5722103,153.090499981.019.440.018.064.5
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918827-27.5722103,153.0904999 -27.5719509,153.090362531.019.440.018.061.7
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918833-27.5719509,153.0903625 -27.5717106,153.090255728.019.440.018.061.6
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +846918834-27.5717106,153.0902557 -27.5714703,153.090133729.019.440.018.061.6
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +130757981-27.5714703,153.0901337 -27.5711498,153.090011637.019.440.018.062.0
    AUQueenslandBrisbaneEight Mile PlainsLogan Rd
    +778753942-27.5711498,153.0900116 -27.5710392,153.0898895 -27.5704498,153.0897064 -27.5699806,153.0894012 -27.5698109,153.0892334 -27.5696507,153.0890503 -27.5693398,153.0885468256.027.780.018.0614.2
    AUQueenslandBrisbaneEight Mile Plains
    +778753943-27.5693398,153.0885468 -27.5685005,153.0870361 -27.5683002,153.0866852 -27.5681305,153.0865479240.027.780.026.399.1
    AUQueenslandBrisbaneEight Mile Plains
    +842194179-27.5681305,153.0865479 -27.5675297,153.0855865115.027.780.026.394.4
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +842194180-27.5675297,153.0855865 -27.5662403,153.0835724 -27.5645599,153.0809937 -27.5645103,153.0809174569.027.780.026.3921.6
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180257-27.5645103,153.0809174 -27.5644493,153.0808258 -27.5643902,153.08071923.027.780.026.390.9
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy, Riverside Expy
    +778513858-27.5643902,153.080719 -27.5632801,153.0790863 -27.5629597,153.0786438 -27.5626793,153.0782623 -27.5623398,153.0778046366.027.780.026.3913.9
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +130848965-27.5623398,153.0778046 -27.5618191,153.077102790.027.780.026.393.4
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180252-27.5618191,153.0771027 -27.5615292,153.0767517 -27.56147,153.076675457.027.780.026.392.2
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180251-27.56147,153.0766754 -27.5613995,153.076583911.027.780.026.390.4
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180253-27.5613995,153.0765839 -27.5612907,153.0764771 -27.5612202,153.076400826.027.780.026.391.0
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +791180254-27.5612202,153.0764008 -27.5611191,153.0762939 -27.5605106,153.0756226 -27.5587692,153.0739288 -27.5560703,153.0716248 -27.5530605,153.06912231157.027.780.026.3943.8
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +782455208-27.5530605,153.0691223 -27.5518303,153.0680542172.027.780.026.396.5
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +782455209-27.5518303,153.0680542 -27.5516701,153.067916922.027.780.026.390.8
    AUQueenslandBrisbaneMacgregorM3, Pacific Mtwy
    +130752000-27.5516701,153.0679169 -27.5500202,153.0665436228.027.780.026.398.6
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +762719178-27.5500202,153.0665436 -27.5492992,153.065948599.027.780.026.393.8
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +762719179-27.5492992,153.0659485 -27.5465107,153.0635986387.027.780.026.3914.7
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +130852721-27.5465107,153.0635986 -27.5454407,153.0627136147.027.780.026.395.6
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +782455204-27.5454407,153.0627136 -27.5446301,153.062027112.027.780.026.394.2
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +851414677-27.5446301,153.062027 -27.5444107,153.0618439 -27.5436707,153.0612946 -27.5428104,153.0607452 -27.5418606,153.0602264 -27.5410309,153.0598297456.027.780.026.3917.3
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +851414678-27.5410309,153.0598297 -27.5407505,153.0596924 -27.5401402,153.0594635105.027.780.026.394.0
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +828675832-27.5401402,153.0594635 -27.5396996,153.0593262 -27.5380306,153.0588379242.027.780.026.399.2
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +828675833-27.5380306,153.0588379 -27.5377598,153.058761631.027.780.026.391.2
    AUQueenslandBrisbaneNathanM3, Pacific Mtwy
    +821207667-27.5377598,153.0587616 -27.5372505,153.0586548 -27.5360794,153.0584106190.027.780.026.397.2
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +821207668-27.5360794,153.0584106 -27.5345192,153.058075176.027.780.026.396.7
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +851414665-27.5345192,153.058075 -27.5335693,153.0578766107.027.780.026.394.1
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +851414666-27.5335693,153.0578766 -27.5287495,153.0568542 -27.5279198,153.0566559 -27.5272694,153.0564575 -27.52668,153.0562439 -27.5265102,153.0561676803.027.780.026.3930.4
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +782385629-27.5265102,153.0561676 -27.5260201,153.055954 -27.5256195,153.0557709106.027.780.026.394.0
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +130853483-27.5256195,153.0557709 -27.5253601,153.0556335 -27.5246906,153.0552826 -27.5240498,153.0548706 -27.5235901,153.0545044258.027.780.026.399.8
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +772093015-27.5235901,153.0545044 -27.5233498,153.054321332.027.780.026.391.2
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +772093016-27.5233498,153.0543213 -27.5231991,153.054214519.027.780.026.390.7
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +736390885-27.5231991,153.0542145 -27.5209904,153.052475299.027.780.026.3911.3
    AUQueenslandBrisbaneTarragindiM3, Pacific Mtwy
    +782385634-27.5209904,153.052475 -27.5201492,153.0517731116.027.780.026.394.4
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +782385635-27.5201492,153.0517731 -27.5198994,153.051574733.027.780.026.391.3
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +778520137-27.5198994,153.0515747 -27.5198002,153.051483214.027.780.026.390.5
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +791180780-27.5198002,153.0514832 -27.5197201,153.05143749.027.780.026.390.3
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +791180781-27.5197201,153.0514374 -27.5196705,153.0513916 -27.5188999,153.0507507 -27.51824,153.0500336 -27.5175304,153.0490723 -27.5173092,153.0487061 -27.5169201,153.0479431 -27.5146999,153.0434113979.027.780.026.3937.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +130853660-27.5146999,153.0434113 -27.5142193,153.0424347 -27.5138206,153.0417633 -27.5135002,153.0413666 -27.5131302,153.0409546 -27.5129299,153.0407715329.027.780.026.3912.5
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +833000842-27.5129299,153.0407715 -27.5126896,153.0405731 -27.5122299,153.0402832 -27.5098591,153.0390167 -27.5095406,153.0388641 -27.5092793,153.0387726452.027.780.026.3917.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +833000843-27.5092793,153.0387726 -27.5090008,153.038681 -27.5088291,153.0386353 -27.5083199,153.0385437109.027.780.026.394.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +130853471-27.5083199,153.0385437 -27.5077591,153.0385132 -27.5072899,153.0385437114.027.780.026.394.3
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +130853513-27.5072899,153.0385437 -27.5068703,153.0386047 -27.5041294,153.0391083355.027.780.026.3913.5
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +782459444-27.5041294,153.0391083 -27.50317,153.0392914108.027.780.026.394.1
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +782459445-27.50317,153.0392914 -27.5017509,153.0395508159.027.780.026.396.0
    AUQueenslandBrisbaneGreenslopesM3, Pacific Mtwy
    +779045467-27.5017509,153.0395508 -27.5009995,153.0397034 -27.5007401,153.0397186113.027.780.026.394.3
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +779045468-27.5007401,153.0397186 -27.5005703,153.0397339 -27.500351,153.039733943.022.220.022.221.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +821196745-27.500351,153.0397339 -27.5001106,153.0397339 -27.4993591,153.0396729 -27.4989605,153.039566156.022.220.022.227.0
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +821196746-27.4989605,153.039566 -27.4988594,153.0395508 -27.49823,153.039306685.022.220.022.223.8
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +812250074-27.49823,153.0393066 -27.4977093,153.0390167 -27.4974995,153.038879491.022.220.022.224.1
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +826731481-27.4974995,153.0388794 -27.4974003,153.038803113.022.220.022.220.6
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +826731482-27.4974003,153.0388031 -27.4970894,153.038604739.022.220.022.221.8
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +130846576-27.4970894,153.0386047 -27.4968204,153.038406435.022.220.022.221.6
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +130738021-27.4968204,153.0384064 -27.4967098,153.038314815.022.220.022.220.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +778479274-27.4967098,153.0383148 -27.4961395,153.037902875.022.220.022.223.4
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +778479275-27.4961395,153.0379028 -27.4959297,153.03773528.022.220.022.221.3
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851379612-27.4959297,153.037735 -27.4958897,153.0377045 -27.4957695,153.037612921.022.220.022.220.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851460191-27.4957695,153.0376129 -27.4948807,153.0368958121.022.220.022.225.4
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851460192-27.4948807,153.0368958 -27.4944096,153.0365143 -27.4940605,153.0361481 -27.4939404,153.0359955137.022.220.022.226.2
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851414374-27.4939404,153.0359955 -27.4934292,153.0354004 -27.4932709,153.0351868109.022.220.022.224.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +812250623-27.4932709,153.0351868 -27.4930305,153.034835843.022.220.022.221.9
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +802502378-27.4930305,153.0348358 -27.4929104,153.034622224.022.220.022.221.1
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +811756237-27.4929104,153.0346222 -27.4927597,153.034393328.022.220.022.221.3
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +811756238-27.4927597,153.0343933 -27.4926701,153.034271215.022.220.022.220.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +840727547-27.4926701,153.0342712 -27.4923592,153.033844 -27.4920998,153.0335999 -27.4918404,153.0334167 -27.4914608,153.0332336 -27.4911499,153.0330963 -27.4907398,153.0330048255.022.220.022.2211.5
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +840727548-27.4907398,153.0330048 -27.4899998,153.032974282.022.220.022.223.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851414359-27.4899998,153.0329742 -27.48983,153.0329437 -27.4895992,153.0329132 -27.48946,153.032882760.022.220.022.222.7
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +851414360-27.48946,153.0328827 -27.4889603,153.032836955.022.220.022.222.5
    AUQueenslandBrisbaneWoolloongabbaM3, Pacific Mtwy
    +791185396-27.4889603,153.0328369 -27.48876,153.0327148 -27.4883404,153.0325317 -27.4881706,153.032455495.016.670.016.675.7
    AUQueenslandBrisbaneWoolloongabba
    +792771311-27.4881706,153.0324554 -27.4880695,153.0324249 -27.4879494,153.032363926.016.670.016.671.6
    AUQueenslandBrisbaneWoolloongabba
    +778513855-27.4879494,153.0323639 -27.4862995,153.0316925 -27.4859505,153.0314331 -27.4858799,153.0313721251.016.670.016.6715.1
    AUQueenslandBrisbaneWoolloongabba
    +130735305-27.4858799,153.0313721 -27.4858608,153.031265310.016.670.016.670.6
    AUQueenslandBrisbaneWoolloongabbaStanley St
    +130735302-27.4858608,153.0312653 -27.4857903,153.030746551.016.670.016.673.1
    AUQueenslandBrisbaneWoolloongabbaStanley St
    +130735282-27.4857903,153.0307465 -27.4857407,153.030395535.016.670.016.672.1
    AUQueenslandBrisbaneWoolloongabbaStanley St
    +130735264-27.4857407,153.0303955 -27.4856205,153.030334514.016.670.016.670.8
    AUQueenslandBrisbaneWoolloongabbaAllen St
    +841394290-27.4856205,153.0303345 -27.4855099,153.030304 -27.4854107,153.030319223.016.670.016.671.4
    AUQueenslandBrisbaneWoolloongabbaAllen St
    +841394291-27.4854107,153.0303192 -27.4853191,153.030319210.016.670.016.670.6
    AUQueenslandBrisbaneWoolloongabbaAllen St
    +841394292-27.4853191,153.0303192 -27.4852009,153.030334513.016.670.016.670.8
    AUQueenslandBrisbaneWoolloongabbaAllen St
    +130735130-27.4852009,153.0303345 -27.4847908,153.030410846.016.670.016.672.8
    AUQueenslandBrisbaneWoolloongabbaAllen St
    +130735029-27.4847908,153.0304108 -27.4846802,153.03042612.016.670.016.670.7
    AUQueenslandBrisbaneWoolloongabbaAllen St
    +130735018-27.4846802,153.030426 -27.4845104,153.0305481 -27.4840393,153.0306396 -27.4839497,153.0306854 -27.48386,153.030777 -27.4838104,153.0309143114.016.670.016.676.8
    AUQueenslandBrisbaneWoolloongabbaAllen St
    -130856418-27.4838104,153.0309143 -27.4838409,153.0311584 -27.4838505,153.0312195 -27.4838696,153.0313873 -27.4839096,153.031646773.016.670.016.674.4
    AUQueenslandBrisbaneKangaroo PointVulture St
    -842230577-27.4839096,153.0316467 -27.4839706,153.03207442.016.670.016.672.5
    AUQueenslandBrisbaneKangaroo PointVulture St
    -842230583-27.4839706,153.032074 -27.4839993,153.032257118.016.670.016.671.1
    AUQueenslandBrisbaneKangaroo PointVulture St
    -842230582-27.4839993,153.0322571 -27.4840908,153.032867461.016.670.016.673.7
    AUQueenslandBrisbaneKangaroo PointVulture St
    -130734905-27.4840908,153.0328674 -27.4842205,153.03384497.016.670.016.675.8
    AUQueenslandBrisbaneKangaroo PointVulture St
    -130734936-27.4842205,153.033844 -27.4843693,153.034774893.016.670.016.675.6
    AUQueenslandBrisbaneKangaroo PointVulture St
    -811701153-27.4843693,153.0347748 -27.4844799,153.035446267.016.670.016.674.0
    AUQueenslandBrisbaneWoolloongabbaVulture St
    -811701152-27.4844799,153.0354462 -27.4845104,153.035583513.016.670.016.670.8
    AUQueenslandBrisbaneWoolloongabbaVulture St
    -782798239-27.4845104,153.0355835 -27.4845505,153.035842925.016.670.016.671.5
    AUQueenslandBrisbaneWoolloongabbaVulture St
    +828228207-27.4845505,153.0358429 -27.4842796,153.035842930.016.670.016.671.8
    AUQueenslandBrisbaneKangaroo PointMain St
    +828228208-27.4842796,153.0358429 -27.4836693,153.035873467.016.670.016.674.0
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734780-27.4836693,153.0358734 -27.4832802,153.035842943.016.670.016.672.6
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734695-27.4832802,153.0358429 -27.4829197,153.035842940.016.670.016.672.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734588-27.4829197,153.0358429 -27.48283,153.03584299.016.670.016.670.5
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734560-27.48283,153.0358429 -27.4825592,153.035842930.016.670.016.671.8
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734480-27.4825592,153.0358429 -27.4824104,153.035842916.016.670.016.671.0
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734442-27.4824104,153.0358429 -27.4821491,153.035827629.016.670.016.671.7
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734384-27.4821491,153.0358276 -27.4819107,153.035812426.016.670.016.671.6
    AUQueenslandBrisbaneKangaroo PointMain St
    +782798224-27.4819107,153.0358124 -27.4816303,153.035812431.016.670.016.671.9
    AUQueenslandBrisbaneKangaroo PointMain St
    +782798225-27.4816303,153.0358124 -27.4814301,153.035812422.016.670.016.671.3
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734269-27.4814301,153.0358124 -27.4812508,153.035812419.016.670.016.671.1
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734246-27.4812508,153.0358124 -27.4805794,153.035781974.016.670.016.674.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734128-27.4805794,153.0357819 -27.4801197,153.035766651.016.670.016.673.1
    AUQueenslandBrisbaneKangaroo PointMain St
    +130734055-27.4801197,153.0357666 -27.4793091,153.035751390.016.670.016.675.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +130733940-27.4793091,153.0357513 -27.4792404,153.03575137.016.670.016.670.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +130733926-27.4792404,153.0357513 -27.47855,153.035736176.016.670.016.674.6
    AUQueenslandBrisbaneKangaroo PointMain St
    +845530476-27.47855,153.0357361 -27.4782295,153.035736135.016.670.016.672.1
    AUQueenslandBrisbaneKangaroo PointMain St
    +845530477-27.4782295,153.0357361 -27.4780293,153.035751322.016.670.016.671.3
    AUQueenslandBrisbaneKangaroo PointMain St
    +845360074-27.4780293,153.0357513 -27.4774399,153.035720865.016.670.016.673.9
    AUQueenslandBrisbaneKangaroo PointMain St
    +845360075-27.4774399,153.0357208 -27.4769306,153.035705656.016.670.016.673.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +845360076-27.4769306,153.0357056 -27.4765701,153.035705640.016.670.016.672.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +130733534-27.4765701,153.0357056 -27.4764309,153.035705615.016.670.016.670.9
    AUQueenslandBrisbaneKangaroo PointMain St
    +811754675-27.4764309,153.0357056 -27.4762497,153.035690320.016.670.016.671.2
    AUQueenslandBrisbaneKangaroo PointMain St
    +821266206-27.4762497,153.0356903 -27.4758797,153.035644541.016.670.016.672.5
    AUQueenslandBrisbaneKangaroo PointMain St
    +821266207-27.4758797,153.0356445 -27.4758091,153.03564457.016.670.016.670.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +130733423-27.4758091,153.0356445 -27.4757595,153.03564455.016.670.016.670.3
    AUQueenslandBrisbaneKangaroo PointMain St
    +811754670-27.4757595,153.0356445 -27.4756107,153.035583517.016.670.016.671.0
    AUQueenslandBrisbaneKangaroo PointMain St
    +811754671-27.4756107,153.0355835 -27.4752903,153.035583535.016.670.016.672.1
    AUQueenslandBrisbaneKangaroo PointMain St
    +781681415-27.4752903,153.0355835 -27.4749699,153.03555335.016.670.016.672.1
    AUQueenslandBrisbaneKangaroo PointMain St
    +130849254-27.4749699,153.035553 -27.4743099,153.03555373.016.670.016.674.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +845463722-27.4743099,153.035553 -27.4732399,153.0355377118.016.670.016.677.1
    AUQueenslandBrisbaneKangaroo PointMain St
    +845463723-27.4732399,153.0355377 -27.4727306,153.035522556.016.670.016.673.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +845463724-27.4727306,153.0355225 -27.4721909,153.035522560.016.670.016.673.6
    AUQueenslandBrisbaneKangaroo PointMain St
    +845463727-27.4721909,153.0355225 -27.4716606,153.035507258.016.670.016.673.5
    AUQueenslandBrisbaneKangaroo PointMain St
    +845463728-27.4716606,153.0355072 -27.4712791,153.035507242.016.670.016.672.5
    AUQueenslandBrisbaneKangaroo PointMain St
    +781679323-27.4712791,153.0355072 -27.4712505,153.03550723.016.670.016.670.2
    AUQueenslandBrisbaneKangaroo PointMain St
    +811498732-27.4712505,153.0355072 -27.4712105,153.0355225 -27.4710503,153.035583523.016.670.016.671.4
    AUQueenslandBrisbaneKangaroo PointMain St
    +130849251-27.4710503,153.0355835 -27.4709301,153.035629314.016.670.016.670.8
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +779133869-27.4709301,153.0356293 -27.4706802,153.035675 -27.4706097,153.03567535.016.670.016.672.1
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +779133870-27.4706097,153.035675 -27.4704609,153.0357056 -27.4702797,153.035797138.016.670.016.672.3
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +130732275-27.4702797,153.0357971 -27.4698505,153.0358429 -27.4694595,153.0358734 -27.4692192,153.0358734118.016.670.016.677.1
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +130732051-27.4692192,153.0358734 -27.4685898,153.035888770.016.670.016.674.2
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +130731930-27.4685898,153.0358887 -27.4680004,153.035903965.016.670.016.673.9
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +778423229-27.4680004,153.0359039 -27.4671707,153.035858292.016.670.016.675.5
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +778423230-27.4671707,153.0358582 -27.4659309,153.0358124137.016.670.016.678.2
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +778423228-27.4659309,153.0358124 -27.4658508,153.03581248.016.670.016.670.5
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy
    +778592178-27.4658508,153.0358124 -27.4657001,153.035812416.016.670.016.671.0
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy, Story Brg
    +779145761-27.4657001,153.0358124 -27.4648304,153.035781996.016.670.016.675.8
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy, Story Brg
    +779145762-27.4648304,153.0357819 -27.4647808,153.0357819 -27.4627304,153.0357056233.016.670.016.6714.0
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy, Story Brg
    +750205884-27.4627304,153.0357056 -27.4624996,153.035705625.016.670.016.671.5
    AUQueenslandBrisbaneKangaroo PointBradfield Hwy, Story Brg
    +779145730-27.4624996,153.0357056 -27.4624195,153.03570568.016.670.016.670.5
    AUQueenslandBrisbaneFortitude ValleyBradfield Hwy, Story Brg
    +779145731-27.4624195,153.0357056 -27.4622898,153.035705614.016.670.016.670.8
    AUQueenslandBrisbaneFortitude ValleyBradfield Hwy, Story Brg
    +779145721-27.4622898,153.0357056 -27.4619904,153.035705633.016.670.016.672.0
    AUQueenslandBrisbaneFortitude ValleyBradfield Hwy, Story Brg
    +130851739-27.4619904,153.0357056 -27.4617596,153.035659826.016.670.016.671.6
    AUQueenslandBrisbaneFortitude ValleyBradfield Hwy
    +130851740-27.4617596,153.0356598 -27.4615402,153.035644524.016.670.016.671.4
    AUQueenslandBrisbaneFortitude ValleyBradfield Hwy
    +828880864-27.4615402,153.0356445 -27.4613094,153.035614 -27.4611397,153.035553 -27.4609203,153.0354004 -27.4607296,153.0351868 -27.4606895,153.0351563109.016.670.016.676.5
    AUQueenslandBrisbaneFortitude Valley
    +828880865-27.4606895,153.0351563 -27.4605103,153.0349731 -27.4603901,153.0348511 -27.4602909,153.0347595 -27.4601593,153.03472974.016.670.016.674.4
    AUQueenslandBrisbaneFortitude Valley
    +811489085-27.4601593,153.034729 -27.4600105,153.0347443 -27.4598408,153.0348969 -27.4597702,153.034973151.016.670.016.673.1
    AUQueenslandBrisbaneFortitude Valley
    +782798336-27.4597702,153.0349731 -27.4596596,153.0350816.016.670.016.671.0
    AUQueenslandBrisbaneFortitude ValleyMcLachlan St
    +782798337-27.4596596,153.03508 -27.4593391,153.035430949.016.670.016.672.9
    AUQueenslandBrisbaneFortitude ValleyMcLachlan St
    +130729659-27.4593391,153.0354309 -27.4590492,153.035736144.016.670.016.672.6
    AUQueenslandBrisbaneFortitude ValleyMcLachlan St
    -130729663-27.4590492,153.0357361 -27.4593506,153.036117650.013.890.09.725.1
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -130729708-27.4593506,153.0361176 -27.4595604,153.03637734.013.890.09.723.5
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -779178787-27.4595604,153.036377 -27.4598408,153.036743247.013.890.09.724.8
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -779178786-27.4598408,153.0367432 -27.45998,153.036926323.013.890.09.722.4
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -130729848-27.45998,153.0369263 -27.4601803,153.037185733.013.890.09.723.4
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -130851715-27.4601803,153.0371857 -27.4603996,153.037460336.013.890.09.723.7
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -130851714-27.4603996,153.0374603 -27.4608307,153.037994471.013.890.09.727.3
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -130730226-27.4608307,153.0379944 -27.4615307,153.0388947118.013.890.09.7212.1
    AUQueenslandBrisbaneFortitude ValleyBrunswick St
    -130730440-27.4615307,153.0388947 -27.4622307,153.0397949118.013.890.09.7212.1
    AUQueenslandBrisbaneNew FarmBrunswick St
    17283.01002.0998.0motorway
    \ No newline at end of file diff --git a/tests/auto/qgeoroutexmlparser/tst_qgeoroutexmlparser.cpp b/tests/auto/qgeoroutexmlparser/tst_qgeoroutexmlparser.cpp new file mode 100644 index 0000000..36d679e --- /dev/null +++ b/tests/auto/qgeoroutexmlparser/tst_qgeoroutexmlparser.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QList) + +QT_USE_NAMESPACE + +class tst_QGeoRouteXmlParser : public QObject +{ + Q_OBJECT + +public: + tst_QGeoRouteXmlParser() + : start(0.0, 0.0), + end(1.0, 1.0) + { + qRegisterMetaType >(); + } + +private: + // dummy values for creating the request object + QGeoCoordinate start; + QGeoCoordinate end; + +private slots: + void test_realData1() + { + QFile f(":/route1.xml"); + if (!f.open(QIODevice::ReadOnly)) + QFAIL("could not open route1.xml"); + + QGeoRouteRequest req(start, end); + QGeoRouteXmlParser xp(req); + xp.setAutoDelete(false); + + QSignalSpy resultsSpy(&xp, SIGNAL(results(QList))); + + xp.parse(f.readAll()); + + QTRY_COMPARE(resultsSpy.count(), 1); + + QVariantList arguments = resultsSpy.first(); + + // xml contains exactly 1 route + QList results = arguments.at(0).value >(); + QCOMPARE(results.size(), 1); + QGeoRoute route = results.first(); + + QList segments; + // get all the segments on the route + segments << route.firstRouteSegment(); + while (segments.last().isValid()) + segments << segments.last().nextRouteSegment(); + + // should be 9 segments in the list (last one invalid) + QCOMPARE(segments.size(), 9); + + // check the first maneuver is correct + QGeoManeuver first = segments.at(0).maneuver(); + QCOMPARE(first.instructionText(), QStringLiteral("Head toward Logan Rd (95) on Padstow Rd (56). Go for 0.3 miles.")); + QCOMPARE(first.position(), QGeoCoordinate(-27.5752144, 153.0879669)); + + QCOMPARE(first.timeToNextInstruction(), 24); + QCOMPARE(first.distanceToNextInstruction(), 403.0); + + // check the last two maneuvers -- route1.xml has a directionless final maneuver + QGeoManeuver secondLast = segments.at(6).maneuver(); + QVERIFY(secondLast.instructionText().contains("Turn right onto Bartley St")); + QCOMPARE(secondLast.position(), QGeoCoordinate(-27.4655991, 153.0231628)); + QCOMPARE(secondLast.distanceToNextInstruction(), 181.0); + QCOMPARE(secondLast.timeToNextInstruction(), 41); + + QGeoManeuver last = segments.at(7).maneuver(); + QVERIFY(last.instructionText().contains("Arrive at Bartley St")); + QCOMPARE(last.position(), QGeoCoordinate(-27.4650097, 153.0230255)); + QCOMPARE(last.distanceToNextInstruction(), 0.0); + QCOMPARE(last.timeToNextInstruction(), 0); + } + + void test_realData2() + { + QFile f(":/route2.xml"); + if (!f.open(QIODevice::ReadOnly)) + QFAIL("could not open route2.xml"); + + QGeoRouteRequest req(start, end); + QGeoRouteXmlParser xp(req); + xp.setAutoDelete(false); + + QSignalSpy resultsSpy(&xp, SIGNAL(results(QList))); + + xp.parse(f.readAll()); + + QTRY_COMPARE(resultsSpy.count(), 1); + + QVariantList arguments = resultsSpy.first(); + + // xml contains exactly 1 route + QList results = arguments.at(0).value >(); + QCOMPARE(results.size(), 1); + QGeoRoute route = results.first(); + + QList segments; + // get all the segments on the route + segments << route.firstRouteSegment(); + while (segments.last().isValid()) + segments << segments.last().nextRouteSegment(); + + // should be 14 segments in the list (last one invalid) + QCOMPARE(segments.size(), 14); + + QCOMPARE(route.path().size(), 284); + QCOMPARE(route.path().at(57), QGeoCoordinate(-27.5530605, 153.0691223)); + QCOMPARE(route.path().at(283), QGeoCoordinate(-27.4622307, 153.0397949)); + + QVERIFY(segments.at(0).maneuver().instructionText().contains("Head toward Electronics St")); + QCOMPARE(segments.at(0).maneuver().direction(), QGeoManeuver::DirectionForward); + QVERIFY(segments.at(1).maneuver().instructionText().contains("Turn left onto Miles Platting")); + QCOMPARE(segments.at(1).maneuver().direction(), QGeoManeuver::DirectionLeft); + QVERIFY(segments.at(2).maneuver().instructionText().contains("Turn right onto Logan Rd")); + QCOMPARE(segments.at(2).maneuver().direction(), QGeoManeuver::DirectionRight); + QVERIFY(segments.at(3).maneuver().instructionText().contains("Take exit #14/M3/City")); + QCOMPARE(segments.at(3).maneuver().direction(), QGeoManeuver::DirectionLightLeft); + QVERIFY(segments.at(4).maneuver().instructionText().contains("Take exit #2/41")); + QCOMPARE(segments.at(4).maneuver().direction(), QGeoManeuver::DirectionLightLeft); + QVERIFY(segments.at(5).maneuver().instructionText().contains("Turn right onto Allen St")); + QCOMPARE(segments.at(5).maneuver().direction(), QGeoManeuver::DirectionRight); + QVERIFY(segments.at(6).maneuver().instructionText().contains("Bear right to stay on")); + QCOMPARE(segments.at(6).maneuver().direction(), QGeoManeuver::DirectionLightRight); + QVERIFY(segments.at(7).maneuver().instructionText().contains("Bear right onto Vulture St")); + QCOMPARE(segments.at(7).maneuver().direction(), QGeoManeuver::DirectionLightRight); + } +}; + +QTEST_GUILESS_MAIN(tst_QGeoRouteXmlParser) +#include "tst_qgeoroutexmlparser.moc" + diff --git a/tests/auto/qgeoroutingmanager/qgeoroutingmanager.pro b/tests/auto/qgeoroutingmanager/qgeoroutingmanager.pro new file mode 100644 index 0000000..836d29c --- /dev/null +++ b/tests/auto/qgeoroutingmanager/qgeoroutingmanager.pro @@ -0,0 +1,11 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeoroutingmanager + +HEADERS += tst_qgeoroutingmanager.h + +SOURCES += tst_qgeoroutingmanager.cpp + +CONFIG -= app_bundle + +QT += location testlib diff --git a/tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.cpp b/tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.cpp new file mode 100644 index 0000000..dc519b4 --- /dev/null +++ b/tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "tst_qgeoroutingmanager.h" + +QT_USE_NAMESPACE + + +void tst_QGeoRoutingManager::initTestCase() +{ +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif + tst_QGeoRoutingManager::loadRoutingManager(); +} + +void tst_QGeoRoutingManager::cleanupTestCase() +{ + //delete qgeoroutingmanager; + delete qgeoserviceprovider; +} + +void tst_QGeoRoutingManager::init() +{ +} + +void tst_QGeoRoutingManager::cleanup() +{ +} + +void tst_QGeoRoutingManager::loadRoutingManager() +{ + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + QVERIFY(providers.contains("georoute.test.plugin")); + + qgeoserviceprovider = new QGeoServiceProvider("georoute.test.plugin"); + QVERIFY(qgeoserviceprovider); + QCOMPARE(qgeoserviceprovider->error(), QGeoServiceProvider::NotSupportedError); + qgeoserviceprovider->setAllowExperimental(true); + + QCOMPARE(qgeoserviceprovider->routingFeatures(), + QGeoServiceProvider::OfflineRoutingFeature + | QGeoServiceProvider::AlternativeRoutesFeature + | QGeoServiceProvider::RouteUpdatesFeature + | QGeoServiceProvider::ExcludeAreasRoutingFeature); + QCOMPARE(qgeoserviceprovider->error(), QGeoServiceProvider::NoError); + + qgeoroutingmanager = qgeoserviceprovider->routingManager(); + QVERIFY(qgeoroutingmanager); + +} + +void tst_QGeoRoutingManager::supports() +{ + QCOMPARE(qgeoroutingmanager->supportedTravelModes(),QGeoRouteRequest::PedestrianTravel); + QCOMPARE(qgeoroutingmanager->supportedFeatureTypes(),QGeoRouteRequest::TollFeature); + QCOMPARE(qgeoroutingmanager->supportedFeatureWeights(),QGeoRouteRequest::PreferFeatureWeight); + QCOMPARE(qgeoroutingmanager->supportedRouteOptimizations(),QGeoRouteRequest::FastestRoute); + QCOMPARE(qgeoroutingmanager->supportedSegmentDetails(),QGeoRouteRequest::BasicSegmentData); + QCOMPARE(qgeoroutingmanager->supportedManeuverDetails(),QGeoRouteRequest::BasicManeuvers); +} + +void tst_QGeoRoutingManager::locale() +{ + QLocale german = QLocale(QLocale::German, QLocale::Germany); + QLocale english = QLocale(QLocale::C, QLocale::AnyCountry); + + qgeoroutingmanager->setLocale(german); + + QCOMPARE(qgeoroutingmanager->locale(), german); + + QVERIFY(qgeoroutingmanager->locale() != english); + + QLocale en_UK = QLocale(QLocale::English, QLocale::UnitedKingdom); + qgeoroutingmanager->setLocale(en_UK); + QCOMPARE(qgeoroutingmanager->measurementSystem(), en_UK.measurementSystem()); + qgeoroutingmanager->setMeasurementSystem(QLocale::MetricSystem); + QCOMPARE(qgeoroutingmanager->measurementSystem(), QLocale::MetricSystem); + QVERIFY(qgeoroutingmanager->locale().measurementSystem() != qgeoroutingmanager->measurementSystem()); +} + +void tst_QGeoRoutingManager::name() +{ + QString name = "georoute.test.plugin"; + QCOMPARE(qgeoroutingmanager->managerName(), name); +} + +void tst_QGeoRoutingManager::version() +{ + QCOMPARE(qgeoroutingmanager->managerVersion(), 100); +} + +void tst_QGeoRoutingManager::calculate() +{ + QString error = "no error"; + origin = new QGeoCoordinate(12.12 , 23.23); + destination = new QGeoCoordinate(34.34 , 89.32); + request = new QGeoRouteRequest(*origin, *destination); + + reply = qgeoroutingmanager->calculateRoute(*request); + + QCOMPARE(reply->error(), QGeoRouteReply::NoError); + QCOMPARE(reply->errorString(), error); + + delete origin; + delete destination; + delete request; + delete reply; +} + + +void tst_QGeoRoutingManager::update() +{ + QString error = "no error"; + position = new QGeoCoordinate(34.34, 89.32); + route = new QGeoRoute(); + + reply = qgeoroutingmanager->updateRoute(*route, *position); + + QCOMPARE(reply->error(), QGeoRouteReply::CommunicationError); + QCOMPARE(reply->errorString(), error); + + delete position; + delete route; + delete reply; +} + +QTEST_MAIN(tst_QGeoRoutingManager) + diff --git a/tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.h b/tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.h new file mode 100644 index 0000000..8d85d47 --- /dev/null +++ b/tests/auto/qgeoroutingmanager/tst_qgeoroutingmanager.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#ifndef TST_QGEOROUTINGMANAGER_H +#define TST_QGEOROUTINGMANAGER_H + +#include +#include +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoRoutingManager: public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + void supports(); + void locale(); + void name(); + void version(); + void calculate(); + void update(); + +private: + QGeoServiceProvider *qgeoserviceprovider; + QGeoRoutingManager *qgeoroutingmanager; + QGeoRouteRequest *request; + QGeoRouteReply *reply; + QGeoCoordinate *origin; + QGeoCoordinate *destination; + QGeoRoute *route; + QGeoCoordinate *position; + void loadRoutingManager(); + +}; + +#endif diff --git a/tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerengine_test.h b/tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerengine_test.h new file mode 100644 index 0000000..cb75fec --- /dev/null +++ b/tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerengine_test.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOROUTINGMANAGERENGINE_TEST_H +#define QGEOROUTINGMANAGERENGINE_TEST_H + +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class QGeoRoutingManagerEngineTest: public QGeoRoutingManagerEngine + +{ +Q_OBJECT +public: + QGeoRoutingManagerEngineTest(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) : + QGeoRoutingManagerEngine(parameters) + { + Q_UNUSED(error) + Q_UNUSED(errorString) + setSupportedTravelModes(QGeoRouteRequest::PedestrianTravel); + setSupportedFeatureTypes(QGeoRouteRequest::TollFeature); + setSupportedFeatureWeights(QGeoRouteRequest::PreferFeatureWeight); + setSupportedRouteOptimizations(QGeoRouteRequest::FastestRoute); + setSupportedSegmentDetails(QGeoRouteRequest::BasicSegmentData); + setSupportedManeuverDetails(QGeoRouteRequest::BasicManeuvers); + } + + QGeoRouteReply* calculateRoute(const QGeoRouteRequest& request) + { + Q_UNUSED(request); + return new QGeoRouteReply(QGeoRouteReply::NoError,"no error"); + } + + QGeoRouteReply* updateRoute(const QGeoRoute &route, const QGeoCoordinate &position) + { + Q_UNUSED(route); + Q_UNUSED(position); + return new QGeoRouteReply(QGeoRouteReply::CommunicationError,"no error"); + + } + + +}; + +#endif diff --git a/tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerplugins.pro b/tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerplugins.pro new file mode 100644 index 0000000..4222f35 --- /dev/null +++ b/tests/auto/qgeoroutingmanagerplugins/qgeoroutingmanagerplugins.pro @@ -0,0 +1,15 @@ +TARGET = qtgeoservices_routingplugin +QT += location + +PLUGIN_TYPE = geoservices +PLUGIN_CLASS_NAME = RoutingTestGeoServicePlugin +PLUGIN_EXTENDS = - +load(qt_plugin) + +HEADERS += qgeoroutingmanagerengine_test.h \ + qgeoserviceproviderplugin_test.h + +SOURCES += qgeoserviceproviderplugin_test.cpp + +OTHER_FILES += \ + routing_plugin.json diff --git a/tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.cpp b/tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.cpp new file mode 100644 index 0000000..4c35a91 --- /dev/null +++ b/tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeoserviceproviderplugin_test.h" +#include "qgeoroutingmanagerengine_test.h" + +#include + +QGeoServiceProviderFactoryTest::QGeoServiceProviderFactoryTest() +{ +} + +QGeoServiceProviderFactoryTest::~QGeoServiceProviderFactoryTest() +{ +} + +QGeoRoutingManagerEngine* QGeoServiceProviderFactoryTest::createRoutingManagerEngine( + const QVariantMap ¶meters, QGeoServiceProvider::Error *error, + QString *errorString) const +{ + return new QGeoRoutingManagerEngineTest(parameters, error, errorString); +} diff --git a/tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.h b/tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.h new file mode 100644 index 0000000..e909b64 --- /dev/null +++ b/tests/auto/qgeoroutingmanagerplugins/qgeoserviceproviderplugin_test.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGEOSERVICEPROVIDER_TEST_H +#define QGEOSERVICEPROVIDER_TEST_H + +#include +#include + +QT_USE_NAMESPACE + +class QGeoServiceProviderFactoryTest: public QObject, public QGeoServiceProviderFactory +{ + Q_OBJECT + Q_INTERFACES(QGeoServiceProviderFactory) + Q_PLUGIN_METADATA(IID "org.qt-project.qt.geoservice.serviceproviderfactory/5.0" + FILE "routing_plugin.json") + +public: + QGeoServiceProviderFactoryTest(); + ~QGeoServiceProviderFactoryTest(); + + QGeoRoutingManagerEngine* createRoutingManagerEngine(const QVariantMap ¶meters, + QGeoServiceProvider::Error *error, QString *errorString) const; + +}; + +#endif + + diff --git a/tests/auto/qgeoroutingmanagerplugins/routing_plugin.json b/tests/auto/qgeoroutingmanagerplugins/routing_plugin.json new file mode 100644 index 0000000..25905f6 --- /dev/null +++ b/tests/auto/qgeoroutingmanagerplugins/routing_plugin.json @@ -0,0 +1,12 @@ +{ + "Keys": ["georoute.test.plugin"], + "Provider": "georoute.test.plugin", + "Version": 100, + "Experimental": true, + "Features": [ + "OfflineRoutingFeature", + "RouteUpdatesFeature", + "AlternativeRoutesFeature", + "ExcludeAreasRoutingFeature" + ] +} diff --git a/tests/auto/qgeosatelliteinfo/qgeosatelliteinfo.pro b/tests/auto/qgeosatelliteinfo/qgeosatelliteinfo.pro new file mode 100644 index 0000000..447ebdc --- /dev/null +++ b/tests/auto/qgeosatelliteinfo/qgeosatelliteinfo.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeosatelliteinfo + +SOURCES += tst_qgeosatelliteinfo.cpp + +QT += testlib positioning diff --git a/tests/auto/qgeosatelliteinfo/tst_qgeosatelliteinfo.cpp b/tests/auto/qgeosatelliteinfo/tst_qgeosatelliteinfo.cpp new file mode 100644 index 0000000..e1b0ad4 --- /dev/null +++ b/tests/auto/qgeosatelliteinfo/tst_qgeosatelliteinfo.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include + +#include +#include +#include +#include + +#include +#include + +QT_USE_NAMESPACE +Q_DECLARE_METATYPE(QGeoSatelliteInfo) +Q_DECLARE_METATYPE(QGeoSatelliteInfo::Attribute) + +QByteArray tst_qgeosatelliteinfo_debug; + +void tst_qgeosatelliteinfo_messageHandler(QtMsgType type, const QMessageLogContext &, const QString &msg) +{ + switch (type) { + case QtDebugMsg : + tst_qgeosatelliteinfo_debug = msg.toLocal8Bit(); + break; + default: + break; + } +} + + +QList tst_qgeosatelliteinfo_qrealTestValues() +{ + QList values; + + if (qreal(DBL_MIN) == DBL_MIN) + values << DBL_MIN; + + values << FLT_MIN; + values << -1.0 << 0.0 << 1.0; + values << FLT_MAX; + + if (qreal(DBL_MAX) == DBL_MAX) + values << DBL_MAX; + + return values; +} + +QList tst_qgeosatelliteinfo_intTestValues() +{ + QList values; + values << INT_MIN << -100 << 0 << 100 << INT_MAX; + return values; +} + +QList tst_qgeosatelliteinfo_getAttributes() +{ + QList attributes; + attributes << QGeoSatelliteInfo::Elevation + << QGeoSatelliteInfo::Azimuth; + return attributes; +} + + +class tst_QGeoSatelliteInfo : public QObject +{ + Q_OBJECT + +private: + QGeoSatelliteInfo updateWithAttribute(QGeoSatelliteInfo::Attribute attribute, qreal value) + { + QGeoSatelliteInfo info; + info.setAttribute(attribute, value); + return info; + } + + void addTestData_update() + { + QTest::addColumn("info"); + + QList intValues = tst_qgeosatelliteinfo_intTestValues(); + + for (int i=0; i attributes = tst_qgeosatelliteinfo_getAttributes(); + QList qrealValues = tst_qgeosatelliteinfo_qrealTestValues(); + for (int i=0; i attributes = tst_qgeosatelliteinfo_getAttributes(); + for (int i=0; i("signal"); + + QList intValues = tst_qgeosatelliteinfo_intTestValues(); + for (int i=0; i("satId"); + + QList intValues = tst_qgeosatelliteinfo_intTestValues(); + for (int i=0; i(system)); + QCOMPARE(info.satelliteSystem(), static_cast(system)); + } + + void setSatelliteSystem_data() + { + QTest::addColumn("system"); + + QTest::newRow("Sat system undefined") + << int(QGeoSatelliteInfo::Undefined); + QTest::newRow("Sat system GPS") + << int(QGeoSatelliteInfo::GPS); + QTest::newRow("Sat system GLONASS") + << int(QGeoSatelliteInfo::GLONASS); + } + + void attribute() + { + QFETCH(QGeoSatelliteInfo::Attribute, attribute); + QFETCH(qreal, value); + + QGeoSatelliteInfo u; + QCOMPARE(u.attribute(attribute), qreal(-1.0)); + + u.setAttribute(attribute, value); + QCOMPARE(u.attribute(attribute), value); + u.removeAttribute(attribute); + QCOMPARE(u.attribute(attribute), qreal(-1.0)); + } + + void attribute_data() + { + QTest::addColumn("attribute"); + QTest::addColumn("value"); + + QList props; + props << QGeoSatelliteInfo::Elevation + << QGeoSatelliteInfo::Azimuth; + for (int i=0; i> inInfo; + QCOMPARE(inInfo, info); + } + + void datastream_data() + { + addTestData_update(); + } + + void debug() + { + QFETCH(QGeoSatelliteInfo, info); + QFETCH(int, nextValue); + QFETCH(QByteArray, debugString); + + qInstallMessageHandler(tst_qgeosatelliteinfo_messageHandler); + qDebug() << info << nextValue; + qInstallMessageHandler(0); + QCOMPARE(QString(tst_qgeosatelliteinfo_debug), QString(debugString)); + } + + void debug_data() + { + QTest::addColumn("info"); + QTest::addColumn("nextValue"); + QTest::addColumn("debugString"); + + QGeoSatelliteInfo info; + + QTest::newRow("uninitialized") << info << 45 + << QByteArray("QGeoSatelliteInfo(system=0, satId=-1, signal-strength=-1) 45"); + + info = QGeoSatelliteInfo(); + info.setSignalStrength(1); + QTest::newRow("with SignalStrength") << info << 60 + << QByteArray("QGeoSatelliteInfo(system=0, satId=-1, signal-strength=1) 60"); + + info = QGeoSatelliteInfo(); + info.setSatelliteIdentifier(1); + QTest::newRow("with SatelliteIdentifier") << info << -1 + << QByteArray("QGeoSatelliteInfo(system=0, satId=1, signal-strength=-1) -1"); + + info = QGeoSatelliteInfo(); + info.setSatelliteSystem(QGeoSatelliteInfo::GPS); + QTest::newRow("with System GPS") << info << 1 + << QByteArray("QGeoSatelliteInfo(system=1, satId=-1, signal-strength=-1) 1"); + + info = QGeoSatelliteInfo(); + info.setSatelliteSystem(QGeoSatelliteInfo::GLONASS); + QTest::newRow("with System GLONASS") << info << 56 + << QByteArray("QGeoSatelliteInfo(system=2, satId=-1, signal-strength=-1) 56"); + + info = QGeoSatelliteInfo(); + info.setAttribute(QGeoSatelliteInfo::Elevation, 1.1); + QTest::newRow("with Elevation") << info << 0 + << QByteArray("QGeoSatelliteInfo(system=0, satId=-1, signal-strength=-1, Elevation=1.1) 0"); + + info = QGeoSatelliteInfo(); + info.setAttribute(QGeoSatelliteInfo::Azimuth, 1.1); + QTest::newRow("with Azimuth") << info << 45 + << QByteArray("QGeoSatelliteInfo(system=0, satId=-1, signal-strength=-1, Azimuth=1.1) 45"); + } +}; + + +QTEST_APPLESS_MAIN(tst_QGeoSatelliteInfo) +#include "tst_qgeosatelliteinfo.moc" diff --git a/tests/auto/qgeosatelliteinfosource/qgeosatelliteinfosource.pro b/tests/auto/qgeosatelliteinfosource/qgeosatelliteinfosource.pro new file mode 100644 index 0000000..4ca0f21 --- /dev/null +++ b/tests/auto/qgeosatelliteinfosource/qgeosatelliteinfosource.pro @@ -0,0 +1,12 @@ +TEMPLATE = app +!no_system_tests:CONFIG += testcase +TARGET=tst_qgeosatelliteinfosource + +SOURCES += tst_qgeosatelliteinfosource.cpp \ + testqgeosatelliteinfosource.cpp \ + ../utils/qlocationtestutils.cpp + +HEADERS += testqgeosatelliteinfosource_p.h \ + ../utils/qlocationtestutils_p.h + +QT += positioning testlib diff --git a/tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource.cpp b/tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource.cpp new file mode 100644 index 0000000..0e75baa --- /dev/null +++ b/tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource.cpp @@ -0,0 +1,767 @@ +/*********************f******************************************************* +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include +#include +#include +#include +#include + +#include +#include + +#include "testqgeosatelliteinfosource_p.h" +#include "../utils/qlocationtestutils_p.h" + +Q_DECLARE_METATYPE(QList) +Q_DECLARE_METATYPE(QGeoSatelliteInfoSource::Error) + +#define MAX_WAITING_TIME 50000 + +// Must provide a valid source, unless testing the source +// returned by QGeoSatelliteInfoSource::createDefaultSource() on a system +// that has no default source +#define CHECK_SOURCE_VALID { \ + if (!m_source) { \ + if (m_testingDefaultSource && QGeoSatelliteInfoSource::createDefaultSource(0) == 0) \ + QSKIP("No default satellite source on this system"); \ + else \ + QFAIL("createTestSource() must return a valid source!"); \ + } \ + } + +class MySatelliteSource : public QGeoSatelliteInfoSource +{ + Q_OBJECT +public: + MySatelliteSource(QObject *parent = 0) + : QGeoSatelliteInfoSource(parent) { + } + virtual void startUpdates() {} + virtual void stopUpdates() {} + virtual void requestUpdate(int) {} + virtual int minimumUpdateInterval() const { + return 0; + } + Error error() const { return QGeoSatelliteInfoSource::NoError; } +}; + + +class DefaultSourceTest : public TestQGeoSatelliteInfoSource +{ + Q_OBJECT +protected: + QGeoSatelliteInfoSource *createTestSource() { + return QGeoSatelliteInfoSource::createDefaultSource(0); + } +}; + +TestQGeoSatelliteInfoSource::TestQGeoSatelliteInfoSource(QObject *parent) + : QObject(parent) +{ + qRegisterMetaType(); + + m_testingDefaultSource = false; +} + +TestQGeoSatelliteInfoSource *TestQGeoSatelliteInfoSource::createDefaultSourceTest() +{ + DefaultSourceTest *test = new DefaultSourceTest; + test->m_testingDefaultSource = true; + return test; +} + +void TestQGeoSatelliteInfoSource::base_initTestCase() +{ + qRegisterMetaType >(); +} + +void TestQGeoSatelliteInfoSource::base_init() +{ + m_source = createTestSource(); + m_testSlot2Called = false; +} + +void TestQGeoSatelliteInfoSource::base_cleanup() +{ + delete m_source; + m_source = 0; +} + +void TestQGeoSatelliteInfoSource::base_cleanupTestCase() +{ +} + +void TestQGeoSatelliteInfoSource::initTestCase() +{ + base_initTestCase(); +} + +void TestQGeoSatelliteInfoSource::init() +{ + base_init(); +} + +void TestQGeoSatelliteInfoSource::cleanup() +{ + base_cleanup(); +} + +void TestQGeoSatelliteInfoSource::cleanupTestCase() +{ + base_cleanupTestCase(); +} + +void TestQGeoSatelliteInfoSource::test_slot1() +{ +} + +void TestQGeoSatelliteInfoSource::test_slot2() +{ + m_testSlot2Called = true; +} + +void TestQGeoSatelliteInfoSource::constructor_withParent() +{ + QObject *parent = new QObject(); + new MySatelliteSource(parent); + delete parent; +} + +void TestQGeoSatelliteInfoSource::constructor_noParent() +{ + MySatelliteSource *obj = new MySatelliteSource(); + delete obj; +} + +void TestQGeoSatelliteInfoSource::createDefaultSource() +{ + QObject *parent = new QObject; + QGeoSatelliteInfoSource *source = QGeoSatelliteInfoSource::createDefaultSource(parent); + + // Check that default satellite source is successfully created. + if (!QGeoSatelliteInfoSource::availableSources().isEmpty()) + QVERIFY(source); + else + QVERIFY(!source); + + delete parent; +} + +void TestQGeoSatelliteInfoSource::createDefaultSource_noParent() +{ + QGeoSatelliteInfoSource *source = QGeoSatelliteInfoSource::createDefaultSource(0); + + // Check that default satellite source is successfully created. + if (!QGeoSatelliteInfoSource::availableSources().isEmpty()) + QVERIFY(source); + else + QVERIFY(!source); + + delete source; +} + +void TestQGeoSatelliteInfoSource::updateInterval() +{ + MySatelliteSource s; + QCOMPARE(s.updateInterval(), 0); +} + +void TestQGeoSatelliteInfoSource::setUpdateInterval() +{ + CHECK_SOURCE_VALID; + + QFETCH(int, interval); + QFETCH(int, expectedInterval); + + m_source->setUpdateInterval(interval); + QCOMPARE(m_source->updateInterval(), expectedInterval); +} + +void TestQGeoSatelliteInfoSource::setUpdateInterval_data() +{ + QTest::addColumn("interval"); + QTest::addColumn("expectedInterval"); + QGeoSatelliteInfoSource *source = createTestSource(); + int minUpdateInterval = source ? source->minimumUpdateInterval() : -1; + if (source) + delete source; + + QTest::newRow("0") << 0 << 0; + + if (minUpdateInterval > -1) { + QTest::newRow("INT_MIN") << INT_MIN << minUpdateInterval; + QTest::newRow("-1") << -1 << minUpdateInterval; + } + + if (minUpdateInterval > 0) { + QTest::newRow("more than minInterval") << minUpdateInterval + 1 << minUpdateInterval + 1; + QTest::newRow("equal to minInterval") << minUpdateInterval << minUpdateInterval; + } + + if (minUpdateInterval > 1) { + QTest::newRow("less then minInterval") << minUpdateInterval - 1 << minUpdateInterval; + QTest::newRow("in btw zero and minInterval") << 1 << minUpdateInterval; + } +} + +void TestQGeoSatelliteInfoSource::minimumUpdateInterval() +{ + CHECK_SOURCE_VALID; + + QVERIFY(m_source->minimumUpdateInterval() > 0); +} + +void TestQGeoSatelliteInfoSource::startUpdates_testIntervals() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy timeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->setUpdateInterval(7000); + int interval = m_source->updateInterval(); + + m_source->startUpdates(); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 9500); + for (int i = 0; i < 6; i++) { + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1) && (timeout.count() == 0), (interval*2)); + spyView.clear(); + spyUse.clear(); + } + m_source->stopUpdates(); +} + + +void TestQGeoSatelliteInfoSource::startUpdates_testIntervalChangesWhileRunning() +{ + // There are two ways of dealing with an interval change, and we have left it system dependent. + // The interval can be changed will running or after the next update. + // WinCE uses the first method, S60 uses the second method. + + // The minimum interval on the symbian emulator is 5000 msecs, which is why the times in + // this test are as high as they are. + + CHECK_SOURCE_VALID; + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy timeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + m_source->setUpdateInterval(0); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() > 0) && (spyUse.count() > 0), 7000); + QCOMPARE(timeout.count(), 0); + spyView.clear(); + spyUse.clear(); + + m_source->setUpdateInterval(5000); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 2) && (spyUse.count() == 2) && (timeout.count() == 0), 15000); + spyView.clear(); + spyUse.clear(); + + m_source->setUpdateInterval(10000); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 2) && (spyUse.count() == 2) && (timeout.count() == 0), 30000); + spyView.clear(); + spyUse.clear(); + + m_source->setUpdateInterval(5000); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 2) && (spyUse.count() == 2) && (timeout.count() == 0), 15000); + spyView.clear(); + spyUse.clear(); + + m_source->setUpdateInterval(5000); + + QTRY_VERIFY_WITH_TIMEOUT( (spyView.count() == 2) && (spyUse.count() == 2) && (timeout.count() == 0), 15000); + spyView.clear(); + spyUse.clear(); + + m_source->setUpdateInterval(0); + + QTRY_VERIFY_WITH_TIMEOUT( (spyView.count() > 0 ) && (spyUse.count() > 0) && (timeout.count() == 0), 7000); + spyView.clear(); + spyUse.clear(); + + m_source->setUpdateInterval(0); + + QTRY_VERIFY_WITH_TIMEOUT( (spyView.count() > 0 ) && (spyUse.count() > 0) && (timeout.count() == 0), 7000); + spyView.clear(); + spyUse.clear(); + m_source->stopUpdates(); +} + +void TestQGeoSatelliteInfoSource::startUpdates_testDefaultInterval() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy timeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->startUpdates(); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + for (int i = 0; i < 3; i++) { + QTRY_VERIFY_WITH_TIMEOUT( (spyView.count() > 0 ) && (spyUse.count() > 0) && (timeout.count() == 0), 7000); + spyView.clear(); + spyUse.clear(); + } + m_source->stopUpdates(); +} + +void TestQGeoSatelliteInfoSource::startUpdates_testZeroInterval() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy timeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + for (int i = 0; i < 3; i++) { + QTRY_VERIFY_WITH_TIMEOUT( (spyView.count() > 0 ) && (spyUse.count() > 0) && (timeout.count() == 0), 7000); + spyView.clear(); + spyUse.clear(); + } + m_source->stopUpdates(); +} + +void TestQGeoSatelliteInfoSource::startUpdates_moreThanOnce() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + m_source->startUpdates(); // check there is no crash + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() > 0) && (spyUse.count() > 0), MAX_WAITING_TIME); + + m_source->startUpdates(); // check there is no crash + + m_source->stopUpdates(); +} + +void TestQGeoSatelliteInfoSource::stopUpdates() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->setUpdateInterval(10000); + m_source->startUpdates(); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + for (int i = 0; i < 2; i++) { + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 12000); + spyView.clear(); + spyUse.clear(); + } + + m_source->stopUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 0) && (spyUse.count() == 0), 12000); +} + +void TestQGeoSatelliteInfoSource::stopUpdates_withoutStart() +{ + CHECK_SOURCE_VALID; + + m_source->stopUpdates(); // check there is no crash +} + +void TestQGeoSatelliteInfoSource::requestUpdate() +{ + CHECK_SOURCE_VALID; + + QFETCH(int, timeout); + QSignalSpy spy(m_source, SIGNAL(requestTimeout())); + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(timeout); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + // Geoclue may deliver update instantly if there is a satellite fix + QTRY_VERIFY_WITH_TIMEOUT(!spy.isEmpty() || !spyView.isEmpty(), 10); +} + +void TestQGeoSatelliteInfoSource::requestUpdate_data() +{ + QTest::addColumn("timeout"); + QTest::newRow("less than zero") << -1; + QTest::newRow("very small timeout") << 1; +} + +void TestQGeoSatelliteInfoSource::requestUpdate_validTimeout() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy spyTimeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(7000); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT( + (spyView.count() == 1) && (spyUse.count() == 1 && (spyTimeout.count()) == 0), 7000); +} + +void TestQGeoSatelliteInfoSource::requestUpdate_defaultTimeout() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy spyTimeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(0); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT( + (spyView.count() == 1) && (spyUse.count() == 1 && (spyTimeout.count()) == 0), + MAX_WAITING_TIME); +} + +void TestQGeoSatelliteInfoSource::requestUpdate_timeoutLessThanMinimumInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyTimeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(1); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + QTRY_COMPARE_WITH_TIMEOUT(spyTimeout.count(), 1, 1000); +} + +void TestQGeoSatelliteInfoSource::requestUpdate_repeatedCalls() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(7000); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 7000); + spyView.clear(); + spyUse.clear(); + + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 7000); +} + +void TestQGeoSatelliteInfoSource::requestUpdate_overlappingCalls() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(7000); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 7000); +} + +void TestQGeoSatelliteInfoSource::requestUpdate_overlappingCallsWithTimeout() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy spyTimeout(m_source, + SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(0); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + m_source->requestUpdate(1); + + QTRY_COMPARE_WITH_TIMEOUT(spyTimeout.count(), 0, 7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 7000); +} + +void TestQGeoSatelliteInfoSource::requestUpdateAfterStartUpdates_ZeroInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy spyTimeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), MAX_WAITING_TIME); + spyView.clear(); + spyUse.clear(); + + m_source->requestUpdate(7000); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1) + && (spyTimeout.count() == 0), 7000); + + spyView.clear(); + spyUse.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 12000); + + m_source->stopUpdates(); +} + +void TestQGeoSatelliteInfoSource::requestUpdateAfterStartUpdates_SmallInterval() +{ + CHECK_SOURCE_VALID; + + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy spyTimeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->setUpdateInterval(10000); + m_source->requestUpdate(7000); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + m_source->startUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() > 0) && (spyUse.count() > 0) + && (spyTimeout.count() == 0), 7000); + + spyView.clear(); + spyUse.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() == 1) && (spyUse.count() == 1), 12000); + + m_source->stopUpdates(); +} + +void TestQGeoSatelliteInfoSource::requestUpdateBeforeStartUpdates_ZeroInterval() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy timeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(7000); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + m_source->setUpdateInterval(0); + m_source->startUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() >= 2) && (spyUse.count() >= 2) && (timeout.count() == 0), 14000); + spyView.clear(); + spyUse.clear(); + + QTest::qWait(7000); + + QCOMPARE(timeout.count(), 0); + + m_source->stopUpdates(); +} + +void TestQGeoSatelliteInfoSource::requestUpdateBeforeStartUpdates_SmallInterval() +{ + CHECK_SOURCE_VALID; + QSignalSpy spyView(m_source, + SIGNAL(satellitesInViewUpdated(QList))); + QSignalSpy spyUse(m_source, + SIGNAL(satellitesInUseUpdated(QList))); + QSignalSpy timeout(m_source, SIGNAL(requestTimeout())); + QSignalSpy errorSpy(m_source, SIGNAL(error(QGeoSatelliteInfoSource::Error))); + + m_source->requestUpdate(7000); + + if (!errorSpy.isEmpty()) + QSKIP("Error starting satellite updates."); + + m_source->setUpdateInterval(10000); + m_source->startUpdates(); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() > 0) && (spyUse.count() > 0) && (timeout.count() == 0), 7000); + spyView.clear(); + spyUse.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyView.count() > 0) && (spyUse.count() > 0) && (timeout.count() == 0), 20000); + + m_source->stopUpdates(); +} + + + +void TestQGeoSatelliteInfoSource::removeSlotForRequestTimeout() +{ + CHECK_SOURCE_VALID; + + bool i = connect(m_source, SIGNAL(requestTimeout()), this, SLOT(test_slot1())); + QVERIFY(i==true); + i = connect(m_source, SIGNAL(requestTimeout()), this, SLOT(test_slot2())); + QVERIFY(i==true); + i = disconnect(m_source, SIGNAL(requestTimeout()), this, SLOT(test_slot1())); + QVERIFY(i==true); + + m_source->requestUpdate(-1); + QTRY_VERIFY_WITH_TIMEOUT((m_testSlot2Called == true), 1000); +} + +void TestQGeoSatelliteInfoSource::removeSlotForSatellitesInUseUpdated() +{ + CHECK_SOURCE_VALID; + + bool i = connect(m_source, SIGNAL(satellitesInUseUpdated(QList)), this, SLOT(test_slot1())); + QVERIFY(i == true); + i = connect(m_source, SIGNAL(satellitesInUseUpdated(QList)), this, SLOT(test_slot2())); + QVERIFY(i == true); + i = disconnect(m_source, SIGNAL(satellitesInUseUpdated(QList)), this, SLOT(test_slot1())); + QVERIFY(i == true); + + m_source->requestUpdate(7000); + + if (m_source->error() != QGeoSatelliteInfoSource::NoError) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT((m_testSlot2Called == true), 7000); +} + +void TestQGeoSatelliteInfoSource::removeSlotForSatellitesInViewUpdated() +{ + CHECK_SOURCE_VALID; + + bool i = connect(m_source, SIGNAL(satellitesInViewUpdated(QList)), this, SLOT(test_slot1())); + QVERIFY(i == true); + i = connect(m_source, SIGNAL(satellitesInViewUpdated(QList)), this, SLOT(test_slot2())); + QVERIFY(i == true); + i = disconnect(m_source, SIGNAL(satellitesInViewUpdated(QList)), this, SLOT(test_slot1())); + QVERIFY(i == true); + + m_source->requestUpdate(7000); + + if (m_source->error() != QGeoSatelliteInfoSource::NoError) + QSKIP("Error starting satellite updates."); + + QTRY_VERIFY_WITH_TIMEOUT((m_testSlot2Called == true), 7000); +} + +#include "testqgeosatelliteinfosource.moc" diff --git a/tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource_p.h b/tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource_p.h new file mode 100644 index 0000000..cf15f7c --- /dev/null +++ b/tests/auto/qgeosatelliteinfosource/testqgeosatelliteinfosource_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTQGEOSATELLITEINFOSOURCE_H +#define TESTQGEOSATELLITEINFOSOURCE_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QGeoSatelliteInfoSource; +QT_END_NAMESPACE + + +class TestQGeoSatelliteInfoSource : public QObject +{ + Q_OBJECT + +public: + TestQGeoSatelliteInfoSource(QObject *parent = 0); + + static TestQGeoSatelliteInfoSource *createDefaultSourceTest(); + +protected: + virtual QGeoSatelliteInfoSource *createTestSource() = 0; + + // MUST be called by subclasses if they override respective test slots + void base_initTestCase(); + void base_init(); + void base_cleanup(); + void base_cleanupTestCase(); + +public slots: + void test_slot1(); + void test_slot2(); + +private slots: + void initTestCase(); + void init(); + void cleanup(); + void cleanupTestCase(); + + void constructor_withParent(); + void constructor_noParent(); + + void updateInterval(); + + void setUpdateInterval(); + void setUpdateInterval_data(); + + void minimumUpdateInterval(); + + void createDefaultSource(); + void createDefaultSource_noParent(); + + void startUpdates_testIntervals(); + void startUpdates_testIntervalChangesWhileRunning(); + void startUpdates_testDefaultInterval(); + void startUpdates_testZeroInterval(); + void startUpdates_moreThanOnce(); + void stopUpdates(); + void stopUpdates_withoutStart(); + + void requestUpdate(); + void requestUpdate_data(); + + void requestUpdate_validTimeout(); + void requestUpdate_defaultTimeout(); + void requestUpdate_timeoutLessThanMinimumInterval(); + void requestUpdate_repeatedCalls(); + void requestUpdate_overlappingCalls(); + void requestUpdate_overlappingCallsWithTimeout(); + + void requestUpdateAfterStartUpdates_ZeroInterval(); + void requestUpdateAfterStartUpdates_SmallInterval(); + void requestUpdateBeforeStartUpdates_ZeroInterval(); + void requestUpdateBeforeStartUpdates_SmallInterval(); + + void removeSlotForRequestTimeout(); + void removeSlotForSatellitesInUseUpdated(); + void removeSlotForSatellitesInViewUpdated(); + +private: + QGeoSatelliteInfoSource *m_source; + bool m_testingDefaultSource; + bool m_testSlot2Called; +}; + +#endif // #ifndef TESTQGEOSATELLITEINFOSOURCE_H diff --git a/tests/auto/qgeosatelliteinfosource/tst_qgeosatelliteinfosource.cpp b/tests/auto/qgeosatelliteinfosource/tst_qgeosatelliteinfosource.cpp new file mode 100644 index 0000000..db8099a --- /dev/null +++ b/tests/auto/qgeosatelliteinfosource/tst_qgeosatelliteinfosource.cpp @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "testqgeosatelliteinfosource_p.h" +#include + +int main(int argc, char *argv[]) +{ + QCoreApplication app(argc, argv); + TestQGeoSatelliteInfoSource *test = TestQGeoSatelliteInfoSource::createDefaultSourceTest(); + return QTest::qExec(test, argc, argv); +} diff --git a/tests/auto/qgeoserviceprovider/qgeoserviceprovider.pro b/tests/auto/qgeoserviceprovider/qgeoserviceprovider.pro new file mode 100644 index 0000000..5dfe2dd --- /dev/null +++ b/tests/auto/qgeoserviceprovider/qgeoserviceprovider.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG+=testcase +TARGET=tst_qgeoserviceprovider + +SOURCES += tst_qgeoserviceprovider.cpp + +CONFIG -= app_bundle + +QT += testlib location diff --git a/tests/auto/qgeoserviceprovider/tst_qgeoserviceprovider.cpp b/tests/auto/qgeoserviceprovider/tst_qgeoserviceprovider.cpp new file mode 100644 index 0000000..e0c581a --- /dev/null +++ b/tests/auto/qgeoserviceprovider/tst_qgeoserviceprovider.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QGeoServiceProvider : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void tst_availableServiceProvider(); + void tst_features_data(); + void tst_features(); + void tst_misc(); + void tst_nokiaRename(); +}; + +void tst_QGeoServiceProvider::initTestCase() +{ +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif +} + +void tst_QGeoServiceProvider::tst_availableServiceProvider() +{ + const QStringList provider = QGeoServiceProvider::availableServiceProviders(); + + // Currently provided plugins + if (provider.count() != 8) + qWarning() << provider; + QVERIFY(provider.count() >= 8); + // these providers are deployed + QVERIFY(provider.contains(QStringLiteral("mapbox"))); + QVERIFY(provider.contains(QStringLiteral("here"))); + QVERIFY(provider.contains(QStringLiteral("osm"))); + QVERIFY(provider.contains(QStringLiteral("esri"))); + // these providers exist for unit tests only + QVERIFY(provider.contains(QStringLiteral("geocode.test.plugin"))); + QVERIFY(provider.contains(QStringLiteral("georoute.test.plugin"))); + QVERIFY(provider.contains(QStringLiteral("qmlgeo.test.plugin"))); + QVERIFY(provider.contains(QStringLiteral("test.places.unsupported"))); + +} + +Q_DECLARE_METATYPE(QGeoServiceProvider::MappingFeatures) +Q_DECLARE_METATYPE(QGeoServiceProvider::GeocodingFeatures) +Q_DECLARE_METATYPE(QGeoServiceProvider::RoutingFeatures) +Q_DECLARE_METATYPE(QGeoServiceProvider::PlacesFeatures) + +void tst_QGeoServiceProvider::tst_features_data() +{ + QTest::addColumn("providerName"); + QTest::addColumn("mappingFeatures"); + QTest::addColumn("codingFeatures"); + QTest::addColumn("routingFeatures"); + QTest::addColumn("placeFeatures"); + + QTest::newRow("invalid") << QString("non-existing-provider-name") + << QGeoServiceProvider::MappingFeatures(QGeoServiceProvider::NoMappingFeatures) + << QGeoServiceProvider::GeocodingFeatures(QGeoServiceProvider::NoGeocodingFeatures) + << QGeoServiceProvider::RoutingFeatures(QGeoServiceProvider::NoRoutingFeatures) + << QGeoServiceProvider::PlacesFeatures(QGeoServiceProvider::NoPlacesFeatures); + + QTest::newRow("mapbox") << QString("mapbox") + << QGeoServiceProvider::MappingFeatures(QGeoServiceProvider::OnlineMappingFeature) + << QGeoServiceProvider::GeocodingFeatures(QGeoServiceProvider::OnlineGeocodingFeature + | QGeoServiceProvider::ReverseGeocodingFeature + | QGeoServiceProvider::LocalizedGeocodingFeature) + << QGeoServiceProvider::RoutingFeatures(QGeoServiceProvider::OnlineRoutingFeature) + << QGeoServiceProvider::PlacesFeatures(QGeoServiceProvider::OnlinePlacesFeature + | QGeoServiceProvider::PlaceRecommendationsFeature + | QGeoServiceProvider::SearchSuggestionsFeature + | QGeoServiceProvider::LocalizedPlacesFeature); + + QTest::newRow("here") << QString("here") + << QGeoServiceProvider::MappingFeatures(QGeoServiceProvider::OnlineMappingFeature) + << QGeoServiceProvider::GeocodingFeatures(QGeoServiceProvider::OnlineGeocodingFeature + | QGeoServiceProvider::ReverseGeocodingFeature) + << QGeoServiceProvider::RoutingFeatures(QGeoServiceProvider::OnlineRoutingFeature + | QGeoServiceProvider::RouteUpdatesFeature + | QGeoServiceProvider::AlternativeRoutesFeature + | QGeoServiceProvider::ExcludeAreasRoutingFeature) + << QGeoServiceProvider::PlacesFeatures(QGeoServiceProvider::OnlinePlacesFeature + | QGeoServiceProvider::PlaceRecommendationsFeature + | QGeoServiceProvider::SearchSuggestionsFeature + | QGeoServiceProvider::LocalizedPlacesFeature); + + QTest::newRow("osm") << QString("osm") + << QGeoServiceProvider::MappingFeatures(QGeoServiceProvider::OnlineMappingFeature) + << QGeoServiceProvider::GeocodingFeatures(QGeoServiceProvider::OnlineGeocodingFeature + | QGeoServiceProvider::ReverseGeocodingFeature) + << QGeoServiceProvider::RoutingFeatures(QGeoServiceProvider::OnlineRoutingFeature) + << QGeoServiceProvider::PlacesFeatures(QGeoServiceProvider::OnlinePlacesFeature); + + QTest::newRow("esri") << QString("esri") + << QGeoServiceProvider::MappingFeatures(QGeoServiceProvider::OnlineMappingFeature) + << QGeoServiceProvider::GeocodingFeatures(QGeoServiceProvider::OnlineGeocodingFeature + | QGeoServiceProvider::ReverseGeocodingFeature) + << QGeoServiceProvider::RoutingFeatures(QGeoServiceProvider::OnlineRoutingFeature) + << QGeoServiceProvider::PlacesFeatures(QGeoServiceProvider::NoPlacesFeatures); +} + +void tst_QGeoServiceProvider::tst_features() +{ + QFETCH(QString, providerName); + QFETCH(QGeoServiceProvider::MappingFeatures, mappingFeatures); + QFETCH(QGeoServiceProvider::GeocodingFeatures, codingFeatures); + QFETCH(QGeoServiceProvider::RoutingFeatures, routingFeatures); + QFETCH(QGeoServiceProvider::PlacesFeatures, placeFeatures); + + QGeoServiceProvider provider(providerName); + QCOMPARE(provider.mappingFeatures(), mappingFeatures); + QCOMPARE(provider.geocodingFeatures(), codingFeatures); + QCOMPARE(provider.routingFeatures(), routingFeatures); + QCOMPARE(provider.placesFeatures(), placeFeatures); + + if (provider.mappingFeatures() == QGeoServiceProvider::NoMappingFeatures) { + QVERIFY(provider.mappingManager() == nullptr); + } else { + // some plugins require token/access parameter + // they return 0 but set QGeoServiceProvider::MissingRequiredParameterError + if (provider.mappingManager() != nullptr) + QCOMPARE(provider.error(), QGeoServiceProvider::NoError); + else + QCOMPARE(provider.error(), QGeoServiceProvider::MissingRequiredParameterError); + } + + if (provider.geocodingFeatures() == QGeoServiceProvider::NoGeocodingFeatures) { + QVERIFY(provider.geocodingManager() == nullptr); + } else { + if (provider.geocodingManager() != nullptr) + QVERIFY(provider.geocodingManager() != nullptr); //pointless but we want a VERIFY here + else + QCOMPARE(provider.error(), QGeoServiceProvider::MissingRequiredParameterError); + } + + if (provider.routingFeatures() == QGeoServiceProvider::NoRoutingFeatures) { + QVERIFY(provider.routingManager() == nullptr); + } else { + if (provider.routingManager() != nullptr) + QCOMPARE(provider.error(), QGeoServiceProvider::NoError); + else + QCOMPARE(provider.error(), QGeoServiceProvider::MissingRequiredParameterError); + } + + if (provider.placesFeatures() == QGeoServiceProvider::NoPlacesFeatures) { + QVERIFY(provider.placeManager() == nullptr); + } else { + if (provider.placeManager() != nullptr) + QCOMPARE(provider.error(), QGeoServiceProvider::NoError); + else + QCOMPARE(provider.error(), QGeoServiceProvider::MissingRequiredParameterError); + } +} + +void tst_QGeoServiceProvider::tst_misc() +{ + const QStringList provider = QGeoServiceProvider::availableServiceProviders(); + QVERIFY(provider.contains(QStringLiteral("osm"))); + QVERIFY(provider.contains(QStringLiteral("geocode.test.plugin"))); + + QGeoServiceProvider test_experimental( + QStringLiteral("geocode.test.plugin"), QVariantMap(), true); + QGeoServiceProvider test_noexperimental( + QStringLiteral("geocode.test.plugin"), QVariantMap(), false); + QCOMPARE(test_experimental.error(), QGeoServiceProvider::NoError); + QCOMPARE(test_noexperimental.error(), QGeoServiceProvider::NotSupportedError); + + QGeoServiceProvider osm_experimental( + QStringLiteral("osm"), QVariantMap(), true); + QGeoServiceProvider osm_noexperimental( + QStringLiteral("osm"), QVariantMap(), false); + QCOMPARE(osm_experimental.error(), QGeoServiceProvider::NoError); + QCOMPARE(osm_noexperimental.error(), QGeoServiceProvider::NoError); +} + +void tst_QGeoServiceProvider::tst_nokiaRename() +{ + // The "nokia" plugin was renamed to "here". + // It remains available under the name "nokia" for now + // but is not advertised via QGeoServiceProvider::availableServiceProviders() + + QVERIFY(!QGeoServiceProvider::availableServiceProviders().contains("nokia")); + QGeoServiceProvider provider(QStringLiteral("nokia")); + QCOMPARE(provider.error(), QGeoServiceProvider::NoError); + +} + +QTEST_GUILESS_MAIN(tst_QGeoServiceProvider) + +#include "tst_qgeoserviceprovider.moc" diff --git a/tests/auto/qgeoshape/qgeoshape.pro b/tests/auto/qgeoshape/qgeoshape.pro new file mode 100644 index 0000000..dd55138 --- /dev/null +++ b/tests/auto/qgeoshape/qgeoshape.pro @@ -0,0 +1,5 @@ +load(testcase) +TARGET = tst_qgeoshape +QT += testlib positioning +SOURCES = \ + tst_qgeoshape.cpp diff --git a/tests/auto/qgeoshape/tst_qgeoshape.cpp b/tests/auto/qgeoshape/tst_qgeoshape.cpp new file mode 100644 index 0000000..72611ff --- /dev/null +++ b/tests/auto/qgeoshape/tst_qgeoshape.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +QString tst_qgeoshape_debug; + +void tst_qgeoshape_messageHandler(QtMsgType type, const QMessageLogContext&, + const QString &msg) +{ + switch (type) { + case QtDebugMsg : + tst_qgeoshape_debug = msg; + break; + default: + break; + } +} + +class tst_qgeoshape : public QObject +{ + Q_OBJECT + +private slots: + void testArea(); + void debug_data(); + void debug(); + void conversions(); +}; + +void tst_qgeoshape::testArea() +{ + QGeoShape area; + QVERIFY(!area.isValid()); + QVERIFY(area.isEmpty()); + QCOMPARE(area.type(), QGeoShape::UnknownType); + QVERIFY(!area.contains(QGeoCoordinate())); + + // QGeoShape never constructs a QGeoShapePrivate. Hence d_ptr is always 0. + + QGeoShape area2; + + QCOMPARE(area, area2); + + area = area2; + + QCOMPARE(area, area2); + + QGeoShape area3(area2); + + QCOMPARE(area2, area3); +} + +void tst_qgeoshape::debug_data() +{ + QTest::addColumn("shape"); + QTest::addColumn("nextValue"); + QTest::addColumn("debugString"); + + QTest::newRow("uninitialized") << QGeoShape() << 45 + << QString("QGeoShape(Unknown) 45"); + QTest::newRow("uninitialized") << QGeoShape(QGeoRectangle()) << 45 + << QString("QGeoShape(Rectangle) 45"); + QTest::newRow("uninitialized") << QGeoShape(QGeoCircle()) << 45 + << QString("QGeoShape(Circle) 45"); +} + + +void tst_qgeoshape::debug() +{ + QFETCH(QGeoShape, shape); + QFETCH(int, nextValue); + QFETCH(QString, debugString); + + qInstallMessageHandler(tst_qgeoshape_messageHandler); + qDebug() << shape << nextValue; + qInstallMessageHandler(0); + QCOMPARE(tst_qgeoshape_debug, debugString); +} + +void tst_qgeoshape::conversions() +{ + QVariant varShape = QVariant::fromValue(QGeoShape()); + QVariant varRect = QVariant::fromValue(QGeoRectangle( + QGeoCoordinate(1, 1), + QGeoCoordinate(2, 2))); + QVariant varCircle = QVariant::fromValue(QGeoCircle(QGeoCoordinate(3, 3), 1000)); + + QVERIFY(varShape.canConvert()); + QVERIFY(varShape.canConvert()); + QVERIFY(varShape.canConvert()); + QVERIFY(!varRect.canConvert()); + QVERIFY(varRect.canConvert()); + QVERIFY(varRect.canConvert()); + QVERIFY(varCircle.canConvert()); + QVERIFY(!varCircle.canConvert()); + QVERIFY(varCircle.canConvert()); +} + +QTEST_MAIN(tst_qgeoshape) +#include "tst_qgeoshape.moc" diff --git a/tests/auto/qgeotiledmap/BLACKLIST b/tests/auto/qgeotiledmap/BLACKLIST new file mode 100644 index 0000000..9faf97a --- /dev/null +++ b/tests/auto/qgeotiledmap/BLACKLIST @@ -0,0 +1,2 @@ +[fetchTiles] +b2qt ci diff --git a/tests/auto/qgeotiledmap/qgeotiledmap.pro b/tests/auto/qgeotiledmap/qgeotiledmap.pro new file mode 100644 index 0000000..b485022 --- /dev/null +++ b/tests/auto/qgeotiledmap/qgeotiledmap.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeotiledmap +INCLUDEPATH += ../geotestplugin + +SOURCES += tst_qgeotiledmap.cpp + +QT += location-private positioning-private testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qgeotiledmap/tst_qgeotiledmap.cpp b/tests/auto/qgeotiledmap/tst_qgeotiledmap.cpp new file mode 100644 index 0000000..5da6b4d --- /dev/null +++ b/tests/auto/qgeotiledmap/tst_qgeotiledmap.cpp @@ -0,0 +1,212 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgeotiledmap_test.h" +#include "qgeotilefetcher_test.h" +#include "qgeotiledmappingmanagerengine_test.h" +#include +#include +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +Q_DECLARE_METATYPE(QGeoTiledMap::PrefetchStyle) + +class FetchTileCounter: public QObject +{ + Q_OBJECT +public Q_SLOTS: + void tileFetched(const QGeoTileSpec& spec) { + m_tiles << spec; + } +public: + QSet m_tiles; +}; + +class tst_QGeoTiledMap : public QObject +{ + Q_OBJECT + +public: + tst_QGeoTiledMap(); + ~tst_QGeoTiledMap(); + +private: + void waitForFetch(int count); + +private Q_SLOTS: + void initTestCase(); + void fetchTiles(); + void fetchTiles_data(); + +private: + QScopedPointer m_map; + QScopedPointer m_tilesCounter; + QGeoTileFetcherTest *m_fetcher; + +}; + +tst_QGeoTiledMap::tst_QGeoTiledMap(): + m_fetcher(0) +{ +} + +tst_QGeoTiledMap::~tst_QGeoTiledMap() +{ +} + +void tst_QGeoTiledMap::initTestCase() +{ +#if QT_CONFIG(library) + // Set custom path since CI doesn't install test plugins +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif + QVariantMap parameters; + parameters["tileSize"] = 256; + parameters["maxZoomLevel"] = 8; + parameters["finishRequestImmediately"] = true; + QGeoServiceProvider *provider = new QGeoServiceProvider("qmlgeo.test.plugin",parameters); + provider->setAllowExperimental(true); + QGeoMappingManager *mappingManager = provider->mappingManager(); + QVERIFY2(provider->error() == QGeoServiceProvider::NoError, "Could not load plugin: " + provider->errorString().toLatin1()); + m_map.reset(static_cast(mappingManager->createMap(this))); + QVERIFY(m_map); + m_map->setViewportSize(QSize(256, 256)); + m_map->setActiveMapType(m_map->m_engine->supportedMapTypes().first()); + m_fetcher = static_cast(m_map->m_engine->tileFetcher()); + m_tilesCounter.reset(new FetchTileCounter()); + connect(m_fetcher, SIGNAL(tileFetched(const QGeoTileSpec&)), m_tilesCounter.data(), SLOT(tileFetched(const QGeoTileSpec&))); +} + +void tst_QGeoTiledMap::fetchTiles() +{ + QFETCH(double, zoomLevel); + QFETCH(int, visibleCount); + QFETCH(int, prefetchCount); + QFETCH(QGeoTiledMap::PrefetchStyle, style); + QFETCH(int, nearestNeighbourLayer); + + m_map->setPrefetchStyle(style); + + QGeoCameraData camera; + camera.setCenter(QWebMercator::mercatorToCoord(QDoubleVector2D( 0.5 , 0.5 ))); + + //prev_visible + camera.setZoomLevel(zoomLevel-1); + // Delay needed on slow targets (e.g. Qemu) + QTest::qWait(10); + m_map->clearData(); + m_tilesCounter->m_tiles.clear(); + m_map->setCameraData(camera); + waitForFetch(visibleCount); + QSet prev_visible = m_tilesCounter->m_tiles; + + //visible + prefetch + camera.setZoomLevel(zoomLevel); + // Delay needed on slow targets (e.g. Qemu) + QTest::qWait(10); + m_map->clearData(); + m_tilesCounter->m_tiles.clear(); + m_map->setCameraData(camera); + waitForFetch(visibleCount); + QSet visible = m_tilesCounter->m_tiles; + m_map->clearData(); + m_tilesCounter->m_tiles.clear(); + m_map->prefetchData(); + waitForFetch(prefetchCount); + QSet prefetched = m_tilesCounter->m_tiles; + + //next visible + camera.setZoomLevel(zoomLevel + 1); + // Delay needed on slow targets (e.g. Qemu) + QTest::qWait(10); + m_map->clearData(); + m_tilesCounter->m_tiles.clear(); + m_map->setCameraData(camera); + waitForFetch(visibleCount); + QSet next_visible = m_tilesCounter->m_tiles; + + QVERIFY2(visibleCount == visible.size(), "visible count incorrect"); + QVERIFY2(prefetchCount == prefetched.size(), "prefetch count incorrect"); + QSetIterator i(visible); + while (i.hasNext()) + QVERIFY2(prefetched.contains(i.next()),"visible tile missing from prefetched tiles"); + + //for zoomLevels wihtout fractions more tiles are fetched for current zoomlevel due to ViewExpansion + if (qCeil(zoomLevel) != zoomLevel && style == QGeoTiledMap::PrefetchNeighbourLayer && nearestNeighbourLayer < zoomLevel) + QVERIFY2(prefetched == prev_visible + visible, "wrongly prefetched tiles"); + + if (qCeil(zoomLevel) != zoomLevel && style == QGeoTiledMap::PrefetchNeighbourLayer && nearestNeighbourLayer > zoomLevel) + QVERIFY2(prefetched == next_visible + visible, "wrongly prefetched tiles"); + + if (qCeil(zoomLevel) != zoomLevel && style == QGeoTiledMap::PrefetchTwoNeighbourLayers) + QVERIFY2(prefetched == prev_visible + visible + next_visible, "wrongly prefetched tiles"); +} + +void tst_QGeoTiledMap::fetchTiles_data() +{ + QTest::addColumn("zoomLevel"); + QTest::addColumn("visibleCount"); + QTest::addColumn("prefetchCount"); + QTest::addColumn("style"); + QTest::addColumn("nearestNeighbourLayer"); + QTest::newRow("zoomLevel: 4 , visible count: 4 : prefetch count: 16") << 4.0 << 4 << 4 + 16 << QGeoTiledMap::PrefetchNeighbourLayer << 3; + QTest::newRow("zoomLevel: 4.06 , visible count: 4 : prefetch count: 4") << 4.06 << 4 << 4 + 4 << QGeoTiledMap::PrefetchNeighbourLayer << 3; + QTest::newRow("zoomLevel: 4.1 , visible count: 4 : prefetch count: 4") << 4.1 << 4 << 4 + 4 << QGeoTiledMap::PrefetchNeighbourLayer << 3; + QTest::newRow("zoomLevel: 4.5 , visible count: 4 : prefetch count: 4") << 4.5 << 4 << 4 + 4 << QGeoTiledMap::PrefetchNeighbourLayer << 3; + QTest::newRow("zoomLevel: 4.6 , visible count: 4 : prefetch count: 4") << 4.6 << 4 << 4 + 4 << QGeoTiledMap::PrefetchNeighbourLayer << 5; + QTest::newRow("zoomLevel: 4.9 , visible count: 4 : prefetch count: 4") << 4.9 << 4 <<4 + 4 << QGeoTiledMap::PrefetchNeighbourLayer << 5; + QTest::newRow("zoomLevel: 4 , visible count: 4 : prefetch count: 4") << 4.0 << 4 << 16 + 4 + 4 << QGeoTiledMap::PrefetchTwoNeighbourLayers << 3; + QTest::newRow("zoomLevel: 4.1 , visible count: 4 : prefetch count: 4") << 4.1 << 4 << 4 + 4 + 4 << QGeoTiledMap::PrefetchTwoNeighbourLayers << 3; + QTest::newRow("zoomLevel: 4.6 ,visible count: 4 : prefetch count: 4") << 4.6 << 4 << 4 + 4 + 4 << QGeoTiledMap::PrefetchTwoNeighbourLayers << 5; +} + +void tst_QGeoTiledMap::waitForFetch(int count) +{ + int timeout = 0; + while (m_tilesCounter->m_tiles.count() < count && timeout < count) { + //250ms for each tile fetch + QTest::qWait(250); + timeout++; + } +} + +QTEST_MAIN(tst_QGeoTiledMap) + +#include "tst_qgeotiledmap.moc" diff --git a/tests/auto/qgeotiledmapscene/qgeotiledmapscene.pro b/tests/auto/qgeotiledmapscene/qgeotiledmapscene.pro new file mode 100644 index 0000000..0db4b54 --- /dev/null +++ b/tests/auto/qgeotiledmapscene/qgeotiledmapscene.pro @@ -0,0 +1,8 @@ +CONFIG += testcase +TARGET = tst_qgeotiledmapscene + +INCLUDEPATH += ../../../src/location/maps + +SOURCES += tst_qgeotiledmapscene.cpp + +QT += location-private positioning-private testlib diff --git a/tests/auto/qgeotiledmapscene/tst_qgeotiledmapscene.cpp b/tests/auto/qgeotiledmapscene/tst_qgeotiledmapscene.cpp new file mode 100644 index 0000000..6b3dc1f --- /dev/null +++ b/tests/auto/qgeotiledmapscene/tst_qgeotiledmapscene.cpp @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location/maps + +#include "qgeotilespec_p.h" +#include "qgeotiledmapscene_p.h" +#include "qgeocameratiles_p.h" +#include "qgeocameradata_p.h" +#include "qabstractgeotilecache_p.h" +#include +#include +#include + +#include + +#include +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QGeoTiledMapScene : public QObject +{ + Q_OBJECT + + private: + void row(QString name, double screenX, double screenX2, double screenY, double cameraCenterX, double cameraCenterY, + double zoom, int tileSize, int screenWidth, int screenHeight, double mercatorX, double mercatorY){ + + // expected behaviour of wrapping + if (mercatorX <= 0.0) + mercatorX += 1.0; + else if (mercatorX > 1.0) + mercatorX -= 1.0; + + QTest::newRow(qPrintable(name)) + << screenX << screenX2 << screenY + << cameraCenterX << cameraCenterY + << zoom << tileSize + << screenWidth << screenHeight + << mercatorX + << mercatorY; + } + + void screenPositions(QString name, double cameraCenterX, double cameraCenterY, double zoom, + int tileSize, int screenWidth, int screenHeight) + { + double screenX; + double screenX2; + double screenY; + double mercatorX; + double mercatorY; + + double halfLength = 1 / (std::pow(2.0, zoom) * 2); + double scaleX = screenWidth / tileSize; + double scaleY = screenHeight / tileSize; + double scaledHalfLengthX = halfLength * scaleX; + double scaledHalfLengthY = halfLength * scaleY; + + bool matchingMapEnds = false; + if (screenWidth == std::pow(2.0, zoom) * tileSize) + matchingMapEnds = true; + + // bottom left + screenX = screenX2 = 0.0; + if (matchingMapEnds) + screenX2 = qAbs(screenX - 1.0) * screenWidth; + screenY = 1.0 * screenHeight; + mercatorX = cameraCenterX - scaledHalfLengthX; + mercatorY = cameraCenterY + scaledHalfLengthY; + row (name + QString("_bottomLeftScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // bottom + screenX = screenX2 = 0.5 * screenWidth; + screenY = 1.0 * screenHeight; + mercatorX = cameraCenterX; + mercatorY = cameraCenterY + scaledHalfLengthY; + row (name + QString("_bottomScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // bottom right + screenX = screenX2 = 1.0 * screenWidth; + if (matchingMapEnds) + screenX2 = qAbs(screenX - 1.0) * screenWidth; + screenY = 1.0 * screenHeight; + mercatorX = cameraCenterX + scaledHalfLengthX; + mercatorY = cameraCenterY + scaledHalfLengthY; + row (name + QString("_bottomRightScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // left + screenX = screenX2 = 0.0 * screenWidth; + if (matchingMapEnds) + screenX2 = qAbs(screenX - 1.0) * screenWidth; + screenY = 0.5 * screenHeight; + mercatorX = cameraCenterX - scaledHalfLengthX; + mercatorY = cameraCenterY; + row (name + QString("_leftScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // center + screenX = screenX2 = 0.5 * screenWidth; + screenY = 0.5 * screenHeight; + mercatorX = cameraCenterX; + mercatorY = cameraCenterY; + row (name + QString("_centerScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // right + screenX = screenX2 = 1.0 * screenWidth; + if (matchingMapEnds) + screenX2 = qAbs(screenX - 1.0) * screenWidth; + screenY = 0.5 * screenHeight; + mercatorX = cameraCenterX + scaledHalfLengthX; + mercatorY = cameraCenterY; + row (name + QString("_rightScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // top left + screenX = screenX2 = 0.0; + if (matchingMapEnds) + screenX2 = qAbs(screenX - 1.0) * screenWidth; + screenY = 0.0; + mercatorX = cameraCenterX - scaledHalfLengthX; + mercatorY = cameraCenterY - scaledHalfLengthY; + row (name + QString("_topLeftScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // top + screenX = screenX2 = 0.5 * screenWidth; + screenY = 0.0; + mercatorX = cameraCenterX; + mercatorY = cameraCenterY - scaledHalfLengthY; + row (name + QString("_topScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + // top right + screenX = screenX2 = 1.0 * screenWidth; + if (matchingMapEnds) + screenX2 = qAbs(screenX - 1.0) * screenWidth; + screenY = 0.0; + mercatorX = cameraCenterX + scaledHalfLengthX; + mercatorY = cameraCenterY - scaledHalfLengthY; + row (name + QString("_topRightScreen"), screenX, screenX2, screenY, cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight, mercatorX, mercatorY); + + } + + void screenCameraPositions(QString name, double zoom, int tileSize, + int screenWidth, int screenHeight) + { + double cameraCenterX; + double cameraCenterY; + + // bottom left + cameraCenterX = 0; + cameraCenterY = 1.0; + screenPositions(name + QString("_bottomLeftCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + + // bottom + cameraCenterX = 0.5; + cameraCenterY = 1.0; + screenPositions(name + QString("_bottomCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + // bottom right + cameraCenterX = 1.0; + cameraCenterY = 1.0; + screenPositions(name + QString("_bottomRightCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + // left + cameraCenterX = 0.0; + cameraCenterY = 0.5; + screenPositions(name + QString("_leftCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + // middle + cameraCenterX = 0.5; + cameraCenterY = 0.5; + screenPositions(name + QString("_middleCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + // right + cameraCenterX = 1.0; + cameraCenterY = 0.5; + screenPositions(name + QString("_rightCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + // top left + cameraCenterX = 0.0; + cameraCenterY = 0.0; + screenPositions(name + QString("_topLeftCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + // top + cameraCenterX = 0.5; + cameraCenterY = 0.0; + screenPositions(name + QString("_topCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + // top right + cameraCenterX = 1.0; + cameraCenterY = 0.0; + screenPositions(name + QString("_topRightCamera"), cameraCenterX, cameraCenterY, + zoom, tileSize, screenWidth, screenHeight); + } + + void populateScreenMercatorData(){ + QTest::addColumn("screenX"); + QTest::addColumn("screenX2"); + QTest::addColumn("screenY"); + QTest::addColumn("cameraCenterX"); + QTest::addColumn("cameraCenterY"); + QTest::addColumn("zoom"); + QTest::addColumn("tileSize"); + QTest::addColumn("screenWidth"); + QTest::addColumn("screenHeight"); + QTest::addColumn("mercatorX"); + QTest::addColumn("mercatorY"); + + int tileSize; + double zoom; + int screenWidth; + int screenHeight; + QString name; + tileSize = 256; + zoom = 1.0; // 4 tiles in the map. map size = 2*tileSize x 2*tileSize + + /* + ScreenWidth = t + ScreenHeight = t + */ + screenWidth = tileSize; + screenHeight = tileSize; + name = QString("_(t x t)"); + screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); + + /* + ScreenWidth = t * 2 + ScreenHeight = t + */ + screenWidth = tileSize * 2; + screenHeight = tileSize; + name = QString("_(2t x t)"); + screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); + + /* + ScreenWidth = t + ScreenHeight = t * 2 + */ + screenWidth = tileSize; + screenHeight = tileSize * 2; + name = QString("_(2t x t)"); + screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); + + + /* + Screen Width = t * 2 + Screen Height = t * 2 + */ + screenWidth = tileSize * 2; + screenHeight = tileSize * 2; + name = QString("_(2t x 2t)"); + screenCameraPositions(name, zoom, tileSize, screenWidth, screenHeight); + } + + // Calculates the distance in mercator space of 2 x coordinates, assuming that 1 == 0 + double wrappedMercatorDistance(double x1, double x2) + { + return qMin(qMin(qAbs(x1 - 1.0 - x2), qAbs(x1 - x2)), qAbs(x1 + 1.0 - x2)); + } + + private slots: + void screenToMercatorPositions(){ + QFETCH(double, screenX); + QFETCH(double, screenY); + QFETCH(double, cameraCenterX); + QFETCH(double, cameraCenterY); + QFETCH(double, zoom); + QFETCH(int, tileSize); + QFETCH(int, screenWidth); + QFETCH(int, screenHeight); + QFETCH(double, mercatorX); + QFETCH(double, mercatorY); + + QGeoCameraData camera; + camera.setZoomLevel(zoom); + QGeoCoordinate centerCoordinate = QWebMercator::mercatorToCoord(QDoubleVector2D(cameraCenterX, cameraCenterY)); + camera.setCenter(centerCoordinate); + + QGeoCameraTiles ct; + ct.setTileSize(tileSize); + ct.setCameraData(camera); + ct.setScreenSize(QSize(screenWidth,screenHeight)); + + QGeoTiledMapScene mapGeometry; + mapGeometry.setTileSize(tileSize); + mapGeometry.setScreenSize(QSize(screenWidth,screenHeight)); + mapGeometry.setCameraData(camera); + mapGeometry.setVisibleTiles(ct.createTiles()); + + QGeoProjectionWebMercator projection; + projection.setViewportSize(QSize(screenWidth,screenHeight)); + projection.setCameraData(camera); + + QDoubleVector2D point(screenX,screenY); + QDoubleVector2D mercartorPos = projection.unwrapMapProjection(projection.itemPositionToWrappedMapProjection(point)); + + const double tolerance = 0.00000000001; // FuzzyCompare is too strict here + QVERIFY2(wrappedMercatorDistance(mercartorPos.x(), mercatorX) < tolerance, + qPrintable(QString("Accepted: %1 , Actual: %2") + .arg(QString::number(mercatorX)) + .arg(QString::number(mercartorPos.x())))); + QVERIFY2(qAbs(mercartorPos.y() - mercatorY) < tolerance, + qPrintable(QString("Accepted: %1 , Actual: %2") + .arg(QString::number(mercatorY)) + .arg(QString::number(mercartorPos.y())))); + } + + void screenToMercatorPositions_data() + { + populateScreenMercatorData(); + } + + void mercatorToScreenPositions(){ + QFETCH(double, screenX); + QFETCH(double, screenX2); + QFETCH(double, screenY); + QFETCH(double, cameraCenterX); + QFETCH(double, cameraCenterY); + QFETCH(double, zoom); + QFETCH(int, tileSize); + QFETCH(int, screenWidth); + QFETCH(int, screenHeight); + QFETCH(double, mercatorX); + QFETCH(double, mercatorY); + + QGeoCameraData camera; + camera.setZoomLevel(zoom); + QGeoCoordinate coord = QWebMercator::mercatorToCoord(QDoubleVector2D(cameraCenterX, cameraCenterY)); + camera.setCenter(coord); + + QGeoCameraTiles ct; + ct.setTileSize(tileSize); + ct.setCameraData(camera); + ct.setScreenSize(QSize(screenWidth,screenHeight)); + + QGeoTiledMapScene mapGeometry; + mapGeometry.setTileSize(tileSize); + mapGeometry.setScreenSize(QSize(screenWidth,screenHeight)); + mapGeometry.setCameraData(camera); + mapGeometry.setVisibleTiles(ct.createTiles()); + + QGeoProjectionWebMercator projection; + projection.setViewportSize(QSize(screenWidth,screenHeight)); + projection.setCameraData(camera); + + QDoubleVector2D mercatorPos(mercatorX, mercatorY); + QPointF point = projection.wrappedMapProjectionToItemPosition(projection.wrapMapProjection(mercatorPos)).toPointF(); + + QVERIFY2((point.x() == screenX) || (point.x() == screenX2), + qPrintable(QString("Accepted: { %1 , %2 } Actual: %3") + .arg(QString::number(screenX)) + .arg(QString::number(screenX2)) + .arg(QString::number(point.x())))); + QCOMPARE(point.y(), screenY); + } + + void mercatorToScreenPositions_data(){ + populateScreenMercatorData(); + } + +}; + +QTEST_GUILESS_MAIN(tst_QGeoTiledMapScene) +#include "tst_qgeotiledmapscene.moc" diff --git a/tests/auto/qgeotilespec/qgeotilespec.pro b/tests/auto/qgeotilespec/qgeotilespec.pro new file mode 100644 index 0000000..87cec15 --- /dev/null +++ b/tests/auto/qgeotilespec/qgeotilespec.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qgeotilespec + +INCLUDEPATH += ../../../src/location/maps + +SOURCES += tst_qgeotilespec.cpp + +QT += location-private testlib diff --git a/tests/auto/qgeotilespec/tst_qgeotilespec.cpp b/tests/auto/qgeotilespec/tst_qgeotilespec.cpp new file mode 100644 index 0000000..84df268 --- /dev/null +++ b/tests/auto/qgeotilespec/tst_qgeotilespec.cpp @@ -0,0 +1,319 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qgeotilespec_p.h" + +QT_USE_NAMESPACE + +class tst_QGeoTileSpec : public QObject +{ + Q_OBJECT + +public: + tst_QGeoTileSpec(); + +private: + void populateGeoTileSpecData(); + +private Q_SLOTS: + void constructorTest_data(); + void constructorTest(); + void pluginTest(); + void zoomTest(); + void xTest(); + void yTest(); + void mapIdTest(); + void assignsOperatorTest_data(); + void assignsOperatorTest(); + void equalsOperatorTest_data(); + void equalsOperatorTest(); + void lessThanOperatorTest_data(); + void lessThanOperatorTest(); + void qHashTest_data(); + void qHashTest(); +}; + +tst_QGeoTileSpec::tst_QGeoTileSpec() +{ +} + +void tst_QGeoTileSpec::populateGeoTileSpecData(){ + QTest::addColumn("plugin"); + QTest::addColumn("mapId"); + QTest::addColumn("zoom"); + QTest::addColumn("x"); + QTest::addColumn("y"); + QTest::newRow("zeros") << QString() << 0 << 0 << 0 << 0; + QTest::newRow("valid") << QString("geo plugin") << 455 << 1 << 20 << 50; + QTest::newRow("negative values") << QString("geo plugin negative") << -350 << 2 << -20 << -50; +} + +void tst_QGeoTileSpec::constructorTest_data() +{ + populateGeoTileSpecData(); +} + +void tst_QGeoTileSpec::constructorTest() +{ + QFETCH(QString,plugin); + QFETCH(int,zoom); + QFETCH(int,mapId); + QFETCH(int,x); + QFETCH(int,y); + + // test constructor copy with default values + QGeoTileSpec testObj; + QGeoTileSpec testObj2(testObj); + QCOMPARE(testObj.plugin(), testObj2.plugin()); + QCOMPARE(testObj.mapId(), testObj2.mapId()); + QCOMPARE(testObj.zoom(), testObj2.zoom()); + QCOMPARE(testObj.x(), testObj2.x()); + QCOMPARE(testObj.y(), testObj2.y()); + + // test second construct + QGeoTileSpec testObj3(plugin, mapId, zoom, x, y); + QCOMPARE(testObj3.plugin(), plugin); + QCOMPARE(testObj3.mapId(), mapId); + QCOMPARE(testObj3.zoom(), zoom); + QCOMPARE(testObj3.x(), x); + QCOMPARE(testObj3.y(), y); +} + +void tst_QGeoTileSpec::pluginTest() +{ + QGeoTileSpec tileSpec; + QCOMPARE(tileSpec.plugin(), QString()); + + QGeoTileSpec tileSpec2(QString("plugin test"),1,10,10,5); + QCOMPARE(tileSpec2.plugin(), QString("plugin test")); +} + +void tst_QGeoTileSpec::zoomTest() +{ + QGeoTileSpec tileSpec; + QVERIFY(tileSpec.zoom() == -1); + tileSpec.setZoom(1); + QVERIFY(tileSpec.zoom() == 1); + + QGeoTileSpec tileSpec2 = tileSpec; + QVERIFY(tileSpec2.zoom() == 1); + tileSpec.setZoom(2); + QVERIFY(tileSpec2.zoom() == 1); +} + +void tst_QGeoTileSpec::xTest() +{ + QGeoTileSpec tileSpec; + QVERIFY(tileSpec.x() == -1); + tileSpec.setX(10); + QVERIFY(tileSpec.x() == 10); + + QGeoTileSpec tileSpec2 = tileSpec; + QVERIFY(tileSpec2.x() == 10); + tileSpec.setX(30); + QVERIFY(tileSpec2.x() == 10); +} + +void tst_QGeoTileSpec::yTest() +{ + QGeoTileSpec tileSpec; + QVERIFY(tileSpec.y() == -1); + tileSpec.setY(20); + QVERIFY(tileSpec.y() == 20); + + QGeoTileSpec tileSpec2 = tileSpec; + QVERIFY(tileSpec2.y() == 20); + tileSpec.setY(40); + QVERIFY(tileSpec2.y() == 20); +} + +void tst_QGeoTileSpec::mapIdTest() +{ + QGeoTileSpec tileSpec; + QVERIFY(tileSpec.mapId() == 0); + tileSpec.setMapId(1); + QVERIFY(tileSpec.mapId() == 1); + + QGeoTileSpec tileSpec2 = tileSpec; + QVERIFY(tileSpec2.mapId() == 1); + tileSpec.setMapId(5); + QVERIFY(tileSpec2.mapId() == 1); +} + +void tst_QGeoTileSpec::assignsOperatorTest_data() +{ + populateGeoTileSpecData(); +} + + +void tst_QGeoTileSpec::assignsOperatorTest() +{ + QFETCH(QString,plugin); + QFETCH(int,mapId); + QFETCH(int,zoom); + QFETCH(int,x); + QFETCH(int,y); + + QGeoTileSpec testObj(plugin, mapId, zoom, x, y); + QGeoTileSpec testObj2; + testObj2 = testObj; + // test the correctness of the asignment operator + QVERIFY2(testObj2.plugin() == plugin, "Plugin not copied correctly"); + QVERIFY2(testObj2.zoom() == zoom, "Zoom not copied correctly"); + QVERIFY2(testObj2.mapId() == mapId, "Map Id not copied correctly"); + QVERIFY2(testObj2.x() == x, "X not copied correctly"); + QVERIFY2(testObj2.y() == y, "Y not copied correctly"); + // verify that values have not changed after an assignment + QVERIFY2(testObj.plugin() == testObj2.plugin(), "Plugin not copied correctly"); + QVERIFY2(testObj.zoom() == testObj2.zoom(), "Zoom not copied correctly"); + QVERIFY2(testObj.mapId() == testObj2.mapId(), "Map Id not copied correctly"); + QVERIFY2(testObj.x() == testObj2.x(), "X not copied correctly"); + QVERIFY2(testObj.y() == testObj2.y(), "Y not copied correctly"); +} + + +void tst_QGeoTileSpec::equalsOperatorTest_data() +{ + populateGeoTileSpecData(); +} + +void tst_QGeoTileSpec::equalsOperatorTest() +{ + QFETCH(QString,plugin); + QFETCH(int,mapId); + QFETCH(int,zoom); + QFETCH(int,x); + QFETCH(int,y); + + QGeoTileSpec testObj(plugin, mapId, zoom, x, y); + QGeoTileSpec testObj2(plugin, mapId, zoom, x, y); + QVERIFY2(testObj == testObj2, "Equals operator is not correct"); + + // test QGeoTileSpec pairs where they differ in one field + testObj2.setZoom(zoom+1); + QVERIFY2(!(testObj == testObj2), "Equals operator is not correct"); + testObj2 = testObj; + testObj2.setMapId(mapId+1); + QVERIFY2(!(testObj == testObj2), "Equals operator is not correct"); + testObj2 = testObj; + testObj2.setX(x+1); + QVERIFY2(!(testObj == testObj2), "Equals operator is not correct"); + testObj2 = testObj; + testObj2.setY(y+1); + QVERIFY2(!(testObj == testObj2), "Equals operator is not correct"); +} + +void tst_QGeoTileSpec::lessThanOperatorTest_data() +{ + populateGeoTileSpecData(); +} + +void tst_QGeoTileSpec::lessThanOperatorTest() +{ + QFETCH(QString,plugin); + QFETCH(int,mapId); + QFETCH(int,zoom); + QFETCH(int,x); + QFETCH(int,y); + + QGeoTileSpec testObj(plugin, mapId, zoom, x, y); + QGeoTileSpec testObj2(testObj); + QVERIFY(!(testObj < testObj2)); + + testObj2.setMapId(mapId-1); + QVERIFY2(testObj2 < testObj, "Less than operator is not correct for mapId"); + testObj2 = testObj; + testObj2.setZoom(zoom-1); + QVERIFY2(testObj2 < testObj, "Less than operator is not correct for zoom"); + testObj2 = testObj; + testObj2.setX(x-1); + QVERIFY2(testObj2 < testObj, "Less than operator is not correct for x"); + testObj2 = testObj; + testObj2.setY(y-1); + QVERIFY2(testObj2 < testObj, "Less than operator is not correct for y"); + + // less than comparisons are done in the order: plugin -> mapId -> zoom -> x -> y + // the test below checks if the order is correct + QGeoTileSpec testObj3(plugin + QString('a'), mapId-1, zoom-1, x-1, y-1); + QVERIFY2(testObj < testObj3, "Order of less than operator is not correct"); + QGeoTileSpec testObj4(plugin, mapId+1, zoom-1, x-1, y-1); + QVERIFY2(testObj < testObj4, "Order of less than operator is not correct"); + QGeoTileSpec testObj5(plugin, mapId, zoom+1, x-1, y-1); + QVERIFY2(testObj < testObj5, "Order of less than operator is not correct"); + QGeoTileSpec testObj6(plugin, mapId, zoom, x+1, y-1); + QVERIFY2(testObj < testObj6, "Order of less than operator is not correct"); + QGeoTileSpec testObj7(plugin, mapId, zoom, x, y+1); + QVERIFY2(testObj < testObj7, "Order of less than operator is not correct"); + + QGeoTileSpec testObj8(plugin, mapId-1, zoom+1, x+1, y+1); + QVERIFY2(testObj8 < testObj, "Order of less than operator is not correct"); + QGeoTileSpec testObj9(plugin, mapId, zoom-1, x+1, y+1); + QVERIFY2(testObj9 < testObj, "Order of less than operator is not correct"); + QGeoTileSpec testObj10(plugin, mapId, zoom, x-1, y+1); + QVERIFY2(testObj10 < testObj, "Order of less than operator is not correct"); +} + +void tst_QGeoTileSpec::qHashTest_data(){ + populateGeoTileSpecData(); +} + +void tst_QGeoTileSpec::qHashTest() +{ + QGeoTileSpec testObj; + unsigned int hash1 = qHash(testObj); + QGeoTileSpec testObj2; + testObj2 = testObj; + unsigned int hash2 = qHash(testObj2); + QCOMPARE(hash1, hash2); + + QFETCH(QString,plugin); + QFETCH(int,mapId); + QFETCH(int,zoom); + QFETCH(int,x); + QFETCH(int,y); + + QGeoTileSpec testObj3(plugin, mapId, zoom, x, y); + unsigned int hash3 = qHash(testObj3); + QVERIFY(hash1 != hash3); + + testObj2.setMapId(testObj3.mapId()+1); + testObj2.setZoom(testObj3.zoom()+1); + testObj2.setX(testObj3.x()*5); + testObj2.setY(testObj3.y()*10); + hash2 = qHash(testObj2); + QVERIFY(hash2 != hash3); +} + +QTEST_APPLESS_MAIN(tst_QGeoTileSpec) + +#include "tst_qgeotilespec.moc" + + diff --git a/tests/auto/qmlinterface/data/TestAddress.qml b/tests/auto/qmlinterface/data/TestAddress.qml new file mode 100644 index 0000000..8d646a6 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestAddress.qml @@ -0,0 +1,38 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtPositioning 5.5 + +Address { + city: "Brisbane" + country: "Australia" + countryCode: "AU" + postalCode: "4000" + state: "Queensland" + street: "123 Fake Street" +} diff --git a/tests/auto/qmlinterface/data/TestCategory.qml b/tests/auto/qmlinterface/data/TestCategory.qml new file mode 100644 index 0000000..d6ac2e4 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestCategory.qml @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtLocation 5.3 + +Category { + name: "Test category" + categoryId: "test-category-id" +} diff --git a/tests/auto/qmlinterface/data/TestContactDetail.qml b/tests/auto/qmlinterface/data/TestContactDetail.qml new file mode 100644 index 0000000..7d76ff2 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestContactDetail.qml @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtLocation 5.3 + +ContactDetail { + label: "Test Contact Detail" + value: "Test contact detail value" +} diff --git a/tests/auto/qmlinterface/data/TestIcon.qml b/tests/auto/qmlinterface/data/TestIcon.qml new file mode 100644 index 0000000..6df8077 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestIcon.qml @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.0 +import QtLocation 5.3 + +Icon { + Component.onCompleted: { + parameters.singleUrl = "http://www.example.com/test-icon.png" + } +} diff --git a/tests/auto/qmlinterface/data/TestLocation.qml b/tests/auto/qmlinterface/data/TestLocation.qml new file mode 100644 index 0000000..6c3bb27 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestLocation.qml @@ -0,0 +1,49 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtPositioning 5.5 + +Location { + address: TestAddress { } +// TODO:unsupported syntax for now +// boundingBox { +// center { +// longitude: 10.0 +// latitude: 20.0 +// altitude: 30.0 +// } +// height: 30.0 +// width: 40.0 +// } + boundingBox : QtPositioning.rectangle(QtPositioning.coordinate(20,10, 30),40.0,30) + coordinate { + longitude: 10.0 + latitude: 20.0 + altitude: 30.0 + } +} diff --git a/tests/auto/qmlinterface/data/TestPlace.qml b/tests/auto/qmlinterface/data/TestPlace.qml new file mode 100644 index 0000000..7b42bf3 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestPlace.qml @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtLocation 5.6 + +Place { + name: "Test Place" + placeId: "test-place-id" + attribution: "Place data by Foo" + categories: [ + Category { + name: "Test category 1" + categoryId: "test-category-id-1" + }, + Category { + name: "Test category 2" + categoryId: "test-category-id-2" + } + ] + location: TestLocation { } + ratings: TestRatings { } + icon: TestIcon { } + supplier: TestSupplier { } + visibility: Place.PrivateVisibility +} diff --git a/tests/auto/qmlinterface/data/TestPlaceAttribute.qml b/tests/auto/qmlinterface/data/TestPlaceAttribute.qml new file mode 100644 index 0000000..07cec51 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestPlaceAttribute.qml @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtLocation 5.3 + +PlaceAttribute { + label: "Test Attribute" + text: "Test attribute text" +} diff --git a/tests/auto/qmlinterface/data/TestRatings.qml b/tests/auto/qmlinterface/data/TestRatings.qml new file mode 100644 index 0000000..6e115fe --- /dev/null +++ b/tests/auto/qmlinterface/data/TestRatings.qml @@ -0,0 +1,35 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtLocation 5.3 + +Ratings { + average: 3.5 + maximum: 5.0 + count: 10 +} diff --git a/tests/auto/qmlinterface/data/TestSupplier.qml b/tests/auto/qmlinterface/data/TestSupplier.qml new file mode 100644 index 0000000..32b4b0f --- /dev/null +++ b/tests/auto/qmlinterface/data/TestSupplier.qml @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtLocation 5.3 + +Supplier { + name: "Test supplier" + supplierId: "test-supplier-id" + url: "http://www.example.com/test-supplier" + icon: TestIcon { } +} diff --git a/tests/auto/qmlinterface/data/TestUser.qml b/tests/auto/qmlinterface/data/TestUser.qml new file mode 100644 index 0000000..062d4b2 --- /dev/null +++ b/tests/auto/qmlinterface/data/TestUser.qml @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtLocation 5.3 + +User { + name: "Test User" + userId: "test-user-id" +} diff --git a/tests/auto/qmlinterface/qmlinterface.pro b/tests/auto/qmlinterface/qmlinterface.pro new file mode 100644 index 0000000..9f1acad --- /dev/null +++ b/tests/auto/qmlinterface/qmlinterface.pro @@ -0,0 +1,26 @@ +QT += location qml testlib + +#QT -= gui + +TARGET = tst_qmlinterface +CONFIG += testcase +CONFIG -= app_bundle + +TEMPLATE = app + + +SOURCES += tst_qmlinterface.cpp +DEFINES += SRCDIR=\\\"$$PWD/\\\" + +OTHER_FILES += \ + data/TestCategory.qml \ + data/TestAddress.qml \ + data/TestLocation.qml \ + data/TestPlace.qml \ + data/TestIcon.qml \ + data/TestRatings.qml \ + data/TestSupplier.qml \ + data/TestUser.qml \ + data/TestPlaceAttribute.qml \ + data/TestContactDetail.qml + diff --git a/tests/auto/qmlinterface/tst_qmlinterface.cpp b/tests/auto/qmlinterface/tst_qmlinterface.cpp new file mode 100644 index 0000000..11fc3c1 --- /dev/null +++ b/tests/auto/qmlinterface/tst_qmlinterface.cpp @@ -0,0 +1,353 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class tst_qmlinterface : public QObject +{ + Q_OBJECT + +public: + tst_qmlinterface(); + +private Q_SLOTS: + void testAddress(); + void testLocation(); + void testCategory(); + void testIcon(); + void testRatings(); + void testSupplier(); + void testUser(); + void testPlaceAttribute(); + void testContactDetail(); + void testPlace(); + +private: + QGeoCoordinate m_coordinate; + QGeoAddress m_address; + QGeoRectangle m_rectangle; + QGeoLocation m_location; + QPlaceCategory m_category; + QPlaceIcon m_icon; + QPlaceRatings m_ratings; + QPlaceSupplier m_supplier; + QPlaceUser m_user; + QPlaceAttribute m_placeAttribute; + QPlaceContactDetail m_contactDetail; + QList m_categories; + QPlace m_place; +}; + +tst_qmlinterface::tst_qmlinterface() +{ + m_coordinate.setLongitude(10.0); + m_coordinate.setLatitude(20.0); + m_coordinate.setAltitude(30.0); + + m_address.setCity(QStringLiteral("Brisbane")); + m_address.setCountry(QStringLiteral("Australia")); + m_address.setCountryCode(QStringLiteral("AU")); + m_address.setPostalCode(QStringLiteral("4000")); + m_address.setState(QStringLiteral("Queensland")); + m_address.setStreet(QStringLiteral("123 Fake Street")); + + m_rectangle.setCenter(m_coordinate); + m_rectangle.setHeight(30.0); + m_rectangle.setWidth(40.0); + + m_location.setAddress(m_address); + m_location.setBoundingBox(m_rectangle); + m_location.setCoordinate(m_coordinate); + + m_category.setName(QStringLiteral("Test category")); + m_category.setCategoryId(QStringLiteral("test-category-id")); + + QVariantMap iconParams; + iconParams.insert(QPlaceIcon::SingleUrl, QUrl(QStringLiteral("http://www.example.com/test-icon.png"))); + m_icon.setParameters(iconParams); + + m_ratings.setAverage(3.5); + m_ratings.setMaximum(5.0); + m_ratings.setCount(10); + + m_supplier.setName(QStringLiteral("Test supplier")); + m_supplier.setUrl(QUrl(QStringLiteral("http://www.example.com/test-supplier"))); + m_supplier.setSupplierId(QStringLiteral("test-supplier-id")); + m_supplier.setIcon(m_icon); + + m_user.setName(QStringLiteral("Test User")); + m_user.setUserId(QStringLiteral("test-user-id")); + + m_placeAttribute.setLabel(QStringLiteral("Test Attribute")); + m_placeAttribute.setText(QStringLiteral("Test attribute text")); + + m_contactDetail.setLabel(QStringLiteral("Test Contact Detail")); + m_contactDetail.setValue(QStringLiteral("Test contact detail value")); + + QPlaceCategory category; + category.setName(QStringLiteral("Test category 1")); + category.setCategoryId(QStringLiteral("test-category-id-1")); + m_categories.append(category); + category.setName(QStringLiteral("Test category 2")); + category.setCategoryId(QStringLiteral("test-category-id-2")); + m_categories.append(category); + + m_place.setName(QStringLiteral("Test Place")); + m_place.setPlaceId(QStringLiteral("test-place-id")); + m_place.setAttribution(QStringLiteral("Place data by Foo")); + m_place.setCategories(m_categories); + m_place.setLocation(m_location); + m_place.setRatings(m_ratings); + m_place.setIcon(m_icon); + m_place.setSupplier(m_supplier); + m_place.setVisibility(QLocation::PrivateVisibility); +} + +void tst_qmlinterface::testAddress() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestAddress.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QGeoAddress address = qmlObject->property("address").value(); + + QCOMPARE(address, m_address); + + qmlObject->setProperty("address", QVariant::fromValue(QGeoAddress())); + + QVERIFY(qmlObject->property("city").toString().isEmpty()); + QVERIFY(qmlObject->property("country").toString().isEmpty()); + QVERIFY(qmlObject->property("countryCode").toString().isEmpty()); + QVERIFY(qmlObject->property("postalCode").toString().isEmpty()); + QVERIFY(qmlObject->property("state").toString().isEmpty()); + QVERIFY(qmlObject->property("street").toString().isEmpty()); + + delete qmlObject; +} + +void tst_qmlinterface::testLocation() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestLocation.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QGeoLocation location = qmlObject->property("location").value(); + + QCOMPARE(location, m_location); + + qmlObject->setProperty("location", QVariant::fromValue(QGeoLocation())); + + QCOMPARE(qmlObject->property("address").value(), QGeoAddress()); + QCOMPARE(qmlObject->property("boundingBox").value(), QGeoRectangle()); + QCOMPARE(qmlObject->property("coordinate").value(), QGeoCoordinate()); + + delete qmlObject; +} + +void tst_qmlinterface::testCategory() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestCategory.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlaceCategory category = qmlObject->property("category").value(); + + QCOMPARE(category, m_category); + + qmlObject->setProperty("category", QVariant::fromValue(QPlaceCategory())); + + QVERIFY(qmlObject->property("name").toString().isEmpty()); + QVERIFY(qmlObject->property("categoryId").toString().isEmpty()); + + delete qmlObject; +} + +void tst_qmlinterface::testIcon() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestIcon.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlaceIcon icon = qmlObject->property("icon").value(); + + QCOMPARE(icon, m_icon); + + qmlObject->setProperty("icon", QVariant::fromValue(QPlaceIcon())); + + QVERIFY(!qmlObject->property("fullUrl").toUrl().isValid()); + + delete qmlObject; +} + +void tst_qmlinterface::testRatings() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestRatings.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlaceRatings ratings = qmlObject->property("ratings").value(); + + QCOMPARE(ratings, m_ratings); + + qmlObject->setProperty("ratings", QVariant::fromValue(QPlaceRatings())); + + QCOMPARE(qmlObject->property("average").value(), 0.0); + QCOMPARE(qmlObject->property("maximum").value(), 0.0); + QCOMPARE(qmlObject->property("average").toInt(), 0); + + delete qmlObject; +} + +void tst_qmlinterface::testSupplier() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestSupplier.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlaceSupplier supplier = qmlObject->property("supplier").value(); + + QCOMPARE(supplier, m_supplier); + + qmlObject->setProperty("supplier", QVariant::fromValue(QPlaceSupplier())); + + QVERIFY(qmlObject->property("name").toString().isEmpty()); + QVERIFY(!qmlObject->property("url").toUrl().isValid()); + QVERIFY(qmlObject->property("supplierId").toString().isEmpty()); + QCOMPARE(qmlObject->property("icon").value(), QPlaceIcon()); + + delete qmlObject; +} + +void tst_qmlinterface::testUser() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestUser.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlaceUser user = qmlObject->property("user").value(); + + QCOMPARE(user, m_user); + + qmlObject->setProperty("user", QVariant::fromValue(QPlaceUser())); + + QVERIFY(qmlObject->property("name").toString().isEmpty()); + QVERIFY(qmlObject->property("userId").toString().isEmpty()); + + delete qmlObject; +} + +void tst_qmlinterface::testPlaceAttribute() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestPlaceAttribute.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlaceAttribute placeAttribute = qmlObject->property("attribute").value(); + + QCOMPARE(placeAttribute, m_placeAttribute); + + qmlObject->setProperty("attribute", QVariant::fromValue(QPlaceAttribute())); + + QVERIFY(qmlObject->property("label").toString().isEmpty()); + QVERIFY(qmlObject->property("text").toString().isEmpty()); + + delete qmlObject; +} + +void tst_qmlinterface::testContactDetail() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestContactDetail.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlaceContactDetail contactDetail = qmlObject->property("contactDetail").value(); + + QCOMPARE(contactDetail, m_contactDetail); + + qmlObject->setProperty("contactDetail", QVariant::fromValue(QPlaceContactDetail())); + + QVERIFY(qmlObject->property("label").toString().isEmpty()); + QVERIFY(qmlObject->property("value").toString().isEmpty()); + + delete qmlObject; +} + +void tst_qmlinterface::testPlace() +{ + QQmlEngine engine; + QQmlComponent component(&engine, SRCDIR "data/TestPlace.qml"); + QVERIFY2(component.isReady(), qPrintable(component.errorString())); + QObject *qmlObject = component.create(); + + QPlace place = qmlObject->property("place").value(); + + QCOMPARE(place, m_place); + + qmlObject->setProperty("place", QVariant::fromValue(QPlace())); + + QVERIFY(qmlObject->property("name").toString().isEmpty()); + QVERIFY(qmlObject->property("placeId").toString().isEmpty()); + QVERIFY(qmlObject->property("attribution").toString().isEmpty()); + QQmlListReference categories(qmlObject, "categories", &engine); + QCOMPARE(categories.count(), 0); + QCOMPARE(qmlObject->property("location").value(), QGeoLocation()); + QCOMPARE(qmlObject->property("ratings").value(), QPlaceRatings()); + QCOMPARE(qmlObject->property("icon").value(), QPlaceIcon()); + QCOMPARE(qmlObject->property("supplier").value(), QPlaceSupplier()); + + delete qmlObject; +} + +QTEST_MAIN(tst_qmlinterface) + +#include "tst_qmlinterface.moc" diff --git a/tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/dummynmeapositioninfosource.pro b/tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/dummynmeapositioninfosource.pro new file mode 100644 index 0000000..261992a --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/dummynmeapositioninfosource.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +CONFIG+=testcase +QT += network positioning testlib +TARGET = tst_dummynmeapositioninfosource + +INCLUDEPATH += .. + +HEADERS += ../../utils/qlocationtestutils_p.h \ + ../../qgeopositioninfosource/testqgeopositioninfosource_p.h \ + ../qnmeapositioninfosourceproxyfactory.h + +SOURCES += ../../utils/qlocationtestutils.cpp \ + ../../qgeopositioninfosource/testqgeopositioninfosource.cpp \ + ../qnmeapositioninfosourceproxyfactory.cpp \ + tst_dummynmeapositioninfosource.cpp + +# This test relies on a working local QTcpSocket(Server). When the CI is under +# heavy load the socket code cannot establish a connection which leads to flaky +# test results. We make this test insiginficant as there is currently no known +# solution to this problem. On the positive side QNmeaPositionInfoSource +# does not have a platform specific implementation. Other platforms should provide +# a close enough test approximation. +win32:CONFIG+=insignificant_test diff --git a/tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/tst_dummynmeapositioninfosource.cpp b/tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/tst_dummynmeapositioninfosource.cpp new file mode 100644 index 0000000..e07fb70 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/dummynmeapositioninfosource/tst_dummynmeapositioninfosource.cpp @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "qnmeapositioninfosourceproxyfactory.h" +#include "../qgeopositioninfosource/testqgeopositioninfosource_p.h" +#include "../utils/qlocationtestutils_p.h" + +#include +#include +#include + +Q_DECLARE_METATYPE(QNmeaPositionInfoSource::UpdateMode) + +class DummyNmeaPositionInfoSource : public QNmeaPositionInfoSource +{ + Q_OBJECT + +public: + DummyNmeaPositionInfoSource(QNmeaPositionInfoSource::UpdateMode mode, QObject *parent = 0); + +protected: + virtual bool parsePosInfoFromNmeaData(const char *data, + int size, + QGeoPositionInfo *posInfo, + bool *hasFix); + +private: + int callCount; +}; + +DummyNmeaPositionInfoSource::DummyNmeaPositionInfoSource(QNmeaPositionInfoSource::UpdateMode mode, QObject *parent) : + QNmeaPositionInfoSource(mode, parent), + callCount(0) +{ +} + +bool DummyNmeaPositionInfoSource::parsePosInfoFromNmeaData(const char* data, + int size, + QGeoPositionInfo *posInfo, + bool *hasFix) +{ + Q_UNUSED(data); + Q_UNUSED(size); + + posInfo->setCoordinate(QGeoCoordinate(callCount * 1.0, callCount * 1.0, callCount * 1.0)); + posInfo->setTimestamp(QDateTime::currentDateTime().toUTC()); + *hasFix = true; + ++callCount; + + return true; +} + +class tst_DummyNmeaPositionInfoSource : public QObject +{ + Q_OBJECT + +public: + tst_DummyNmeaPositionInfoSource(); + +private slots: + void initTestCase(); + void testOverloadedParseFunction(); +}; + + +tst_DummyNmeaPositionInfoSource::tst_DummyNmeaPositionInfoSource() {} + +void tst_DummyNmeaPositionInfoSource::initTestCase() +{ + +} + +void tst_DummyNmeaPositionInfoSource::testOverloadedParseFunction() +{ + DummyNmeaPositionInfoSource source(QNmeaPositionInfoSource::RealTimeMode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + + QGeoPositionInfo pos; + + proxy->source()->startUpdates(); + + proxy->feedBytes(QString("The parser converts\n").toLatin1()); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 1), 10000); + pos = spy.at(0).at(0).value(); + + QVERIFY((pos.coordinate().latitude() == 0.0) + && (pos.coordinate().longitude() == 0.0) + && (pos.coordinate().altitude() == 0.0)); + + spy.clear(); + + proxy->feedBytes(QString("any data it receives\n").toLatin1()); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 1), 10000); + pos = spy.at(0).at(0).value(); + + QVERIFY((pos.coordinate().latitude() == 1.0) + && (pos.coordinate().longitude() == 1.0) + && (pos.coordinate().altitude() == 1.0)); + + spy.clear(); + + proxy->feedBytes(QString("into positions\n").toLatin1()); + + QTRY_VERIFY_WITH_TIMEOUT((spy.count() == 1), 10000); + pos = spy.at(0).at(0).value(); + + QVERIFY((pos.coordinate().latitude() == 2.0) + && (pos.coordinate().longitude() == 2.0) + && (pos.coordinate().altitude() == 2.0)); + + spy.clear(); +} + +#include "tst_dummynmeapositioninfosource.moc" + +QTEST_GUILESS_MAIN(tst_DummyNmeaPositionInfoSource); diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource.pro b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource.pro new file mode 100644 index 0000000..8c168d5 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource.pro @@ -0,0 +1,8 @@ +TEMPLATE = subdirs +SUBDIRS += \ + dummynmeapositioninfosource \ + qnmeapositioninfosource_realtime \ + qnmeapositioninfosource_simulation \ + qnmeapositioninfosource_realtime_generic \ + qnmeapositioninfosource_simulation_generic + diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/qnmeapositioninfosource_realtime.pro b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/qnmeapositioninfosource_realtime.pro new file mode 100644 index 0000000..2b93cc2 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/qnmeapositioninfosource_realtime.pro @@ -0,0 +1,25 @@ +TEMPLATE = app +CONFIG+=testcase +QT += network positioning testlib +TARGET = tst_qnmeapositioninfosource_realtime + +INCLUDEPATH += .. + +HEADERS += ../../utils/qlocationtestutils_p.h \ + ../../qgeopositioninfosource/testqgeopositioninfosource_p.h \ + ../qnmeapositioninfosourceproxyfactory.h \ + ../tst_qnmeapositioninfosource.h + +SOURCES += ../../utils/qlocationtestutils.cpp \ + ../../qgeopositioninfosource/testqgeopositioninfosource.cpp \ + ../qnmeapositioninfosourceproxyfactory.cpp \ + ../tst_qnmeapositioninfosource.cpp \ + tst_qnmeapositioninfosource_realtime.cpp + +# This test relies on a working local QTcpSocket(Server). When the CI is under +# heavy load the socket code cannot establish a connection which leads to flaky +# test results. We make this test insiginficant as there is currently no known +# solution to this problem. On the positive side QNmeaPositionInfoSource +# does not have a platform specific implementation. Other platforms should provide +# a close enough test approximation. +win32:CONFIG+=insignificant_test diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/tst_qnmeapositioninfosource_realtime.cpp b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/tst_qnmeapositioninfosource_realtime.cpp new file mode 100644 index 0000000..423f657 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime/tst_qnmeapositioninfosource_realtime.cpp @@ -0,0 +1,44 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "tst_qnmeapositioninfosource.h" + +class tst_QNmeaPositionInfoSource_RealTime : public tst_QNmeaPositionInfoSource +{ + Q_OBJECT + +public: + tst_QNmeaPositionInfoSource_RealTime() + : tst_QNmeaPositionInfoSource(QNmeaPositionInfoSource::RealTimeMode) {} +}; + +#include "tst_qnmeapositioninfosource_realtime.moc" + +QTEST_GUILESS_MAIN(tst_QNmeaPositionInfoSource_RealTime); diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/qnmeapositioninfosource_realtime_generic.pro b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/qnmeapositioninfosource_realtime_generic.pro new file mode 100644 index 0000000..068289b --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/qnmeapositioninfosource_realtime_generic.pro @@ -0,0 +1,28 @@ +TEMPLATE = app +CONFIG+=testcase +testcase.timeout = 400 # this test is slow +QT += network positioning testlib +TARGET = tst_qnmeapositioninfosource_realtime_generic + +INCLUDEPATH += .. + +HEADERS += ../../utils/qlocationtestutils_p.h \ + ../../qgeopositioninfosource/testqgeopositioninfosource_p.h \ + ../qnmeapositioninfosourceproxyfactory.h \ + ../tst_qnmeapositioninfosource.h + +SOURCES += ../../utils/qlocationtestutils.cpp \ + ../../qgeopositioninfosource/testqgeopositioninfosource.cpp \ + ../qnmeapositioninfosourceproxyfactory.cpp \ + ../tst_qnmeapositioninfosource.cpp \ + tst_qnmeapositioninfosource_realtime_generic.cpp + +CONFIG -= app_bundle + +# This test relies on a working local QTcpSocket(Server). When the CI is under +# heavy load the socket code cannot establish a connection which leads to flaky +# test results. We make this test insiginficant as there is currently no known +# solution to this problem. On the positive side QNmeaPositionInfoSource +# does not have a platform specific implementation. Other platforms should provide +# a close enough test approximation. +win32:CONFIG+=insignificant_test diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/tst_qnmeapositioninfosource_realtime_generic.cpp b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/tst_qnmeapositioninfosource_realtime_generic.cpp new file mode 100644 index 0000000..d3f03c0 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_realtime_generic/tst_qnmeapositioninfosource_realtime_generic.cpp @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "tst_qnmeapositioninfosource.h" + +class tst_QNmeaPositionInfoSource_RealTime_Generic : public TestQGeoPositionInfoSource +{ + Q_OBJECT + +public: + tst_QNmeaPositionInfoSource_RealTime_Generic() + { + m_factory = new QNmeaPositionInfoSourceProxyFactory; +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#endif +#endif + } + + ~tst_QNmeaPositionInfoSource_RealTime_Generic() + { + delete m_factory; + } + +protected: + QGeoPositionInfoSource *createTestSource() + { + QNmeaPositionInfoSource *source = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::RealTimeMode); + QNmeaPositionInfoSourceProxy *proxy = static_cast(m_factory->createProxy(source)); + Feeder *feeder = new Feeder(source); + feeder->start(proxy); + return source; + } + +private: + QNmeaPositionInfoSourceProxyFactory *m_factory; +}; + +#include "tst_qnmeapositioninfosource_realtime_generic.moc" + +QTEST_GUILESS_MAIN(tst_QNmeaPositionInfoSource_RealTime_Generic); diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/qnmeapositioninfosource_simulation.pro b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/qnmeapositioninfosource_simulation.pro new file mode 100644 index 0000000..1214d7e --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/qnmeapositioninfosource_simulation.pro @@ -0,0 +1,25 @@ +TEMPLATE = app +CONFIG+=testcase +QT += network positioning testlib +TARGET = tst_qnmeapositioninfosource_simulation + +INCLUDEPATH += .. + +HEADERS += ../../utils/qlocationtestutils_p.h \ + ../../qgeopositioninfosource/testqgeopositioninfosource_p.h \ + ../qnmeapositioninfosourceproxyfactory.h \ + ../tst_qnmeapositioninfosource.h + +SOURCES += ../../utils/qlocationtestutils.cpp \ + ../../qgeopositioninfosource/testqgeopositioninfosource.cpp \ + ../qnmeapositioninfosourceproxyfactory.cpp \ + ../tst_qnmeapositioninfosource.cpp \ + tst_qnmeapositioninfosource_simulation.cpp + +# This test relies on a working local QTcpSocket(Server). When the CI is under +# heavy load the socket code cannot establish a connection which leads to flaky +# test results. We make this test insiginficant as there is currently no known +# solution to this problem. On the positive side QNmeaPositionInfoSource +# does not have a platform specific implementation. Other platforms should provide +# a close enough test approximation. +win32:CONFIG+=insignificant_test diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/tst_qnmeapositioninfosource_simulation.cpp b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/tst_qnmeapositioninfosource_simulation.cpp new file mode 100644 index 0000000..ffe5e1d --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation/tst_qnmeapositioninfosource_simulation.cpp @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "tst_qnmeapositioninfosource.h" + +class tst_QNmeaPositionInfoSource_Simulation : public tst_QNmeaPositionInfoSource +{ + Q_OBJECT +public: + tst_QNmeaPositionInfoSource_Simulation() + : tst_QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode) {} +}; + +#include "tst_qnmeapositioninfosource_simulation.moc" + +QTEST_GUILESS_MAIN(tst_QNmeaPositionInfoSource_Simulation); diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/qnmeapositioninfosource_simulation_generic.pro b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/qnmeapositioninfosource_simulation_generic.pro new file mode 100644 index 0000000..726a746 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/qnmeapositioninfosource_simulation_generic.pro @@ -0,0 +1,28 @@ +TEMPLATE = app +CONFIG+=testcase +testcase.timeout = 400 # this test is slow +QT += network positioning testlib +TARGET = tst_qnmeapositioninfosource_simulation_generic + +INCLUDEPATH += .. + +HEADERS += ../../utils/qlocationtestutils_p.h \ + ../../qgeopositioninfosource/testqgeopositioninfosource_p.h \ + ../qnmeapositioninfosourceproxyfactory.h \ + ../tst_qnmeapositioninfosource.h + +SOURCES += ../../utils/qlocationtestutils.cpp \ + ../../qgeopositioninfosource/testqgeopositioninfosource.cpp \ + ../qnmeapositioninfosourceproxyfactory.cpp \ + ../tst_qnmeapositioninfosource.cpp \ + tst_qnmeapositioninfosource_simulation_generic.cpp + +CONFIG -= app_bundle + +# This test relies on a working local QTcpSocket(Server). When the CI is under +# heavy load the socket code cannot establish a connection which leads to flaky +# test results. We make this test insiginficant as there is currently no known +# solution to this problem. On the positive side QNmeaPositionInfoSource +# does not have a platform specific implementation. Other platforms should provide +# a close enough test approximation. +win32:CONFIG+=insignificant_test diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/tst_qnmeapositioninfosource_simulation_generic.cpp b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/tst_qnmeapositioninfosource_simulation_generic.cpp new file mode 100644 index 0000000..adc55f6 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosource_simulation_generic/tst_qnmeapositioninfosource_simulation_generic.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "tst_qnmeapositioninfosource.h" + +class tst_QNmeaPositionInfoSource_Simulation_Generic : public TestQGeoPositionInfoSource +{ + Q_OBJECT +public: + tst_QNmeaPositionInfoSource_Simulation_Generic() + { +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#endif +#endif + } + +protected: + QGeoPositionInfoSource *createTestSource() + { + QNmeaPositionInfoSource *source = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode); + source->setDevice(new UnlimitedNmeaStream(source)); + return source; + } +}; + +#include "tst_qnmeapositioninfosource_simulation_generic.moc" + +QTEST_GUILESS_MAIN(tst_QNmeaPositionInfoSource_Simulation_Generic); diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.cpp b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.cpp new file mode 100644 index 0000000..756d027 --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnmeapositioninfosourceproxyfactory.h" +#include "../utils/qlocationtestutils_p.h" + +#include +#include +#include + + +QNmeaPositionInfoSourceProxy::QNmeaPositionInfoSourceProxy(QNmeaPositionInfoSource *source, QIODevice *outDevice) + : m_source(source), + m_outDevice(outDevice) +{ +} + +QNmeaPositionInfoSourceProxy::~QNmeaPositionInfoSourceProxy() +{ + m_outDevice->close(); + delete m_outDevice; +} + +QGeoPositionInfoSource *QNmeaPositionInfoSourceProxy::source() const +{ + return m_source; +} + +void QNmeaPositionInfoSourceProxy::feedUpdate(const QDateTime &dt) +{ + m_outDevice->write(QLocationTestUtils::createRmcSentence(dt).toLatin1()); +} + +void QNmeaPositionInfoSourceProxy::feedBytes(const QByteArray &bytes) +{ + m_outDevice->write(bytes); +} + + +QNmeaPositionInfoSourceProxyFactory::QNmeaPositionInfoSourceProxyFactory() + : m_server(new QTcpServer(this)) +{ + bool b = m_server->listen(QHostAddress::LocalHost); + Q_ASSERT(b); +} + +QNmeaPositionInfoSourceProxy *QNmeaPositionInfoSourceProxyFactory::createProxy(QNmeaPositionInfoSource *source) +{ + QTcpSocket *client = new QTcpSocket; + client->connectToHost(m_server->serverAddress(), m_server->serverPort()); + qDebug() << "listening on" << m_server->serverAddress() << m_server->serverPort(); + bool b = m_server->waitForNewConnection(15000); + if (!b) + qWarning() << "Server didin't receive new connection"; + b = client->waitForConnected(); + if (!b) + qWarning() << "Client could not connect to server"; + + //QNmeaPositionInfoSource *source = new QNmeaPositionInfoSource(m_mode); + QIODevice *device = m_server->nextPendingConnection(); + if (!device) + qWarning() << "Missing pending connection. Test is going to fail."; + else + qWarning() << "Received pending connection:" << device << b; + source->setDevice(device); + Q_ASSERT(source->device() != 0); + QNmeaPositionInfoSourceProxy *proxy = new QNmeaPositionInfoSourceProxy(source, client); + proxy->setParent(source); + return proxy; +} diff --git a/tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.h b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.h new file mode 100644 index 0000000..d740f9b --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/qnmeapositioninfosourceproxyfactory.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QNMEAPOSITIONINFOSOURCEPROXYFACTORY_H +#define QNMEAPOSITIONINFOSOURCEPROXYFACTORY_H + +#include + +#include + +QT_BEGIN_NAMESPACE +class QTcpServer; +class QIODevice; +class QNmeaPositionInfoSource; +QT_END_NAMESPACE + +class QNmeaPositionInfoSourceProxy : public QObject +{ + Q_OBJECT +public: + QNmeaPositionInfoSourceProxy(QNmeaPositionInfoSource *source, QIODevice *outDevice); + ~QNmeaPositionInfoSourceProxy(); + + QGeoPositionInfoSource *source() const; + + void feedUpdate(const QDateTime &dt); + + void feedBytes(const QByteArray &bytes); + + int updateIntervalErrorMargin() const { return 50; } + +private: + QNmeaPositionInfoSource *m_source; + QIODevice *m_outDevice; +}; + +class QNmeaPositionInfoSourceProxyFactory : public QObject +{ + Q_OBJECT +public: + QNmeaPositionInfoSourceProxyFactory(); + + // proxy is created as child of source + QNmeaPositionInfoSourceProxy *createProxy(QNmeaPositionInfoSource *source); + +private: + QTcpServer *m_server; +}; + +#endif diff --git a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp new file mode 100644 index 0000000..37fe7ab --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.cpp @@ -0,0 +1,580 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Jolla Ltd. +** Contact: Aaron McCarthy +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +//TESTED_COMPONENT=src/location + +#include "tst_qnmeapositioninfosource.h" + +#include + +#ifdef Q_OS_WIN + +// Windows seems to require longer timeouts and step length +// We override the standard QTestCase related macros + +#ifdef QTRY_COMPARE_WITH_TIMEOUT +#undef QTRY_COMPARE_WITH_TIMEOUT +#endif +#define QTRY_COMPARE_WITH_TIMEOUT(__expr, __expected, __timeout) \ +do { \ + const int __step = 100; \ + const int __timeoutValue = __timeout; \ + if ((__expr) != (__expected)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeoutValue && ((__expr) != (__expected)); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + QCOMPARE(__expr, __expected); \ +} while (0) + +#ifdef QTRY_COMPARE +#undef QTRY_COMPARE +#endif +#define QTRY_COMPARE(__expr, __expected) QTRY_COMPARE_WITH_TIMEOUT(__expr, __expected, 10000) + +#endif + +tst_QNmeaPositionInfoSource::tst_QNmeaPositionInfoSource(QNmeaPositionInfoSource::UpdateMode mode, QObject *parent) + : QObject(parent), + m_mode(mode) +{ +} + +void tst_QNmeaPositionInfoSource::initTestCase() +{ + qRegisterMetaType(); +} + +void tst_QNmeaPositionInfoSource::constructor() +{ + QObject o; + QNmeaPositionInfoSource source(m_mode, &o); + QCOMPARE(source.updateMode(), m_mode); + QCOMPARE(source.parent(), &o); +} + +void tst_QNmeaPositionInfoSource::supportedPositioningMethods() +{ + QNmeaPositionInfoSource source(m_mode); + QCOMPARE(source.supportedPositioningMethods(), QNmeaPositionInfoSource::SatellitePositioningMethods); +} + +void tst_QNmeaPositionInfoSource::minimumUpdateInterval() +{ + QNmeaPositionInfoSource source(m_mode); + QCOMPARE(source.minimumUpdateInterval(), 2); +} + +void tst_QNmeaPositionInfoSource::userEquivalentRangeError() +{ + QNmeaPositionInfoSource source(m_mode); + QVERIFY(qIsNaN(source.userEquivalentRangeError())); + source.setUserEquivalentRangeError(5.1); + QVERIFY(qFuzzyCompare(source.userEquivalentRangeError(), 5.1)); +} + +void tst_QNmeaPositionInfoSource::setUpdateInterval_delayedUpdate() +{ + // If an update interval is set, and an update is not available at a + // particular interval, the source should emit the next update as soon + // as it becomes available + + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + proxy->source()->setUpdateInterval(500); + proxy->source()->startUpdates(); + + QTest::qWait(600); + QDateTime now = QDateTime::currentDateTime(); + proxy->feedUpdate(now); + QTRY_COMPARE(spyUpdate.count(), 1); + + // should have gotten the update immediately, and not have needed to + // wait until the next interval + QVERIFY(now.time().msecsTo(QDateTime::currentDateTime().time()) < 200); +} + +void tst_QNmeaPositionInfoSource::lastKnownPosition() +{ + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QCOMPARE(proxy->source()->lastKnownPosition(), QGeoPositionInfo()); + + // source may need requestUpdate() or startUpdates() to be called to + // trigger reading of data channel + QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout())); + proxy->source()->requestUpdate(proxy->source()->minimumUpdateInterval()); + QTRY_COMPARE(spyTimeout.count(), 1); + + // If an update is received and startUpdates() or requestUpdate() hasn't + // been called, it should still be available through lastKnownPosition() + QDateTime dt = QDateTime::currentDateTime().toUTC(); + proxy->feedUpdate(dt); + QTRY_COMPARE(proxy->source()->lastKnownPosition().timestamp(), dt); + + QList dateTimes = createDateTimes(5); + for (int i=0; isource()->requestUpdate(); // Irrelevant for this test + proxy->feedUpdate(dateTimes[i]); + QTRY_COMPARE(proxy->source()->lastKnownPosition().timestamp(), dateTimes[i]); + } + + proxy->source()->startUpdates(); + // if dateTimes are older than before, they will be ignored. + dateTimes = createDateTimes(dateTimes.last().addMSecs(100), 5); + for (int i=0; ifeedUpdate(dateTimes[i]); + QTRY_COMPARE(proxy->source()->lastKnownPosition().timestamp(), dateTimes[i]); + } +} + +void tst_QNmeaPositionInfoSource::beginWithBufferedData() +{ + // In SimulationMode, data stored in the QIODevice is read when + // startUpdates() or requestUpdate() is called. + // In RealTimeMode, all existing data in the QIODevice is ignored - + // only new data will be read. + + QFETCH(QList, dateTimes); + QFETCH(UpdateTriggerMethod, trigger); + + QByteArray bytes; + for (int i=0; i().timestamp(), dateTimes[i]); + } else if (trigger == RequestUpdatesMethod) { + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(spy.at(0).at(0).value().timestamp(), dateTimes.first()); + } + } +} + +void tst_QNmeaPositionInfoSource::beginWithBufferedData_data() +{ + QTest::addColumn >("dateTimes"); + QTest::addColumn("trigger"); + + QList dateTimes; + dateTimes << QDateTime::currentDateTime().toUTC(); + + QTest::newRow("startUpdates(), 1 update in buffer") << dateTimes << StartUpdatesMethod; + QTest::newRow("requestUpdate(), 1 update in buffer") << dateTimes << RequestUpdatesMethod; + + for (int i=1; i<3; i++) + dateTimes << dateTimes[0].addMSecs(i * 100); + QTest::newRow("startUpdates(), multiple updates in buffer") << dateTimes << StartUpdatesMethod; + QTest::newRow("requestUpdate(), multiple updates in buffer") << dateTimes << RequestUpdatesMethod; +} + +void tst_QNmeaPositionInfoSource::startUpdates() +{ + QFETCH(QList, dateTimes); + + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + proxy->source()->startUpdates(); + + for (int i=0; ifeedUpdate(dateTimes[i]); + QTRY_COMPARE(spyUpdate.count(), dateTimes.count()); +} + +void tst_QNmeaPositionInfoSource::startUpdates_data() +{ + QTest::addColumn >("dateTimes"); + + QTest::newRow("1 update") << createDateTimes(1); + QTest::newRow("2 updates") << createDateTimes(2); + QTest::newRow("10 updates") << createDateTimes(10); +} + +void tst_QNmeaPositionInfoSource::startUpdates_withTimeout() +{ + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout())); + + proxy->source()->setUpdateInterval(1000); + proxy->source()->startUpdates(); + + QDateTime dt = QDateTime::currentDateTime().toUTC(); + + if (m_mode == QNmeaPositionInfoSource::SimulationMode) { + // the first sentence primes the simulation + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt).toLatin1()); + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addMSecs(10)).toLatin1()); + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addMSecs(1100)).toLatin1()); + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addMSecs(2200)).toLatin1()); + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(9)).toLatin1()); + + QTime t; + t.start(); + + for (int j = 1; j < 4; ++j) { + QTRY_COMPARE(spyUpdate.count(), j); + QCOMPARE(spyTimeout.count(), 0); + int time = t.elapsed(); + QVERIFY((time > j*1000 - 300) && (time < j*1000 + 300)); + } + + spyUpdate.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() == 0) && (spyTimeout.count() == 1), 7500); + spyTimeout.clear(); + + QTRY_VERIFY_WITH_TIMEOUT((spyUpdate.count() == 1) && (spyTimeout.count() == 0), 7500); + + } else { + // dt + 900 + QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 0); + + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(1)).toLatin1()); + // dt + 1200 + QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0); + spyUpdate.clear(); + + // dt + 1900 + QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 0); + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(2)).toLatin1()); + + // dt + 2200 + QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0); + spyUpdate.clear(); + + // dt + 2900 + QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 0); + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(3)).toLatin1()); + + // dt + 3200 + QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0); + spyUpdate.clear(); + + // dt + 6900 + QTRY_VERIFY(spyUpdate.count() == 0 && spyTimeout.count() == 1); + spyTimeout.clear(); + proxy->feedBytes(QLocationTestUtils::createRmcSentence(dt.addSecs(7)).toLatin1()); + + // dt + 7200 + QTRY_VERIFY(spyUpdate.count() == 1 && spyTimeout.count() == 0); + spyUpdate.clear(); + } +} + +void tst_QNmeaPositionInfoSource::startUpdates_expectLatestUpdateOnly() +{ + // If startUpdates() is called and an interval has been set, if multiple' + // updates are in the buffer, only the latest update should be emitted + + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + proxy->source()->setUpdateInterval(500); + proxy->source()->startUpdates(); + + QList dateTimes = createDateTimes(3); + for (int i=0; ifeedUpdate(dateTimes[i]); + + QTRY_COMPARE(spyUpdate.count(), 1); + QCOMPARE(spyUpdate[0][0].value().timestamp(), dateTimes.last()); +} + +void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime() +{ + // Tests that the class does not emit an update until it receives a + // sentences with a valid date *and* time. All sentences before this + // should be ignored, and any sentences received after this that do + // not have a date should use the known date. + + QFETCH(QByteArray, bytes); + QFETCH(QList, dateTimes); + QFETCH(QList, expectHorizontalAccuracy); + QFETCH(QList, expectVerticalAccuracy); + + QNmeaPositionInfoSource source(m_mode); + source.setUserEquivalentRangeError(5.1); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + QObject::connect(proxy->source(), &QNmeaPositionInfoSource::positionUpdated, [](const QGeoPositionInfo &info) { + qDebug() << info.timestamp(); + }); + + proxy->source()->startUpdates(); + proxy->feedBytes(bytes); + QTest::qWait(1000); // default push delay is 20ms + QTRY_COMPARE(spy.count(), dateTimes.count()); + + for (int i=0; i(); + + QCOMPARE(pInfo.timestamp(), dateTimes[i]); + + // Generated GGA/GSA sentences have hard coded HDOP of 3.5, which corrisponds to a + // horizontal accuracy of 35.7, for the user equivalent range error of 5.1 set above. + QCOMPARE(pInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy), + expectHorizontalAccuracy[i]); + if (pInfo.hasAttribute(QGeoPositionInfo::HorizontalAccuracy)) + QVERIFY(qFuzzyCompare(pInfo.attribute(QGeoPositionInfo::HorizontalAccuracy), 35.7)); + + // Generated GSA sentences have hard coded VDOP of 4.0, which corrisponds to a vertical + // accuracy of 40.8, for the user equivalent range error of 5.1 set above. + QCOMPARE(pInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy), + expectVerticalAccuracy[i]); + if (pInfo.hasAttribute(QGeoPositionInfo::VerticalAccuracy)) + QVERIFY(qFuzzyCompare(pInfo.attribute(QGeoPositionInfo::VerticalAccuracy), 40.8)); + } +} + +void tst_QNmeaPositionInfoSource::startUpdates_waitForValidDateTime_data() +{ + QTest::addColumn("bytes"); + QTest::addColumn >("dateTimes"); + QTest::addColumn >("expectHorizontalAccuracy"); + QTest::addColumn >("expectVerticalAccuracy"); + + QDateTime dt = QDateTime::currentDateTime().toUTC(); + QByteArray bytes; + + // should only receive RMC sentence and the GGA sentence *after* it + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(100).time()).toLatin1(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(200)).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1(); + // The first GGA does not have date, and there's no cached date to inject, so that update will be invalid + QTest::newRow("Feed GGA,RMC,GGA; expect RMC, second GGA only") + << bytes << (QList() << dt.addMSecs(200) << dt.addMSecs(300)) + << (QList() << true << true) // accuracies are currently cached and injected in QGeoPositionInfos that do not have it + << (QList() << false << false); + + // should not receive ZDA (has no coordinates) but should get the GGA + // sentence after it since it got the date/time from ZDA + bytes.clear(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(100).time()).toLatin1(); + bytes += QLocationTestUtils::createZdaSentence(dt.addMSecs(200)).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1(); + QTest::newRow("Feed GGA,ZDA,GGA; expect second GGA only") + << bytes << (QList() << dt.addMSecs(300)) + << (QList() << true) + << (QList() << false); + + // Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA. + bytes.clear(); + bytes += QLocationTestUtils::createZdaSentence(dt.addMSecs(100)).toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(200).time()).toLatin1(); + bytes += QLocationTestUtils::createGsaSentence().toLatin1(); + bytes += QLocationTestUtils::createGgaSentence(dt.addMSecs(300).time()).toLatin1(); + if (m_mode == QNmeaPositionInfoSource::SimulationMode) { + QTest::newRow("Feed ZDA,GGA,GSA,GGA; expect vertical accuracy from second GGA") + << bytes << (QList() << dt.addMSecs(200) << dt.addMSecs(300)) + << (QList() << true << true) + << (QList() << true << true); // First GGA gets VDOP from GSA bundled into previous, as it has no timestamp, second GGA gets the cached value. + } + + if (m_mode == QNmeaPositionInfoSource::SimulationMode) { + // In sim m_mode, should ignore sentence with a date/time before the known date/time + // (in real time m_mode, everything is passed on regardless) + bytes.clear(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(100)).toLatin1(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(-200)).toLatin1(); + bytes += QLocationTestUtils::createRmcSentence(dt.addMSecs(200)).toLatin1(); + QTest::newRow("Feed good RMC, RMC with bad date/time, good RMC; expect first and third RMC only") + << bytes << (QList() << dt.addMSecs(100) << dt.addMSecs(200)) + << (QList() << false << false) + << (QList() << false << false); + } +} + +void tst_QNmeaPositionInfoSource::requestUpdate_waitForValidDateTime() +{ + QFETCH(QByteArray, bytes); + QFETCH(QList, dateTimes); + + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + proxy->source()->requestUpdate(); + + proxy->feedBytes(bytes); + QTRY_COMPARE(spy.count(), 1); + QCOMPARE(spy[0][0].value().timestamp(), dateTimes[0]); +} + +void tst_QNmeaPositionInfoSource::requestUpdate_waitForValidDateTime_data() +{ + startUpdates_waitForValidDateTime_data(); +} + +void tst_QNmeaPositionInfoSource::requestUpdate() +{ + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout())); + QDateTime dt; + + proxy->source()->requestUpdate(100); + QTRY_COMPARE(spyTimeout.count(), 1); + spyTimeout.clear(); + + dt = QDateTime::currentDateTime().toUTC(); + proxy->feedUpdate(dt); + proxy->source()->requestUpdate(); + QTRY_COMPARE(spyUpdate.count(), 1); + QCOMPARE(spyUpdate[0][0].value().timestamp(), dt); + QCOMPARE(spyTimeout.count(), 0); + spyUpdate.clear(); + + // delay the update and expect it to be emitted after 300ms + dt = QDateTime::currentDateTime().toUTC(); + proxy->source()->requestUpdate(1000); + QTest::qWait(300); + proxy->feedUpdate(dt); + QTRY_COMPARE(spyUpdate.count(), 1); + QCOMPARE(spyUpdate[0][0].value().timestamp(), dt); + QCOMPARE(spyTimeout.count(), 0); + spyUpdate.clear(); + + // delay the update and expect updateTimeout() to be emitted + dt = QDateTime::currentDateTime().toUTC(); + proxy->source()->requestUpdate(500); + QTest::qWait(1000); + proxy->feedUpdate(dt); + QCOMPARE(spyTimeout.count(), 1); + QCOMPARE(spyUpdate.count(), 0); + spyUpdate.clear(); +} + +void tst_QNmeaPositionInfoSource::requestUpdate_after_start() +{ + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spyUpdate(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + QSignalSpy spyTimeout(proxy->source(), SIGNAL(updateTimeout())); + + // Start updates with 500ms interval and requestUpdate() with 100ms + // timeout. Feed an update, and it should be emitted immediately due to + // the requestUpdate(). The update should not be emitted again after that + // (i.e. the startUpdates() interval should not cause it to be re-emitted). + + QDateTime dt = QDateTime::currentDateTime().toUTC(); + proxy->source()->setUpdateInterval(500); + proxy->source()->startUpdates(); + proxy->source()->requestUpdate(100); + proxy->feedUpdate(dt); + QTRY_COMPARE(spyUpdate.count(), 1); + QCOMPARE(spyUpdate[0][0].value().timestamp(), dt); + QCOMPARE(spyTimeout.count(), 0); + spyUpdate.clear(); + + // Update has been emitted for requestUpdate(), shouldn't be emitted for startUpdates() + QTRY_COMPARE_WITH_TIMEOUT(spyUpdate.count(), 0, 1000); +} + +void tst_QNmeaPositionInfoSource::testWithBadNmea() +{ + QFETCH(QByteArray, bytes); + QFETCH(QList, dateTimes); + QFETCH(UpdateTriggerMethod, trigger); + + QNmeaPositionInfoSource source(m_mode); + QNmeaPositionInfoSourceProxyFactory factory; + QNmeaPositionInfoSourceProxy *proxy = static_cast(factory.createProxy(&source)); + + QSignalSpy spy(proxy->source(), SIGNAL(positionUpdated(QGeoPositionInfo))); + if (trigger == StartUpdatesMethod) + proxy->source()->startUpdates(); + else + proxy->source()->requestUpdate(); + + proxy->feedBytes(bytes); + QTRY_COMPARE(spy.count(), dateTimes.count()); + for (int i=0; i().timestamp(), dateTimes[i]); +} + +void tst_QNmeaPositionInfoSource::testWithBadNmea_data() +{ + QTest::addColumn("bytes"); + QTest::addColumn >("dateTimes"); + QTest::addColumn("trigger"); + + QDateTime firstDateTime = QDateTime::currentDateTime().toUTC(); + QByteArray bad = QLocationTestUtils::createRmcSentence(firstDateTime.addSecs(1)).toLatin1(); + bad = bad.mid(bad.length()/2); + QDateTime lastDateTime = firstDateTime.addSecs(2); + + QByteArray bytes; + bytes += QLocationTestUtils::createRmcSentence(firstDateTime).toLatin1(); + bytes += bad; + bytes += QLocationTestUtils::createRmcSentence(lastDateTime).toLatin1(); + QTest::newRow("requestUpdate(), bad second sentence") << bytes + << (QList() << firstDateTime) << RequestUpdatesMethod; + QTest::newRow("startUpdates(), bad second sentence") << bytes + << (QList() << firstDateTime << lastDateTime) << StartUpdatesMethod; +} diff --git a/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h new file mode 100644 index 0000000..073a4aa --- /dev/null +++ b/tests/auto/qnmeapositioninfosource/tst_qnmeapositioninfosource.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qnmeapositioninfosourceproxyfactory.h" +#include "../qgeopositioninfosource/testqgeopositioninfosource_p.h" +#include "../utils/qlocationtestutils_p.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE +Q_DECLARE_METATYPE(QNmeaPositionInfoSource::UpdateMode) +Q_DECLARE_METATYPE(QList) + +class tst_QNmeaPositionInfoSource : public QObject +{ + Q_OBJECT + +public: + enum UpdateTriggerMethod + { + StartUpdatesMethod, + RequestUpdatesMethod + }; + + tst_QNmeaPositionInfoSource(QNmeaPositionInfoSource::UpdateMode mode, QObject *parent = 0); + +private: + QList createDateTimes(const QDateTime &dt, int count) const + { + QList dateTimes; + int interval = 100; + for (int i=0; i createDateTimes(int count) const + { + return createDateTimes(QDateTime::currentDateTime().toUTC(), count); + } + +private slots: + void initTestCase(); + + void constructor(); + + void supportedPositioningMethods(); + + void minimumUpdateInterval(); + + void userEquivalentRangeError(); + + void setUpdateInterval_delayedUpdate(); + + void lastKnownPosition(); + + void beginWithBufferedData(); + void beginWithBufferedData_data(); + + void startUpdates(); + void startUpdates_data(); + + void startUpdates_withTimeout(); + + void startUpdates_expectLatestUpdateOnly(); + + void startUpdates_waitForValidDateTime(); + void startUpdates_waitForValidDateTime_data(); + + void requestUpdate_waitForValidDateTime(); + void requestUpdate_waitForValidDateTime_data(); + + void requestUpdate(); + void requestUpdate_after_start(); + + void testWithBadNmea(); + void testWithBadNmea_data(); + +private: + QNmeaPositionInfoSource::UpdateMode m_mode; +}; + +Q_DECLARE_METATYPE(tst_QNmeaPositionInfoSource::UpdateTriggerMethod) + +//--------------------------------------------------- + +class Feeder : public QObject +{ + Q_OBJECT + +public: + Feeder(QObject *parent) + : QObject(parent) + { + } + + void start(QNmeaPositionInfoSourceProxy *proxy) + { + m_proxy = proxy; + QTimer *timer = new QTimer; + QObject::connect(timer, SIGNAL(timeout()), this, SLOT(timeout())); + timer->setInterval(proxy->source()->minimumUpdateInterval()*2); + timer->start(); + } + +public slots: + void timeout() + { + m_proxy->feedBytes(QLocationTestUtils::createRmcSentence(QDateTime::currentDateTime()).toLatin1()); + } + +private: + QNmeaPositionInfoSourceProxy *m_proxy; +}; + +//--------------------------------------------------- + + +class UnlimitedNmeaStream : public QIODevice +{ + Q_OBJECT + +public: + UnlimitedNmeaStream(QObject *parent) : QIODevice(parent) {} + +protected: + qint64 readData(char *data, qint64 maxSize) + { + QByteArray bytes = QLocationTestUtils::createRmcSentence(QDateTime::currentDateTime()).toLatin1(); + qint64 sz = qMin(qint64(bytes.size()), maxSize); + memcpy(data, bytes.constData(), sz); + return sz; + } + + qint64 writeData(const char *, qint64) + { + return -1; + } + + qint64 bytesAvailable() const + { + return 1024 + QIODevice::bytesAvailable(); + } +}; diff --git a/tests/auto/qplace/qplace.pro b/tests/auto/qplace/qplace.pro new file mode 100644 index 0000000..290c831 --- /dev/null +++ b/tests/auto/qplace/qplace.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplace + +SOURCES += tst_qplace.cpp + +QT += location testlib diff --git a/tests/auto/qplace/tst_qplace.cpp b/tests/auto/qplace/tst_qplace.cpp new file mode 100644 index 0000000..bc86599 --- /dev/null +++ b/tests/auto/qplace/tst_qplace.cpp @@ -0,0 +1,681 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_Place : public QObject +{ + Q_OBJECT + +public: + tst_Place(); + +private Q_SLOTS: + void constructorTest(); + void categoriesTest(); + void detailsFetchedTest(); + void locationTest(); + void ratingTest(); + void supplierTest(); + void imageContentTest(); + void reviewContentTest(); + void editorialContentTest(); + void totalContentCountTest(); + void totalContentCountTest_data(); + void nameTest(); + void placeIdTest(); + void attributionTest(); + void contactDetailsTest(); + void primaryPhoneTest(); + void primaryFaxTest(); + void primaryEmailTest(); + void primaryWebsiteTest(); + void operatorsTest(); + void extendedAttributeTest(); + void visibilityTest(); + void isEmptyTest(); +}; + +tst_Place::tst_Place() +{ +} + +void tst_Place::constructorTest() +{ + QPlace testObj; + testObj.setPlaceId("testId"); + QPlaceAttribute paymentMethods; + paymentMethods.setLabel("Payment methods"); + paymentMethods.setText("Visa"); + testObj.setExtendedAttribute(QStringLiteral("paymentMethods"), paymentMethods); + QGeoLocation loc; + loc.setCoordinate(QGeoCoordinate(10,20)); + testObj.setLocation(loc); + QPlace *testObjPtr = new QPlace(testObj); + + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + + delete testObjPtr; +} + +void tst_Place::nameTest() +{ + QPlace testObj; + QVERIFY2(testObj.name() == QString(), "Wrong default value"); + testObj.setName("testText"); + QVERIFY2(testObj.name() == "testText", "Wrong value returned"); +} + +void tst_Place::placeIdTest() +{ + QPlace testObj; + QVERIFY2(testObj.placeId() == QString(), "Wrong default value"); + testObj.setPlaceId("testText"); + QVERIFY2(testObj.placeId() == "testText", "Wrong value returned"); +} + +void tst_Place::totalContentCountTest() +{ + QFETCH(QPlaceContent::Type, contentType); + QPlace testObj; + QVERIFY2(testObj.totalContentCount(contentType) == 0, "Wrong default value"); + testObj.setTotalContentCount(contentType, 50); + QVERIFY2(testObj.totalContentCount(contentType) == 50, "Wrong value returned"); + + testObj.setTotalContentCount(contentType,0); + QVERIFY2(testObj.totalContentCount(contentType) == 0, "Wrong value returned"); +} + +void tst_Place::totalContentCountTest_data() +{ + QTest::addColumn("contentType"); + QTest::newRow("Image content") << QPlaceContent::ImageType; + QTest::newRow("Editoral content") << QPlaceContent::EditorialType; + QTest::newRow("Review content") << QPlaceContent::ReviewType; +} + +void tst_Place::ratingTest() +{ + QPlace testObj; + QVERIFY2(testObj.ratings() == QPlaceRatings(), "Wrong default value"); + QPlaceRatings obj; + obj.setCount(10); + testObj.setRatings(obj); + QVERIFY2(testObj.ratings() == obj, "Wrong value returned"); +} + +void tst_Place::locationTest() +{ + QPlace testObj; + QVERIFY2(testObj.location() == QGeoLocation(), "Wrong default value"); + QGeoLocation obj; + obj.setCoordinate(QGeoCoordinate(10,20)); + testObj.setLocation(obj); + QVERIFY2(testObj.location() == obj, "Wrong value returned"); +} + +void tst_Place::detailsFetchedTest() +{ + QPlace testPlace; + QVERIFY2(testPlace.detailsFetched() == false, "Wrong default value"); + testPlace.setDetailsFetched(true); + QVERIFY2(testPlace.detailsFetched() == true, "Wrong value returned"); + testPlace.setDetailsFetched(false); + QVERIFY2(testPlace.detailsFetched() == false, "Wrong value returned"); +} + +void tst_Place::imageContentTest() +{ + QPlace place; + QVERIFY2(place.content(QPlaceContent::ImageType).count() ==0,"Wrong default value"); + + QPlaceImage dummyImage; + dummyImage.setUrl(QUrl("www.dummy.one")); + + QPlaceImage dummyImage2; + dummyImage2.setUrl(QUrl("www.dummy.two")); + + QPlaceImage dummyImage3; + dummyImage3.setUrl(QUrl("www.dummy.three")); + + QPlaceContent::Collection imageCollection; + imageCollection.insert(0,dummyImage); + imageCollection.insert(1, dummyImage2); + imageCollection.insert(2, dummyImage3); + + place.setContent(QPlaceContent::ImageType, imageCollection); + QPlaceContent::Collection retrievedCollection = place.content(QPlaceContent::ImageType); + + QCOMPARE(retrievedCollection.count(), 3); + QCOMPARE(QPlaceImage(retrievedCollection.value(0)), dummyImage); + QCOMPARE(QPlaceImage(retrievedCollection.value(1)), dummyImage2); + QCOMPARE(QPlaceImage(retrievedCollection.value(2)), dummyImage3); + + //replace the second and insert a sixth image + //indexes 4 and 5 are "missing" + QPlaceImage dummyImage2New; + dummyImage2.setUrl(QUrl("www.dummy.two.new")); + + QPlaceImage dummyImage6; + dummyImage6.setUrl(QUrl("www.dummy.six")); + + imageCollection.clear(); + imageCollection.insert(1, dummyImage2New); + imageCollection.insert(5, dummyImage6); + place.insertContent(QPlaceContent::ImageType, imageCollection); + + retrievedCollection = place.content(QPlaceContent::ImageType); + QCOMPARE(retrievedCollection.count(), 4); + QCOMPARE(QPlaceImage(retrievedCollection.value(0)), dummyImage); + QCOMPARE(QPlaceImage(retrievedCollection.value(1)), dummyImage2New); + QCOMPARE(QPlaceImage(retrievedCollection.value(2)), dummyImage3); + QCOMPARE(QPlaceImage(retrievedCollection.value(3)), QPlaceImage()); + QCOMPARE(QPlaceImage(retrievedCollection.value(4)), QPlaceImage()); + QCOMPARE(QPlaceImage(retrievedCollection.value(5)), dummyImage6); +} + +void tst_Place::reviewContentTest() +{ + QPlace place; + QVERIFY2(place.content(QPlaceContent::ReviewType).count() ==0,"Wrong default value"); + + QPlaceReview dummyReview; + dummyReview.setTitle(QStringLiteral("Review 1")); + + QPlaceReview dummyReview2; + dummyReview2.setTitle(QStringLiteral("Review 2")); + + QPlaceReview dummyReview3; + dummyReview3.setTitle(QStringLiteral("Review 3")); + + QPlaceContent::Collection reviewCollection; + reviewCollection.insert(0,dummyReview); + reviewCollection.insert(1, dummyReview2); + reviewCollection.insert(2, dummyReview3); + + place.setContent(QPlaceContent::ReviewType, reviewCollection); + QPlaceContent::Collection retrievedCollection = place.content(QPlaceContent::ReviewType); + + QCOMPARE(retrievedCollection.count(), 3); + QCOMPARE(QPlaceReview(retrievedCollection.value(0)), dummyReview); + QCOMPARE(QPlaceReview(retrievedCollection.value(1)), dummyReview2); + QCOMPARE(QPlaceReview(retrievedCollection.value(2)), dummyReview3); + + //replace the second and insert a sixth review + //indexes 4 and 5 are "missing" + QPlaceReview dummyReview2New; + dummyReview2.setTitle(QStringLiteral("Review 2 new")); + + QPlaceReview dummyReview6; + dummyReview6.setTitle(QStringLiteral("Review 6")); + + reviewCollection.clear(); + reviewCollection.insert(1, dummyReview2New); + reviewCollection.insert(5, dummyReview6); + place.insertContent(QPlaceContent::ReviewType, reviewCollection); + + retrievedCollection = place.content(QPlaceContent::ReviewType); + QCOMPARE(retrievedCollection.count(), 4); + QCOMPARE(QPlaceReview(retrievedCollection.value(0)), dummyReview); + QCOMPARE(QPlaceReview(retrievedCollection.value(1)), dummyReview2New); + QCOMPARE(QPlaceReview(retrievedCollection.value(2)), dummyReview3); + QCOMPARE(QPlaceReview(retrievedCollection.value(3)), QPlaceReview()); + QCOMPARE(QPlaceReview(retrievedCollection.value(4)), QPlaceReview()); + QCOMPARE(QPlaceReview(retrievedCollection.value(5)), dummyReview6); +} + +void tst_Place::editorialContentTest() +{ + QPlace place; + QVERIFY2(place.content(QPlaceContent::EditorialType).count() == 0, "Wrong default value"); + + QPlaceEditorial dummyEditorial; + dummyEditorial.setTitle(QStringLiteral("Editorial 1")); + + QPlaceEditorial dummyEditorial2; + dummyEditorial2.setTitle(QStringLiteral("Editorial 2")); + + QPlaceEditorial dummyEditorial3; + dummyEditorial3.setTitle(QStringLiteral("Editorial 3")); + + QPlaceContent::Collection editorialCollection; + editorialCollection.insert(0,dummyEditorial); + editorialCollection.insert(1, dummyEditorial2); + editorialCollection.insert(2, dummyEditorial3); + + place.setContent(QPlaceContent::EditorialType, editorialCollection); + QPlaceContent::Collection retrievedCollection = place.content(QPlaceContent::EditorialType); + + QCOMPARE(retrievedCollection.count(), 3); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(0)), dummyEditorial); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(1)), dummyEditorial2); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(2)), dummyEditorial3); + + //replace the second and insert a sixth editorial + //indexes 4 and 5 are "missing" + QPlaceEditorial dummyEditorial2New; + dummyEditorial2.setTitle(QStringLiteral("Editorial 2 new")); + + QPlaceEditorial dummyEditorial6; + dummyEditorial6.setTitle(QStringLiteral("Editorial 6")); + + editorialCollection.clear(); + editorialCollection.insert(1, dummyEditorial2New); + editorialCollection.insert(5, dummyEditorial6); + place.insertContent(QPlaceContent::EditorialType, editorialCollection); + + retrievedCollection = place.content(QPlaceContent::EditorialType); + QCOMPARE(retrievedCollection.count(), 4); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(0)), dummyEditorial); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(1)), dummyEditorial2New); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(2)), dummyEditorial3); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(3)), QPlaceEditorial()); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(4)), QPlaceEditorial()); + QCOMPARE(QPlaceEditorial(retrievedCollection.value(5)), dummyEditorial6); +} + +void tst_Place::categoriesTest() +{ + QPlace place; + QVERIFY(place.categories().isEmpty()); + + //set a single category + QPlaceCategory cat1; + cat1.setName("cat1"); + + place.setCategory(cat1); + QCOMPARE(place.categories().count(), 1); + QCOMPARE(place.categories().at(0), cat1); + + //set multiple categories + QPlaceCategory cat2; + cat2.setName("cat2"); + + QPlaceCategory cat3; + cat3.setName("cat3"); + + QList categories; + categories << cat2 << cat3; + + place.setCategories(categories); + QCOMPARE(place.categories().count(), 2); + QVERIFY(place.categories().contains(cat2)); + QVERIFY(place.categories().contains(cat3)); + + //set a single category again while there are multiple categories already assigned. + place.setCategory(cat1); + QCOMPARE(place.categories().count(), 1); + QCOMPARE(place.categories().at(0), cat1); + + //set an empty list of categories + place.setCategories(QList()); + QVERIFY(place.categories().isEmpty()); +} + +void tst_Place::supplierTest() +{ + QPlace testObj; + QCOMPARE(testObj.supplier(), QPlaceSupplier()); + + QPlaceSupplier sup; + sup.setName("testName1"); + sup.setSupplierId("testId"); + + testObj.setSupplier(sup); + + QCOMPARE(testObj.supplier(), sup); +} + +void tst_Place::attributionTest() +{ + QPlace testPlace; + QVERIFY(testPlace.attribution().isEmpty()); + testPlace.setAttribution(QStringLiteral("attribution")); + QCOMPARE(testPlace.attribution(), QStringLiteral("attribution")); + testPlace.setAttribution(QString()); + QVERIFY(testPlace.attribution().isEmpty()); +} + +void tst_Place::contactDetailsTest() +{ + QPlaceContactDetail phone1; + phone1.setLabel("Phone1"); + phone1.setValue("555-5555"); + + QPlaceContactDetail phone2; + phone2.setLabel("Phone2"); + phone2.setValue("555-5556"); + + QList phones; + phones << phone1 << phone2; + + + QPlaceContactDetail email; + email.setLabel("Email"); + email.setValue("email@email.com"); + + QPlace place; + place.setContactDetails(QPlaceContactDetail::Phone,phones); + QCOMPARE(place.contactTypes().count(), 1); + QVERIFY(place.contactTypes().contains(QPlaceContactDetail::Phone)); + QCOMPARE(place.contactDetails(QPlaceContactDetail::Phone), phones); + + place.appendContactDetail(QPlaceContactDetail::Email, email); + QCOMPARE(place.contactTypes().count(), 2); + QVERIFY(place.contactTypes().contains(QPlaceContactDetail::Phone)); + QVERIFY(place.contactTypes().contains(QPlaceContactDetail::Email)); + QCOMPARE(place.contactDetails(QPlaceContactDetail::Phone), phones); + QCOMPARE(place.contactDetails(QPlaceContactDetail::Email).count(), 1); + QCOMPARE(place.contactDetails(QPlaceContactDetail::Email).at(0), email); + + place.removeContactDetails(QPlaceContactDetail::Phone); + QCOMPARE(place.contactTypes().count(), 1); + QVERIFY(!place.contactTypes().contains(QPlaceContactDetail::Phone)); + QVERIFY(place.contactDetails(QPlaceContactDetail::Phone).isEmpty()); + QVERIFY(place.contactTypes().contains(QPlaceContactDetail::Email)); + QCOMPARE(place.contactDetails(QPlaceContactDetail::Email).count(), 1); + QCOMPARE(place.contactDetails(QPlaceContactDetail::Email).at(0), email); + + place.removeContactDetails(QPlaceContactDetail::Email); + QVERIFY(place.contactTypes().isEmpty()); + QVERIFY(place.contactDetails(QPlaceContactDetail::Email).isEmpty()); +} + +void tst_Place::primaryPhoneTest() +{ + QPlace place; + QVERIFY2(place.primaryPhone().isEmpty(), "Wrong default value"); + + QPlaceContactDetail contactDetail; + contactDetail.setLabel(QStringLiteral("Phone")); + contactDetail.setValue(QStringLiteral("555-5555")); + place.appendContactDetail(QPlaceContactDetail::Phone, contactDetail); + + QCOMPARE(place.primaryPhone(), QString("555-5555")); + + //try clearing the primary phone number + place.setContactDetails(QPlaceContactDetail::Phone, QList()); + QCOMPARE(place.primaryPhone(), QString()); +} + +void tst_Place::primaryEmailTest() +{ + QPlace place; + QVERIFY2(place.primaryEmail().isEmpty(), "Wrong default value"); + + QPlaceContactDetail contactDetail; + contactDetail.setLabel(QStringLiteral("Email")); + contactDetail.setValue(QStringLiteral("test@test.com")); + place.appendContactDetail(QPlaceContactDetail::Email, contactDetail); + + QCOMPARE(place.primaryEmail(), QStringLiteral("test@test.com")); + + //try clearing the primary email address + place.setContactDetails(QPlaceContactDetail::Email, QList()); + QCOMPARE(place.primaryEmail(), QString()); +} + +void tst_Place::primaryFaxTest() +{ + QPlace place; + QVERIFY2(place.primaryFax().isEmpty(), "Wrong default value"); + + QPlaceContactDetail contactDetail; + contactDetail.setLabel(QStringLiteral("Fax")); + contactDetail.setValue(QStringLiteral("555-5555")); + place.appendContactDetail(QPlaceContactDetail::Fax, contactDetail); + + QCOMPARE(place.primaryFax(), QStringLiteral("555-5555")); + + //try clearing the primary fax number + place.setContactDetails(QPlaceContactDetail::Fax, QList()); + QCOMPARE(place.primaryFax(), QString()); +} + +void tst_Place::primaryWebsiteTest() +{ + QPlace place; + QVERIFY2(place.primaryWebsite().isEmpty(), "Wrong default value"); + + QPlaceContactDetail contactDetail; + contactDetail.setLabel(QStringLiteral("Website")); + contactDetail.setValue(QStringLiteral("www.example.com")); + place.appendContactDetail(QPlaceContactDetail::Website, contactDetail); + + QCOMPARE(place.primaryWebsite(), QUrl("www.example.com")); + + //try clearing the primary website number + place.setContactDetails(QPlaceContactDetail::Website, QList()); + QCOMPARE(place.primaryWebsite(), QUrl()); +} + +void tst_Place::operatorsTest() +{ + QPlace testObj; + testObj.setPlaceId("testId"); + QPlaceAttribute paymentMethods; + paymentMethods.setLabel("Payment methods"); + paymentMethods.setText("Visa"); + testObj.setExtendedAttribute(QStringLiteral("paymentMethods"), paymentMethods); + QGeoLocation loc; + loc.setCoordinate(QGeoCoordinate(10,20)); + testObj.setLocation(loc); + + QPlace testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setPlaceId("342-456"); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_Place::extendedAttributeTest() +{ + QPlace place; + QVERIFY(place.extendedAttributeTypes().isEmpty()); + QPlaceAttribute smoking; + smoking.setLabel(QStringLiteral("Public Smoking")); + smoking.setText(QStringLiteral("No")); + + //test setting of an attribue + place.setExtendedAttribute(QStringLiteral("smoking"), smoking); + + QVERIFY(place.extendedAttributeTypes().contains(QStringLiteral("smoking"))); + QCOMPARE(place.extendedAttributeTypes().count(), 1); + + QCOMPARE(place.extendedAttribute(QStringLiteral("smoking")).label(), QStringLiteral("Public Smoking")); + QCOMPARE(place.extendedAttribute(QStringLiteral("smoking")).text(), QStringLiteral("No")); + + QPlaceAttribute shelter; + shelter.setLabel(QStringLiteral("Outdoor shelter")); + shelter.setText(QStringLiteral("Yes")); + + //test setting another new attribute + place.setExtendedAttribute("shelter", shelter); + QVERIFY(place.extendedAttributeTypes().contains(QStringLiteral("shelter"))); + QVERIFY(place.extendedAttributeTypes().contains(QStringLiteral("smoking"))); + QCOMPARE(place.extendedAttributeTypes().count(), 2); + QCOMPARE(place.extendedAttribute(QStringLiteral("shelter")).label(), QStringLiteral("Outdoor shelter")); + QCOMPARE(place.extendedAttribute(QStringLiteral("shelter")).text(), QStringLiteral("Yes")); + + //test overwriting an attribute + shelter.setText(QStringLiteral("No")); + place.setExtendedAttribute(QStringLiteral("shelter"), shelter); + + QVERIFY(place.extendedAttributeTypes().contains(QStringLiteral("shelter"))); + QVERIFY(place.extendedAttributeTypes().contains(QStringLiteral("smoking"))); + QCOMPARE(place.extendedAttributeTypes().count(), 2); + QCOMPARE(place.extendedAttribute(QStringLiteral("shelter")).text(), QStringLiteral("No")); + + //test clearing of attributes by setting them to the default attribute + foreach (const QString &attributeType, place.extendedAttributeTypes()) + place.setExtendedAttribute(attributeType, QPlaceAttribute()); + + QCOMPARE(place.extendedAttributeTypes().count(), 0); + + //test removing of attributes + place.setExtendedAttribute(QStringLiteral("smoking"), smoking); + QVERIFY(!place.extendedAttributeTypes().isEmpty()); + place.removeExtendedAttribute(QStringLiteral("smoking")); + QVERIFY(place.extendedAttributeTypes().isEmpty()); +} +void tst_Place::visibilityTest() +{ + QPlace place; + + QCOMPARE(place.visibility(), QLocation::UnspecifiedVisibility); + + place.setVisibility(QLocation::DeviceVisibility); + + QCOMPARE(place.visibility(), QLocation::DeviceVisibility); +} + +void tst_Place::isEmptyTest() +{ + QGeoLocation location; + location.setCoordinate(QGeoCoordinate(6.788697, 51.224679)); + QVERIFY(!location.isEmpty()); + + QPlaceCategory category; + + QPlaceRatings ratings; + ratings.setCount(1); + QVERIFY(!ratings.isEmpty()); + + QPlaceSupplier supplier; + supplier.setName(QStringLiteral("Foo & Bar Imports")); + QVERIFY(!supplier.isEmpty()); + + QPlaceIcon icon; + QVariantMap iconParametersMap; + iconParametersMap.insert(QStringLiteral("Para"), QStringLiteral("meter")); + icon.setParameters(iconParametersMap); + QVERIFY(!icon.isEmpty()); + + QPlaceContent content; + QPlaceContent::Collection contentCollection; + contentCollection.insert(42, content); + + QPlaceAttribute attribute; + attribute.setLabel(QStringLiteral("noodle")); + + QPlaceContactDetail contactDetail; + + + QPlace place; + + // default constructed + QVERIFY(place.isEmpty()); + + // categories + place.setCategory(category); + QVERIFY(!place.isEmpty()); + place.categories().clear(); + place = QPlace(); + + // location + place.setLocation(location); + QVERIFY(!place.isEmpty()); + place.setLocation(QGeoLocation()); + QVERIFY(place.isEmpty()); + + // ratings + place.setRatings(ratings); + QVERIFY(!place.isEmpty()); + place.setRatings(QPlaceRatings()); + QVERIFY(place.isEmpty()); + + // supplier + place.setSupplier(supplier); + QVERIFY(!place.isEmpty()); + place.setSupplier(QPlaceSupplier()); + QVERIFY(place.isEmpty()); + + // attribution + place.setAttribution(QStringLiteral("attr")); + QVERIFY(!place.isEmpty()); + place.setAttribution(QString()); + QVERIFY(place.isEmpty()); + + // icon + place.setIcon(icon); + QVERIFY(!place.isEmpty()); + place.setIcon(QPlaceIcon()); + QVERIFY(place.isEmpty()); + + // content + place.insertContent(QPlaceContent::EditorialType, contentCollection); + QVERIFY(!place.isEmpty()); + place = QPlace(); + + // name + place.setName(QStringLiteral("Naniwa")); + QVERIFY(!place.isEmpty()); + place.setName(QString()); + QVERIFY(place.isEmpty()); + + // placeId + place.setPlaceId(QStringLiteral("naniwa")); + QVERIFY(!place.isEmpty()); + place.setPlaceId(QString()); + QVERIFY(place.isEmpty()); + + // extendedAttributes + place.setExtendedAttribute(QStringLiteral("part"), attribute); + QVERIFY(!place.isEmpty()); + place.removeExtendedAttribute(QStringLiteral("part")); + QVERIFY(place.isEmpty()); + + // extendedAttributes + place.setDetailsFetched(true); + QVERIFY(place.isEmpty()); + + // contact detail + place.appendContactDetail(QStringLiteral("phone"), contactDetail); + QVERIFY(!place.isEmpty()); + place.removeContactDetails(QStringLiteral("phone")); + QVERIFY(place.isEmpty()); + + // visibility + place.setVisibility(QLocation::DeviceVisibility); + QVERIFY(!place.isEmpty()); + place.setVisibility(QLocation::UnspecifiedVisibility); + QVERIFY(place.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_Place) + +#include "tst_qplace.moc" diff --git a/tests/auto/qplaceattribute/qplaceattribute.pro b/tests/auto/qplaceattribute/qplaceattribute.pro new file mode 100644 index 0000000..4fc27e5 --- /dev/null +++ b/tests/auto/qplaceattribute/qplaceattribute.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaceattribute + +SOURCES += tst_qplaceattribute.cpp + +QT += location testlib diff --git a/tests/auto/qplaceattribute/tst_qplaceattribute.cpp b/tests/auto/qplaceattribute/tst_qplaceattribute.cpp new file mode 100644 index 0000000..1f229ab --- /dev/null +++ b/tests/auto/qplaceattribute/tst_qplaceattribute.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QPlaceAttribute : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceAttribute(); + +private Q_SLOTS: + void constructorTest(); + void operatorsTest(); + void isEmptyTest(); +}; + +tst_QPlaceAttribute::tst_QPlaceAttribute() +{ +} + +void tst_QPlaceAttribute::constructorTest() +{ + QPlaceAttribute testObj; + + QPlaceAttribute *testObjPtr = new QPlaceAttribute(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(testObjPtr->label() == QString(), "Copy constructor - wrong label"); + QVERIFY2(testObjPtr->text() == QString(), "Copy constructor - wrong text"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceAttribute::operatorsTest() +{ + QPlaceAttribute testObj; + testObj.setLabel(QStringLiteral("label")); + QPlaceAttribute testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setText(QStringLiteral("text")); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QPlaceAttribute::isEmptyTest() +{ + QPlaceAttribute attribute; + + QVERIFY(attribute.isEmpty()); + + attribute.setLabel(QStringLiteral("label")); + QVERIFY(!attribute.isEmpty()); + attribute.setLabel(QString()); + QVERIFY(attribute.isEmpty()); + + attribute.setText(QStringLiteral("text")); + QVERIFY(!attribute.isEmpty()); + attribute.setText(QString()); + QVERIFY(attribute.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QPlaceAttribute); + +#include "tst_qplaceattribute.moc" diff --git a/tests/auto/qplacecategory/qplacecategory.pro b/tests/auto/qplacecategory/qplacecategory.pro new file mode 100644 index 0000000..43686ec --- /dev/null +++ b/tests/auto/qplacecategory/qplacecategory.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacecategory + +SOURCES += tst_qplacecategory.cpp + +QT += location testlib diff --git a/tests/auto/qplacecategory/tst_qplacecategory.cpp b/tests/auto/qplacecategory/tst_qplacecategory.cpp new file mode 100644 index 0000000..8a84d64 --- /dev/null +++ b/tests/auto/qplacecategory/tst_qplacecategory.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include + +QT_USE_NAMESPACE + +class tst_QPlaceCategory : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceCategory(); + +private Q_SLOTS: + void constructorTest(); + void categoryIdTest(); + void nameTest(); + void visibilityTest(); + void operatorsTest(); + void isEmptyTest(); +}; + +tst_QPlaceCategory::tst_QPlaceCategory() +{ +} + +void tst_QPlaceCategory::constructorTest() +{ + QPlaceCategory testObj; + Q_UNUSED(testObj); + + testObj.setCategoryId("testId"); + QPlaceCategory *testObjPtr = new QPlaceCategory(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceCategory::categoryIdTest() +{ + QPlaceCategory testObj; + QVERIFY2(testObj.categoryId() == QString(), "Wrong default value"); + testObj.setCategoryId("testText"); + QVERIFY2(testObj.categoryId() == "testText", "Wrong value returned"); +} + +void tst_QPlaceCategory::nameTest() +{ + QPlaceCategory testObj; + QVERIFY2(testObj.name() == QString(), "Wrong default value"); + testObj.setName("testText"); + QVERIFY2(testObj.name() == "testText", "Wrong value returned"); +} + +void tst_QPlaceCategory::visibilityTest() +{ + QPlaceCategory category; + + QCOMPARE(category.visibility(), QLocation::UnspecifiedVisibility); + + category.setVisibility(QLocation::DeviceVisibility); + + QCOMPARE(category.visibility(), QLocation::DeviceVisibility); +} + +void tst_QPlaceCategory::operatorsTest() +{ + QPlaceCategory testObj; + testObj.setName("testValue"); + QPlaceCategory testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setCategoryId("a3rfg"); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QPlaceCategory::isEmptyTest() +{ + QPlaceIcon icon; + QVariantMap parameters; + parameters.insert(QStringLiteral("para"), QStringLiteral("meter")); + icon.setParameters(parameters); + QVERIFY(!icon.isEmpty()); + + QPlaceCategory category; + + QVERIFY(category.isEmpty()); + + category.setName(QStringLiteral("name")); + QVERIFY(!category.isEmpty()); + category.setName(QString()); + QVERIFY(category.isEmpty()); + + category.setCategoryId(QStringLiteral("id")); + QVERIFY(!category.isEmpty()); + category.setCategoryId(QString()); + QVERIFY(category.isEmpty()); + + category.setVisibility(QLocation::PublicVisibility); + QVERIFY(!category.isEmpty()); + category.setVisibility(QLocation::UnspecifiedVisibility); + QVERIFY(category.isEmpty()); + + category.setIcon(icon); + QVERIFY(!category.isEmpty()); + category.setIcon(QPlaceIcon()); + QVERIFY(category.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QPlaceCategory); + +#include "tst_qplacecategory.moc" diff --git a/tests/auto/qplacecontactdetail/qplacecontactdetail.pro b/tests/auto/qplacecontactdetail/qplacecontactdetail.pro new file mode 100644 index 0000000..ab49471 --- /dev/null +++ b/tests/auto/qplacecontactdetail/qplacecontactdetail.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacecontactdetail + +SOURCES += tst_qplacecontactdetail.cpp + +QT += location testlib diff --git a/tests/auto/qplacecontactdetail/tst_qplacecontactdetail.cpp b/tests/auto/qplacecontactdetail/tst_qplacecontactdetail.cpp new file mode 100644 index 0000000..370bb5e --- /dev/null +++ b/tests/auto/qplacecontactdetail/tst_qplacecontactdetail.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QPlaceContactDetail : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceContactDetail(); + +private Q_SLOTS: + void constructorTest(); + void labelTest(); + void valueTest(); + void clearTest(); + void operatorsTest(); + void operatorsTest_data(); +}; + +tst_QPlaceContactDetail::tst_QPlaceContactDetail() +{ +} + +void tst_QPlaceContactDetail::constructorTest() +{ + QPlaceContactDetail detail; + QVERIFY(detail.label().isEmpty()); + QVERIFY(detail.value().isEmpty()); + + detail.setLabel(QStringLiteral("Emergency Services")); + detail.setValue(QStringLiteral("0118 999")); + + QPlaceContactDetail detail2(detail); + QCOMPARE(detail2.label(), QStringLiteral("Emergency Services")); + QCOMPARE(detail2.value(), QStringLiteral("0118 999")); +} + +void tst_QPlaceContactDetail::labelTest() +{ + QPlaceContactDetail detail; + detail.setLabel(QStringLiteral("home")); + QCOMPARE(detail.label(), QStringLiteral("home")); + detail.setLabel(QString()); + QVERIFY(detail.label().isEmpty()); +} + +void tst_QPlaceContactDetail::valueTest() +{ + QPlaceContactDetail detail; + detail.setValue(QStringLiteral("555-5555")); + QCOMPARE(detail.value(), QStringLiteral("555-5555")); + detail.setValue(QString()); + QVERIFY(detail.value().isEmpty()); +} + +void tst_QPlaceContactDetail::clearTest() +{ + QPlaceContactDetail detail; + detail.setLabel(QStringLiteral("Ghostbusters")); + detail.setValue(QStringLiteral("555-2368")); + detail.clear(); + QVERIFY(detail.label().isEmpty()); + QVERIFY(detail.value().isEmpty()); +} + +void tst_QPlaceContactDetail::operatorsTest() +{ + QPlaceContactDetail detail1; + detail1.setLabel(QStringLiteral("Kramer")); + detail1.setValue(QStringLiteral("555-filk")); + + QPlaceContactDetail detail2; + detail2.setLabel(QStringLiteral("Kramer")); + detail2.setValue(QStringLiteral("555-filk")); + + QVERIFY(detail1 == detail2); + QVERIFY(!(detail1 != detail2)); + QVERIFY(detail2 == detail1); + QVERIFY(!(detail2 != detail1)); + + QPlaceContactDetail detail3; + QVERIFY(!(detail1 == detail3)); + QVERIFY(detail1 != detail3); + QVERIFY(!(detail1 == detail3)); + QVERIFY(detail1 != detail3); + + detail3 = detail1; + QVERIFY(detail1 == detail3); + QVERIFY(!(detail1 != detail3)); + QVERIFY(detail3 == detail1); + QVERIFY(!(detail3 != detail1)); + + QFETCH(QString, field); + if (field == QStringLiteral("label")) + detail3.setLabel(QStringLiteral("Cosmo")); + else if (field == QStringLiteral("value")) + detail3.setValue(QStringLiteral("555-5555")); + + QVERIFY(!(detail1 == detail3)); + QVERIFY(detail1 != detail3); + QVERIFY(!(detail3 == detail1)); + QVERIFY(detail3 != detail1); +} + +void tst_QPlaceContactDetail::operatorsTest_data() +{ + QTest::addColumn("field"); + QTest::newRow("label") << "label"; + QTest::newRow("value") << "value"; +} + +QTEST_APPLESS_MAIN(tst_QPlaceContactDetail) + +#include "tst_qplacecontactdetail.moc" diff --git a/tests/auto/qplacecontentrequest/qplacecontentrequest.pro b/tests/auto/qplacecontentrequest/qplacecontentrequest.pro new file mode 100644 index 0000000..6e27c4e --- /dev/null +++ b/tests/auto/qplacecontentrequest/qplacecontentrequest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacecontentrequest +SOURCES += tst_qplacecontentrequest.cpp + +QT += location testlib diff --git a/tests/auto/qplacecontentrequest/tst_qplacecontentrequest.cpp b/tests/auto/qplacecontentrequest/tst_qplacecontentrequest.cpp new file mode 100644 index 0000000..46bfb9e --- /dev/null +++ b/tests/auto/qplacecontentrequest/tst_qplacecontentrequest.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QPlaceContentRequest : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceContentRequest(); + +private Q_SLOTS: + void contentTest(); + void clearTest(); +}; + +tst_QPlaceContentRequest::tst_QPlaceContentRequest() +{ +} + +void tst_QPlaceContentRequest::contentTest() +{ + QPlaceContentRequest req; + QCOMPARE(req.limit(), -1); + QCOMPARE(req.contentType(), QPlaceContent::NoType); + + //check that we can set the request fields + req.setLimit(100); + req.setContentType(QPlaceContent::ImageType); + QCOMPARE(req.limit(), 100); + QCOMPARE(req.contentType(), QPlaceContent::ImageType); + + //check that we assignment works correctly + QPlaceContentRequest otherReq; + otherReq.setLimit(10); + otherReq.setContentType(QPlaceContent::ReviewType); + req = otherReq; + QCOMPARE(req.limit(), 10); + QCOMPARE(req.contentType(), QPlaceContent::ReviewType); + QCOMPARE(req, otherReq); + + //check that comparison will fail if one the fields are different + req.setLimit(9000); + QVERIFY(req != otherReq); +} + +void tst_QPlaceContentRequest::clearTest() +{ + QPlaceContentRequest req; + req.setContentType(QPlaceContent::ReviewType); + req.setLimit(9000); + req.clear(); + QVERIFY(req.contentType() == QPlaceContent::NoType); + QVERIFY(req.limit() == -1); +} + +QTEST_APPLESS_MAIN(tst_QPlaceContentRequest) + +#include "tst_qplacecontentrequest.moc" diff --git a/tests/auto/qplacedetailsreply/qplacedetailsreply.pro b/tests/auto/qplacedetailsreply/qplacedetailsreply.pro new file mode 100644 index 0000000..05daa2f --- /dev/null +++ b/tests/auto/qplacedetailsreply/qplacedetailsreply.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacedetailsreply + +SOURCES += tst_qplacedetailsreply.cpp + +QT += location testlib diff --git a/tests/auto/qplacedetailsreply/tst_qplacedetailsreply.cpp b/tests/auto/qplacedetailsreply/tst_qplacedetailsreply.cpp new file mode 100644 index 0000000..5ea509b --- /dev/null +++ b/tests/auto/qplacedetailsreply/tst_qplacedetailsreply.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class TestDetailsReply : public QPlaceDetailsReply +{ + Q_OBJECT +public: + TestDetailsReply(QObject *parent) : QPlaceDetailsReply(parent){} + ~TestDetailsReply() {} + void setPlace(const QPlace &place) { QPlaceDetailsReply::setPlace(place); } +}; + +class tst_QPlaceDetailsReply : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceDetailsReply(); + +private Q_SLOTS: + void constructorTest(); + void typeTest(); + void placeTest(); +}; + +tst_QPlaceDetailsReply::tst_QPlaceDetailsReply() +{ +} + +void tst_QPlaceDetailsReply::constructorTest() +{ + TestDetailsReply *reply = new TestDetailsReply(this); + QVERIFY(reply->parent() == this); + delete reply; +} + +void tst_QPlaceDetailsReply::typeTest() +{ + TestDetailsReply *reply = new TestDetailsReply(this); + QCOMPARE(reply->type(), QPlaceReply::DetailsReply); + delete reply; +} + +void tst_QPlaceDetailsReply::placeTest() +{ + TestDetailsReply *reply = new TestDetailsReply(this); + QPlace place; + place.setName(QStringLiteral("Gotham City")); + reply->setPlace(place); + + QCOMPARE(reply->place(), place); + delete reply; +} + +QTEST_APPLESS_MAIN(tst_QPlaceDetailsReply) + +#include "tst_qplacedetailsreply.moc" diff --git a/tests/auto/qplaceeditorial/qplaceeditorial.pro b/tests/auto/qplaceeditorial/qplaceeditorial.pro new file mode 100644 index 0000000..703fa8b --- /dev/null +++ b/tests/auto/qplaceeditorial/qplaceeditorial.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaceeditorial + +SOURCES += tst_qplaceeditorial.cpp + +QT += location testlib diff --git a/tests/auto/qplaceeditorial/tst_qplaceeditorial.cpp b/tests/auto/qplaceeditorial/tst_qplaceeditorial.cpp new file mode 100644 index 0000000..1c20bd2 --- /dev/null +++ b/tests/auto/qplaceeditorial/tst_qplaceeditorial.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" + +QT_USE_NAMESPACE + +class tst_QPlaceEditorial : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceEditorial(); + + //needed for QLocationTestUtils::testConversion + QPlaceEditorial initialSubObject(); + bool checkType(const QPlaceContent &); + void detach(QPlaceContent *); + void setSubClassProperty(QPlaceEditorial *); + +private Q_SLOTS: + void constructorTest(); + void supplierTest(); + void textTest(); + void titleTest(); + void languageTest(); + void operatorsTest(); + void conversionTest(); +}; + +tst_QPlaceEditorial::tst_QPlaceEditorial() +{ +} + +QPlaceEditorial tst_QPlaceEditorial::initialSubObject() +{ + QPlaceUser user; + user.setName("user 1"); + user.setUserId("0001"); + + QPlaceSupplier supplier; + supplier.setName("supplier"); + supplier.setSupplierId("1"); + + QPlaceEditorial editorial; + editorial.setTitle("title"); + editorial.setText("text"); + editorial.setLanguage("en"); + editorial.setUser(user); + editorial.setSupplier(supplier); + editorial.setAttribution("attribution"); + + return editorial; +} + +bool tst_QPlaceEditorial::checkType(const QPlaceContent &content) +{ + return content.type() == QPlaceContent::EditorialType; +} + +void tst_QPlaceEditorial::detach(QPlaceContent *content) +{ + content->setAttribution("attribution"); +} + +void tst_QPlaceEditorial::setSubClassProperty(QPlaceEditorial * editorial) +{ + editorial->setTitle("new title"); +} +void tst_QPlaceEditorial::constructorTest() +{ + QPlaceEditorial testObj; + testObj.setText("testId"); + QPlaceEditorial *testObjPtr = new QPlaceEditorial(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceEditorial::supplierTest() +{ + QPlaceEditorial testObj; + QVERIFY2(testObj.supplier().supplierId() == QString(), "Wrong default value"); + QPlaceSupplier sup; + sup.setName("testName1"); + sup.setSupplierId("testId"); + testObj.setSupplier(sup); + QVERIFY2(testObj.supplier() == sup, "Wrong value returned"); +} + +void tst_QPlaceEditorial::textTest() +{ + QPlaceEditorial testObj; + QVERIFY2(testObj.text() == QString(), "Wrong default value"); + testObj.setText("testText"); + QVERIFY2(testObj.text() == "testText", "Wrong value returned"); +} + +void tst_QPlaceEditorial::titleTest() +{ + QPlaceEditorial testObj; + QVERIFY2(testObj.title() == QString(), "Wrong default value"); + testObj.setTitle("testText"); + QVERIFY2(testObj.title() == "testText", "Wrong value returned"); +} + +void tst_QPlaceEditorial::languageTest() +{ + QPlaceEditorial testObj; + QVERIFY2(testObj.language() == QString(), "Wrong default value"); + testObj.setLanguage("testText"); + QVERIFY2(testObj.language() == "testText", "Wrong value returned"); +} + +void tst_QPlaceEditorial::operatorsTest() +{ + QPlaceEditorial testObj; + testObj.setLanguage("testValue"); + QPlaceEditorial testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setText("testValue2"); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QPlaceEditorial::conversionTest() +{ + QLocationTestUtils::testConversion(this); +} +QTEST_APPLESS_MAIN(tst_QPlaceEditorial); + +#include "tst_qplaceeditorial.moc" diff --git a/tests/auto/qplaceimage/qplaceimage.pro b/tests/auto/qplaceimage/qplaceimage.pro new file mode 100644 index 0000000..b6a2805 --- /dev/null +++ b/tests/auto/qplaceimage/qplaceimage.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaceimage + +SOURCES += tst_qplaceimage.cpp + +QT += location testlib diff --git a/tests/auto/qplaceimage/tst_qplaceimage.cpp b/tests/auto/qplaceimage/tst_qplaceimage.cpp new file mode 100644 index 0000000..aa3f9df --- /dev/null +++ b/tests/auto/qplaceimage/tst_qplaceimage.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" + +QT_USE_NAMESPACE + +class tst_QPlaceImage : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceImage(); + + //needed for QLocationTestUtils::testConversion + QPlaceImage initialSubObject(); + bool checkType(const QPlaceContent &); + void detach(QPlaceContent *); + void setSubClassProperty(QPlaceImage *); + +private Q_SLOTS: + void constructorTest(); + void supplierTest(); + void idTest(); + void mimeTypeTest(); + void attributionTest(); + void operatorsTest(); + void conversionTest(); +}; + +tst_QPlaceImage::tst_QPlaceImage() +{ +} + +QPlaceImage tst_QPlaceImage::initialSubObject() +{ + QPlaceUser user; + user.setName("user 1"); + user.setUserId("0001"); + + QPlaceSupplier supplier; + supplier.setName("supplier"); + supplier.setSupplierId("1"); + + QPlaceImage image; + image.setUrl(QUrl(QStringLiteral("file:///opt/icon/img.png"))); + image.setImageId("0001"); + image.setMimeType("image/png"); + image.setUser(user); + image.setSupplier(supplier); + image.setAttribution("attribution"); + + return image; +} + +bool tst_QPlaceImage::checkType(const QPlaceContent &content) +{ + return content.type() == QPlaceContent::ImageType; +} + +void tst_QPlaceImage::detach(QPlaceContent *content) +{ + content->setAttribution("attribution"); +} + +void tst_QPlaceImage::setSubClassProperty(QPlaceImage *image) +{ + image->setImageId("0002"); +} + +void tst_QPlaceImage::constructorTest() +{ + QPlaceImage testObj; + testObj.setImageId("testId"); + QPlaceImage *testObjPtr = new QPlaceImage(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceImage::supplierTest() +{ + QPlaceImage testObj; + QVERIFY2(testObj.supplier().supplierId() == QString(), "Wrong default value"); + QPlaceSupplier sup; + sup.setName("testName1"); + sup.setSupplierId("testId"); + testObj.setSupplier(sup); + QVERIFY2(testObj.supplier() == sup, "Wrong value returned"); +} + +void tst_QPlaceImage::idTest() +{ + QPlaceImage testObj; + QVERIFY2(testObj.imageId() == QString(), "Wrong default value"); + testObj.setImageId("testText"); + QVERIFY2(testObj.imageId() == "testText", "Wrong value returned"); +} + +void tst_QPlaceImage::mimeTypeTest() +{ + QPlaceImage testObj; + QVERIFY2(testObj.mimeType() == QString(), "Wrong default value"); + testObj.setMimeType("testText"); + QVERIFY2(testObj.mimeType() == "testText", "Wrong value returned"); +} + +void tst_QPlaceImage::attributionTest() +{ + QPlaceImage image; + QVERIFY(image.attribution().isEmpty()); + image.setAttribution(QStringLiteral("Brought to you by acme")); + QCOMPARE(image.attribution(), QStringLiteral("Brought to you by acme")); + image.setAttribution(QString()); + QVERIFY(image.attribution().isEmpty()); +} + +void tst_QPlaceImage::operatorsTest() +{ + QPlaceImage testObj; + testObj.setMimeType("testValue"); + QPlaceImage testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setImageId("testValue2"); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QPlaceImage::conversionTest() +{ + QLocationTestUtils::testConversion(this); +} + +QTEST_APPLESS_MAIN(tst_QPlaceImage); + +#include "tst_qplaceimage.moc" diff --git a/tests/auto/qplacemanager/qplacemanager.pro b/tests/auto/qplacemanager/qplacemanager.pro new file mode 100644 index 0000000..7e7b25e --- /dev/null +++ b/tests/auto/qplacemanager/qplacemanager.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacemanager + +SOURCES += tst_qplacemanager.cpp + +CONFIG -= app_bundle + +QT += location testlib diff --git a/tests/auto/qplacemanager/tst_qplacemanager.cpp b/tests/auto/qplacemanager/tst_qplacemanager.cpp new file mode 100644 index 0000000..2cb035c --- /dev/null +++ b/tests/auto/qplacemanager/tst_qplacemanager.cpp @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include +#include + + +#ifndef WAIT_UNTIL +#define WAIT_UNTIL(__expr) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if (!(__expr)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + } while (0) +#endif + +QT_USE_NAMESPACE + +class tst_QPlaceManager : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void compatiblePlace(); + void testMetadata(); + void testLocales(); + void testMatchUnsupported(); + +private: + bool checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError); + + QGeoServiceProvider *provider; + QPlaceManager *placeManager; +}; + +void tst_QPlaceManager::initTestCase() +{ +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif + provider = 0; + + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + QVERIFY(providers.contains("qmlgeo.test.plugin")); + + provider = new QGeoServiceProvider("qmlgeo.test.plugin"); + provider->setAllowExperimental(true); + QCOMPARE(provider->placesFeatures() & QGeoServiceProvider::OfflinePlacesFeature, + QGeoServiceProvider::OfflinePlacesFeature); + placeManager = provider->placeManager(); + QVERIFY(placeManager); +} + +void tst_QPlaceManager::testMetadata() +{ + QCOMPARE(placeManager->managerName(), QStringLiteral("qmlgeo.test.plugin")); + QCOMPARE(placeManager->managerVersion(), 100); +} + +void tst_QPlaceManager::testLocales() +{ + QCOMPARE(placeManager->locales().count(), 1); + QCOMPARE(placeManager->locales().at(0), QLocale()); + + QLocale locale(QLocale::Norwegian, QLocale::Norway); + placeManager->setLocale(locale); + + QCOMPARE(placeManager->locales().at(0), locale); + + QList locales; + QLocale en_AU = QLocale(QLocale::English, QLocale::Australia); + QLocale en_UK = QLocale(QLocale::English, QLocale::UnitedKingdom); + locales << en_AU << en_UK; + placeManager->setLocales(locales); + QCOMPARE(placeManager->locales().count(), 2); + QCOMPARE(placeManager->locales().at(0), en_AU); + QCOMPARE(placeManager->locales().at(1), en_UK); +} + +void tst_QPlaceManager::testMatchUnsupported() +{ + QPlaceMatchRequest request; + QPlaceMatchReply *reply = placeManager->matchingPlaces(request); + QVERIFY(checkSignals(reply, QPlaceReply::UnsupportedError)); +} + +void tst_QPlaceManager::compatiblePlace() +{ + QPlace place; + place.setPlaceId(QStringLiteral("4-8-15-16-23-42")); + place.setName(QStringLiteral("Island")); + place.setVisibility(QLocation::PublicVisibility); + + QPlace compatPlace = placeManager->compatiblePlace(place); + QVERIFY(compatPlace.placeId().isEmpty()); + QCOMPARE(compatPlace.name(), QStringLiteral("Island")); + QCOMPARE(compatPlace.visibility(), QLocation::UnspecifiedVisibility); +} + +void tst_QPlaceManager::cleanupTestCase() +{ + delete provider; +} + +bool tst_QPlaceManager::checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError) +{ + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + QSignalSpy errorSpy(reply, SIGNAL(error(QPlaceReply::Error,QString))); + QSignalSpy managerFinishedSpy(placeManager, SIGNAL(finished(QPlaceReply*))); + QSignalSpy managerErrorSpy(placeManager,SIGNAL(error(QPlaceReply*,QPlaceReply::Error,QString))); + + if (expectedError != QPlaceReply::NoError) { + //check that we get an error signal from the reply + WAIT_UNTIL(errorSpy.count() == 1); + if (errorSpy.count() != 1) { + qWarning() << "Error signal for search operation not received"; + return false; + } + + //check that we get the correct error from the reply's signal + QPlaceReply::Error actualError = qvariant_cast(errorSpy.at(0).at(0)); + if (actualError != expectedError) { + qWarning() << "Actual error code in reply signal does not match expected error code"; + qWarning() << "Actual error code = " << actualError; + qWarning() << "Expected error coe =" << expectedError; + return false; + } + + //check that we get an error signal from the manager + WAIT_UNTIL(managerErrorSpy.count() == 1); + if (managerErrorSpy.count() !=1) { + qWarning() << "Error signal from manager for search operation not received"; + return false; + } + + //check that we get the correct reply instance in the error signal from the manager + if (qvariant_cast(managerErrorSpy.at(0).at(0)) != reply) { + qWarning() << "Reply instance in error signal from manager is incorrect"; + return false; + } + + //check that we get the correct error from the signal of the manager + actualError = qvariant_cast(managerErrorSpy.at(0).at(1)); + if (actualError != expectedError) { + qWarning() << "Actual error code from manager signal does not match expected error code"; + qWarning() << "Actual error code =" << actualError; + qWarning() << "Expected error code = " << expectedError; + return false; + } + } + + //check that we get a finished signal + WAIT_UNTIL(finishedSpy.count() == 1); + if (finishedSpy.count() !=1) { + qWarning() << "Finished signal from reply not received"; + return false; + } + + if (reply->error() != expectedError) { + qWarning() << "Actual error code does not match expected error code"; + qWarning() << "Actual error code: " << reply->error(); + qWarning() << "Expected error code" << expectedError; + return false; + } + + if (expectedError == QPlaceReply::NoError && !reply->errorString().isEmpty()) { + qWarning() << "Expected error was no error but error string was not empty"; + qWarning() << "Error string=" << reply->errorString(); + return false; + } + + //check that we get the finished signal from the manager + WAIT_UNTIL(managerFinishedSpy.count() == 1); + if (managerFinishedSpy.count() != 1) { + qWarning() << "Finished signal from manager not received"; + return false; + } + + //check that the reply instance in the finished signal from the manager is correct + if (qvariant_cast(managerFinishedSpy.at(0).at(0)) != reply) { + qWarning() << "Reply instance in finished signal from manager is incorrect"; + return false; + } + + return true; +} + +QTEST_GUILESS_MAIN(tst_QPlaceManager) + +#include "tst_qplacemanager.moc" diff --git a/tests/auto/qplacemanager_nokia/qplacemanager_nokia.pro b/tests/auto/qplacemanager_nokia/qplacemanager_nokia.pro new file mode 100644 index 0000000..41f5998 --- /dev/null +++ b/tests/auto/qplacemanager_nokia/qplacemanager_nokia.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qplacemanager_nokia + +SOURCES += tst_qplacemanager_nokia.cpp + +QT += location testlib + diff --git a/tests/auto/qplacemanager_nokia/tst_qplacemanager_nokia.cpp b/tests/auto/qplacemanager_nokia/tst_qplacemanager_nokia.cpp new file mode 100644 index 0000000..46dbc61 --- /dev/null +++ b/tests/auto/qplacemanager_nokia/tst_qplacemanager_nokia.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include +#include + +#ifndef WAIT_UNTIL +#define WAIT_UNTIL(__expr) \ + do { \ + const int __step = 50; \ + const int __timeout = 5000; \ + if (!(__expr)) { \ + QTest::qWait(0); \ + } \ + for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \ + QTest::qWait(__step); \ + } \ + } while (0) +#endif + +Q_DECLARE_METATYPE(QPlaceIdReply *); + +QT_USE_NAMESPACE + +class tst_QPlaceManagerNokia : public QObject +{ + Q_OBJECT +public: + tst_QPlaceManagerNokia(); + +private Q_SLOTS: + void initTestCase(); + void unsupportedFunctions(); + +private: + bool checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError); + QGeoServiceProvider *provider; + QPlaceManager *placeManager; +}; + +tst_QPlaceManagerNokia::tst_QPlaceManagerNokia() +{ +} + +void tst_QPlaceManagerNokia::initTestCase() +{ + qRegisterMetaType(); + + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + + QVariantMap params; + params.insert(QStringLiteral("here.app_id"), "stub"); + params.insert(QStringLiteral("here.token"), "stub"); + provider = new QGeoServiceProvider("here", params); + placeManager = provider->placeManager(); + QVERIFY(placeManager); +} + +void tst_QPlaceManagerNokia::unsupportedFunctions() +{ + QPlace place; + place.setName(QStringLiteral("Brisbane")); + QPlaceIdReply *savePlaceReply = placeManager->savePlace(place); + QVERIFY(savePlaceReply); + QVERIFY(checkSignals(savePlaceReply, QPlaceReply::UnsupportedError)); + QCOMPARE(savePlaceReply->operationType(), QPlaceIdReply::SavePlace); + + QPlaceIdReply *removePlaceReply = placeManager->removePlace(place.placeId()); + QVERIFY(removePlaceReply); + QVERIFY(checkSignals(removePlaceReply, QPlaceReply::UnsupportedError)); + QCOMPARE(removePlaceReply->operationType(), QPlaceIdReply::RemovePlace); + + QPlaceCategory category; + category.setName(QStringLiteral("Accommodation")); + QPlaceIdReply *saveCategoryReply = placeManager->saveCategory(category); + QVERIFY(saveCategoryReply); + QVERIFY(checkSignals(saveCategoryReply, QPlaceReply::UnsupportedError)); + QCOMPARE(saveCategoryReply->operationType(), QPlaceIdReply::SaveCategory); + + QPlaceIdReply *removeCategoryReply = placeManager->removeCategory(category.categoryId()); + QVERIFY(removeCategoryReply); + QVERIFY(checkSignals(removeCategoryReply, QPlaceReply::UnsupportedError)); + QCOMPARE(removeCategoryReply->operationType(), QPlaceIdReply::RemoveCategory); +} + +bool tst_QPlaceManagerNokia::checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError) +{ + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + QSignalSpy errorSpy(reply, SIGNAL(error(QPlaceReply::Error,QString))); + QSignalSpy managerFinishedSpy(placeManager, SIGNAL(finished(QPlaceReply*))); + QSignalSpy managerErrorSpy(placeManager,SIGNAL(error(QPlaceReply*,QPlaceReply::Error,QString))); + + if (expectedError != QPlaceReply::NoError) { + //check that we get an error signal from the reply + WAIT_UNTIL(errorSpy.count() == 1); + if (errorSpy.count() != 1) { + qWarning() << "Error signal for operation not received"; + return false; + } + + //check that we get the correct error from the reply's signal + QPlaceReply::Error actualError = qvariant_cast(errorSpy.at(0).at(0)); + if (actualError != expectedError) { + qWarning() << "Actual error code in reply signal does not match expected error code"; + qWarning() << "Actual error code = " << actualError; + qWarning() << "Expected error coe =" << expectedError; + return false; + } + + //check that we get an error signal from the manager + WAIT_UNTIL(managerErrorSpy.count() == 1); + if (managerErrorSpy.count() !=1) { + qWarning() << "Error signal from manager for search operation not received"; + return false; + } + + //check that we get the correct reply instance in the error signal from the manager + if (qvariant_cast(managerErrorSpy.at(0).at(0)) != reply) { + qWarning() << "Reply instance in error signal from manager is incorrect"; + return false; + } + + //check that we get the correct error from the signal of the manager + actualError = qvariant_cast(managerErrorSpy.at(0).at(1)); + if (actualError != expectedError) { + qWarning() << "Actual error code from manager signal does not match expected error code"; + qWarning() << "Actual error code =" << actualError; + qWarning() << "Expected error code = " << expectedError; + return false; + } + } + + //check that we get a finished signal + WAIT_UNTIL(finishedSpy.count() == 1); + if (finishedSpy.count() !=1) { + qWarning() << "Finished signal from reply not received"; + return false; + } + + if (reply->error() != expectedError) { + qWarning() << "Actual error code does not match expected error code"; + qWarning() << "Actual error code: " << reply->error(); + qWarning() << "Expected error code" << expectedError; + return false; + } + + if (expectedError == QPlaceReply::NoError && !reply->errorString().isEmpty()) { + qWarning() << "Expected error was no error but error string was not empty"; + qWarning() << "Error string=" << reply->errorString(); + return false; + } + + //check that we get the finished signal from the manager + WAIT_UNTIL(managerFinishedSpy.count() == 1); + if (managerFinishedSpy.count() != 1) { + qWarning() << "Finished signal from manager not received"; + return false; + } + + //check that the reply instance in the finished signal from the manager is correct + if (qvariant_cast(managerFinishedSpy.at(0).at(0)) != reply) { + qWarning() << "Reply instance in finished signal from manager is incorrect"; + return false; + } + + return true; +} + +QTEST_GUILESS_MAIN(tst_QPlaceManagerNokia) + +#include "tst_qplacemanager_nokia.moc" diff --git a/tests/auto/qplacemanager_unsupported/qplacemanager_unsupported.pro b/tests/auto/qplacemanager_unsupported/qplacemanager_unsupported.pro new file mode 100644 index 0000000..57898ca --- /dev/null +++ b/tests/auto/qplacemanager_unsupported/qplacemanager_unsupported.pro @@ -0,0 +1,9 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacemanager_unsupported + +SOURCES += tst_qplacemanager_unsupported.cpp + +CONFIG -= app_bundle + +QT += location testlib diff --git a/tests/auto/qplacemanager_unsupported/tst_qplacemanager_unsupported.cpp b/tests/auto/qplacemanager_unsupported/tst_qplacemanager_unsupported.cpp new file mode 100644 index 0000000..98a9dd3 --- /dev/null +++ b/tests/auto/qplacemanager_unsupported/tst_qplacemanager_unsupported.cpp @@ -0,0 +1,279 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QPlaceManagerUnsupported : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + + void testMetadata(); + void testLocales(); + + void testGetPlaceDetails(); + void testGetPlaceContent(); + void testSearch(); + void testSearchSuggestions(); + + void testSavePlace(); + void testRemovePlace(); + void testSaveCategory(); + void testRemoveCategory(); + + void testCategories(); + + void compatiblePlace(); + + void testMatchUnsupported(); + +private: + void checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError, bool *failed); + bool checkSignals(QPlaceReply *reply, QPlaceReply::Error expectedError); + + QGeoServiceProvider *m_provider; + QPlaceManager *m_manager; +}; + +void tst_QPlaceManagerUnsupported::initTestCase() +{ +#if QT_CONFIG(library) + /* + * Set custom path since CI doesn't install test plugins + */ +#ifdef Q_OS_WIN + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../../plugins")); +#else + QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath() + + QStringLiteral("/../../../plugins")); +#endif +#endif + + m_provider = 0; + m_manager = 0; + + QStringList providers = QGeoServiceProvider::availableServiceProviders(); + QVERIFY(providers.contains("test.places.unsupported")); + + m_provider = new QGeoServiceProvider("test.places.unsupported"); + QVERIFY(m_provider); + QCOMPARE(m_provider->error(), QGeoServiceProvider::NotSupportedError); + m_provider->setAllowExperimental(true); + QCOMPARE(m_provider->error(), QGeoServiceProvider::NoError); + + m_manager = m_provider->placeManager(); + QVERIFY(m_manager); +} + +void tst_QPlaceManagerUnsupported::cleanupTestCase() +{ + delete m_provider; +} + +void tst_QPlaceManagerUnsupported::testMetadata() +{ + QCOMPARE(m_manager->managerName(), QStringLiteral("test.places.unsupported")); + QCOMPARE(m_manager->managerVersion(), 1); + QCOMPARE(m_provider->placesFeatures(), QGeoServiceProvider::NoPlacesFeatures); +} + +void tst_QPlaceManagerUnsupported::testLocales() +{ + QVERIFY(m_manager->locales().isEmpty()); + + QLocale locale(QLocale::Norwegian, QLocale::Norway); + m_manager->setLocale(locale); + + QVERIFY(m_manager->locales().isEmpty()); + + QList locales; + QLocale en_AU = QLocale(QLocale::English, QLocale::Australia); + QLocale en_UK = QLocale(QLocale::English, QLocale::UnitedKingdom); + locales << en_AU << en_UK; + m_manager->setLocales(locales); + + QVERIFY(m_manager->locales().isEmpty()); +} + +void tst_QPlaceManagerUnsupported::testGetPlaceDetails() +{ + QPlaceDetailsReply *reply = m_manager->getPlaceDetails(QString()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testGetPlaceContent() +{ + QPlaceContentReply *reply = m_manager->getPlaceContent(QPlaceContentRequest()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testSearch() +{ + QPlaceSearchReply *reply = m_manager->search(QPlaceSearchRequest()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testSearchSuggestions() +{ + QPlaceSearchSuggestionReply *reply = m_manager->searchSuggestions(QPlaceSearchRequest()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testSavePlace() +{ + QPlaceIdReply *reply = m_manager->savePlace(QPlace()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testRemovePlace() +{ + QPlaceIdReply *reply = m_manager->removePlace(QString()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testSaveCategory() +{ + QPlaceIdReply *reply = m_manager->saveCategory(QPlaceCategory()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testRemoveCategory() +{ + QPlaceIdReply *reply = m_manager->removeCategory(QString()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::testCategories() +{ + QPlaceReply *reply = m_manager->initializeCategories(); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; + + QVERIFY(m_manager->childCategoryIds().isEmpty()); + QVERIFY(m_manager->parentCategoryId(QString()).isEmpty()); + QCOMPARE(m_manager->category(QString()), QPlaceCategory()); +} + +void tst_QPlaceManagerUnsupported::compatiblePlace() +{ + QPlace place; + place.setPlaceId(QStringLiteral("4-8-15-16-23-42")); + place.setName(QStringLiteral("Island")); + place.setVisibility(QLocation::PublicVisibility); + + QPlace compatPlace = m_manager->compatiblePlace(place); + QCOMPARE(compatPlace, QPlace()); +} + +void tst_QPlaceManagerUnsupported::testMatchUnsupported() +{ + QPlaceMatchReply *reply = m_manager->matchingPlaces(QPlaceMatchRequest()); + if (!checkSignals(reply, QPlaceReply::UnsupportedError)) + return; +} + +void tst_QPlaceManagerUnsupported::checkSignals(QPlaceReply *reply, + QPlaceReply::Error expectedError, bool *failed) +{ + *failed = true; + + QSignalSpy finishedSpy(reply, SIGNAL(finished())); + QSignalSpy errorSpy(reply, SIGNAL(error(QPlaceReply::Error,QString))); + QSignalSpy managerFinishedSpy(m_manager, SIGNAL(finished(QPlaceReply*))); + QSignalSpy managerErrorSpy(m_manager,SIGNAL(error(QPlaceReply*,QPlaceReply::Error,QString))); + + if (expectedError != QPlaceReply::NoError) { + //check that we get an error signal from the reply + QTRY_VERIFY(errorSpy.count() == 1); + + //check that we get the correct error from the reply's signal + QPlaceReply::Error actualError = qvariant_cast(errorSpy.at(0).at(0)); + QCOMPARE(actualError, expectedError); + + //check that we get an error signal from the manager + QTRY_VERIFY(managerErrorSpy.count() == 1); + + //check that we get the correct reply instance in the error signal from the manager + QPlaceReply *managerReply = qvariant_cast(managerErrorSpy.at(0).at(0)); + QCOMPARE(managerReply, reply); + + //check that we get the correct error from the signal of the manager + actualError = qvariant_cast(managerErrorSpy.at(0).at(1)); + QCOMPARE(actualError, expectedError); + } + + //check that we get a finished signal + QTRY_VERIFY(finishedSpy.count() == 1); + + QCOMPARE(reply->error(), expectedError); + + QCOMPARE(reply->errorString().isEmpty(), expectedError == QPlaceReply::NoError); + + //check that we get the finished signal from the manager + QTRY_VERIFY(managerFinishedSpy.count() == 1); + + //check that the reply instance in the finished signal from the manager is correct + QPlaceReply *managerReply = qvariant_cast(managerFinishedSpy.at(0).at(0)); + QCOMPARE(managerReply, reply); + + *failed = false; +} + +bool tst_QPlaceManagerUnsupported::checkSignals(QPlaceReply *reply, + QPlaceReply::Error expectedError) +{ + bool failed; + checkSignals(reply, expectedError, &failed); + return failed; +} + +QTEST_GUILESS_MAIN(tst_QPlaceManagerUnsupported) + +#include "tst_qplacemanager_unsupported.moc" diff --git a/tests/auto/qplacematchreply/qplacematchreply.pro b/tests/auto/qplacematchreply/qplacematchreply.pro new file mode 100644 index 0000000..00c1dcd --- /dev/null +++ b/tests/auto/qplacematchreply/qplacematchreply.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacematchreply + +SOURCES += tst_qplacematchreply.cpp + +QT += location testlib diff --git a/tests/auto/qplacematchreply/tst_qplacematchreply.cpp b/tests/auto/qplacematchreply/tst_qplacematchreply.cpp new file mode 100644 index 0000000..34ee7ba --- /dev/null +++ b/tests/auto/qplacematchreply/tst_qplacematchreply.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class TestMatchReply : public QPlaceMatchReply +{ + Q_OBJECT +public: + TestMatchReply(QObject *parent) : QPlaceMatchReply(parent) {} + TestMatchReply() {} + + void setPlaces(const QList &places) { + QPlaceMatchReply::setPlaces(places); + } + + void setRequest(const QPlaceMatchRequest &request) { + QPlaceMatchReply::setRequest(request); + } +}; + +class tst_QPlaceMatchReply : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceMatchReply(); + +private Q_SLOTS: + void constructorTest(); + void typeTest(); + void requestTest(); +// void resultsTest(); +}; + +tst_QPlaceMatchReply::tst_QPlaceMatchReply() +{ +} + +void tst_QPlaceMatchReply::constructorTest() +{ + QPlaceMatchReply *reply = new TestMatchReply(this); + QVERIFY(reply->parent() == this); + delete reply; +} + +void tst_QPlaceMatchReply::typeTest() +{ + TestMatchReply *reply = new TestMatchReply(this); + QVERIFY(reply->type() == QPlaceReply::MatchReply); + delete reply; +} + +void tst_QPlaceMatchReply::requestTest() +{ + TestMatchReply *reply = new TestMatchReply(this); + QPlaceMatchRequest request; + + QPlace place1; + place1.setName(QStringLiteral("place1")); + + QPlace place2; + place2.setName(QStringLiteral("place2")); + + QList places; + places << place1 << place2; + + request.setPlaces(places); + + reply->setRequest(request); + QCOMPARE(reply->request(), request); + + reply->setRequest(QPlaceMatchRequest()); + QCOMPARE(reply->request(), QPlaceMatchRequest()); + delete reply; +} + + +QTEST_APPLESS_MAIN(tst_QPlaceMatchReply) + +#include "tst_qplacematchreply.moc" diff --git a/tests/auto/qplacematchrequest/qplacematchrequest.pro b/tests/auto/qplacematchrequest/qplacematchrequest.pro new file mode 100644 index 0000000..558cd2e --- /dev/null +++ b/tests/auto/qplacematchrequest/qplacematchrequest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacematchrequest +SOURCES += tst_qplacematchrequest.cpp + +QT += location testlib diff --git a/tests/auto/qplacematchrequest/tst_qplacematchrequest.cpp b/tests/auto/qplacematchrequest/tst_qplacematchrequest.cpp new file mode 100644 index 0000000..03ba59a --- /dev/null +++ b/tests/auto/qplacematchrequest/tst_qplacematchrequest.cpp @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include + +QT_USE_NAMESPACE + +class tst_QPlaceMatchRequest : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceMatchRequest(); + +private Q_SLOTS: + void constructorTest(); + void placesTest(); + void resultsTest(); + void parametersTest(); + void clearTest(); +}; + +tst_QPlaceMatchRequest::tst_QPlaceMatchRequest() +{ +} + +void tst_QPlaceMatchRequest::constructorTest() +{ + QPlaceMatchRequest request; + QVariantMap params; + params.insert(QStringLiteral("key"), QStringLiteral("val")); + + QPlace place1; + place1.setName(QStringLiteral("place1")); + + QPlace place2; + place2.setName(QStringLiteral("place2")); + + QList places; + places << place1 << place2; + + request.setPlaces(places); + request.setParameters(params); + + QPlaceMatchRequest copy(request); + QCOMPARE(copy, request); + QCOMPARE(copy.places(), places); + QCOMPARE(copy.parameters(), params); +} + +void tst_QPlaceMatchRequest::placesTest() +{ + QPlaceMatchRequest request; + QCOMPARE(request.places().count(), 0); + + QPlace place1; + place1.setName(QStringLiteral("place1")); + + QPlace place2; + place2.setName(QStringLiteral("place2")); + + QList places; + places << place1 << place2; + + request.setPlaces(places); + QCOMPARE(request.places(), places); + + request.setPlaces(QList()); + QCOMPARE(request.places().count(), 0); +} + +void tst_QPlaceMatchRequest::resultsTest() +{ + QPlaceMatchRequest request; + QCOMPARE(request.places().count(), 0); + + QPlace place1; + place1.setName(QStringLiteral("place1")); + QPlaceResult result1; + result1.setPlace(place1); + + QPlace place2; + place2.setName(QStringLiteral("place2")); + QPlaceResult result2; + result2.setPlace(place2); + + QList results; + results << result1 << result2; + + request.setResults(results); + + QCOMPARE(request.places().count(), 2); + QCOMPARE(request.places().at(0), place1); + QCOMPARE(request.places().at(1), place2); + + request.setResults(QList()); + QCOMPARE(request.places().count(), 0); +} + +void tst_QPlaceMatchRequest::parametersTest() +{ + QPlaceMatchRequest request; + QVERIFY(request.parameters().isEmpty()); + + QVariantMap params; + params.insert(QStringLiteral("key"), QStringLiteral("value")); + + request.setParameters(params); + QCOMPARE(request.parameters(), params); +} + +void tst_QPlaceMatchRequest::clearTest() +{ + QPlaceMatchRequest request; + QVariantMap params; + params.insert(QStringLiteral("key"), QStringLiteral("value")); + + QPlace place1; + place1.setName(QStringLiteral("place1")); + + QPlace place2; + place2.setName(QStringLiteral("place2")); + + QList places; + places << place1 << place2; + + request.setPlaces(places); + request.setParameters(params); + + request.clear(); + QVERIFY(request.places().isEmpty()); + QVERIFY(request.parameters().isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QPlaceMatchRequest) + +#include "tst_qplacematchrequest.moc" diff --git a/tests/auto/qplaceperiod/qplaceperiod.pro b/tests/auto/qplaceperiod/qplaceperiod.pro new file mode 100644 index 0000000..c680a75 --- /dev/null +++ b/tests/auto/qplaceperiod/qplaceperiod.pro @@ -0,0 +1,8 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaceperiod + +SOURCES += tst_qplaceperiod.cpp + +QT += location testlib +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/qplaceperiod/tst_qplaceperiod.cpp b/tests/auto/qplaceperiod/tst_qplaceperiod.cpp new file mode 100644 index 0000000..78db546 --- /dev/null +++ b/tests/auto/qplaceperiod/tst_qplaceperiod.cpp @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QPlacePeriod : public QObject +{ + Q_OBJECT + +public: + tst_QPlacePeriod(); + +private Q_SLOTS: + void constructorTest(); + void startDateTest(); + void startTimeTest(); + void endDateTest(); + void endTimeTest(); + void operatorsTest(); +}; + +tst_QPlacePeriod::tst_QPlacePeriod() +{ +} + +void tst_QPlacePeriod::constructorTest() +{ + QPlacePeriod testObj; + testObj.setStartTime(QTime::currentTime()); + QPlacePeriod *testObjPtr = new QPlacePeriod(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(testObjPtr->startTime() == testObj.startTime(), "Copy constructor - start time"); + delete testObjPtr; +} + +void tst_QPlacePeriod::startDateTest() +{ + QPlacePeriod testObj; + QVERIFY2(testObj.startDate().isNull() == true, "Wrong default value"); + QDate date = QDate::currentDate(); + testObj.setStartDate(date); + QVERIFY2(testObj.startDate() == date, "Wrong value returned"); +} + +void tst_QPlacePeriod::startTimeTest() +{ + QPlacePeriod testObj; + QVERIFY2(testObj.startTime().isNull() == true, "Wrong default value"); + QTime time = QTime::currentTime(); + testObj.setStartTime(time); + QVERIFY2(testObj.startTime() == time, "Wrong value returned"); +} + +void tst_QPlacePeriod::endDateTest() +{ + QPlacePeriod testObj; + QVERIFY2(testObj.endDate().isNull() == true, "Wrong default value"); + QDate date = QDate::currentDate(); + testObj.setEndDate(date); + QVERIFY2(testObj.endDate() == date, "Wrong value returned"); +} + +void tst_QPlacePeriod::endTimeTest() +{ + QPlacePeriod testObj; + QVERIFY2(testObj.endTime().isNull() == true, "Wrong default value"); + QTime time = QTime::currentTime(); + testObj.setEndTime(time); + QVERIFY2(testObj.endTime() == time, "Wrong value returned"); +} + +void tst_QPlacePeriod::operatorsTest() +{ + QPlacePeriod testObj; + QTime time = QTime::currentTime(); + testObj.setEndTime(time); + QPlacePeriod testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj.setEndTime(QTime::currentTime().addSecs(102021)); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +QTEST_APPLESS_MAIN(tst_QPlacePeriod); + +#include "tst_qplaceperiod.moc" diff --git a/tests/auto/qplaceratings/qplaceratings.pro b/tests/auto/qplaceratings/qplaceratings.pro new file mode 100644 index 0000000..b17b6aa --- /dev/null +++ b/tests/auto/qplaceratings/qplaceratings.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaceratings + +SOURCES += tst_qplaceratings.cpp + +QT += location testlib diff --git a/tests/auto/qplaceratings/tst_qplaceratings.cpp b/tests/auto/qplaceratings/tst_qplaceratings.cpp new file mode 100644 index 0000000..4de19dc --- /dev/null +++ b/tests/auto/qplaceratings/tst_qplaceratings.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QPlaceRatings : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceRatings(); + +private Q_SLOTS: + void constructorTest(); + void averageTest(); + void countTest(); + void operatorsTest(); + void isEmptyTest(); +}; + +tst_QPlaceRatings::tst_QPlaceRatings() +{ +} + +void tst_QPlaceRatings::constructorTest() +{ + QPlaceRatings testObj; + Q_UNUSED(testObj); + + QPlaceRatings *testObjPtr = new QPlaceRatings(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(testObjPtr->count() == 0, "Copy constructor - wrong count"); + QVERIFY2(testObjPtr->average() == 0, "Copy constructor - wrong average"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceRatings::averageTest() +{ + QPlaceRatings testObj; + QVERIFY2(qFuzzyCompare(testObj.average(), 0) , "Wrong default average"); + testObj.setAverage(-10.23); + QCOMPARE(testObj.average(), -10.23); +} + +void tst_QPlaceRatings::countTest() +{ + QPlaceRatings testObj; + QVERIFY2(testObj.count() == 0, "Wrong default value"); + testObj.setCount(-1002); + QVERIFY2(testObj.count() == -1002, "Wrong value returned"); +} + +void tst_QPlaceRatings::operatorsTest() +{ + QPlaceRatings testObj; + testObj.setAverage(0.123); + QPlaceRatings testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setCount(-10); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QPlaceRatings::isEmptyTest() +{ + QPlaceRatings ratings; + + QVERIFY(ratings.isEmpty()); + + ratings.setCount(1); + QVERIFY(!ratings.isEmpty()); + ratings.setCount(0); + QVERIFY(ratings.isEmpty()); + + ratings.setMaximum(1); + QVERIFY(!ratings.isEmpty()); + ratings.setMaximum(0); + QVERIFY(ratings.isEmpty()); + + ratings.setAverage(1); + QVERIFY(!ratings.isEmpty()); + ratings.setAverage(0); + QVERIFY(ratings.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QPlaceRatings); + +#include "tst_qplaceratings.moc" diff --git a/tests/auto/qplacereply/qplacereply.pro b/tests/auto/qplacereply/qplacereply.pro new file mode 100644 index 0000000..3d18e71 --- /dev/null +++ b/tests/auto/qplacereply/qplacereply.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacereply + +SOURCES += tst_qplacereply.cpp + +QT += location testlib diff --git a/tests/auto/qplacereply/tst_qplacereply.cpp b/tests/auto/qplacereply/tst_qplacereply.cpp new file mode 100644 index 0000000..a67028d --- /dev/null +++ b/tests/auto/qplacereply/tst_qplacereply.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class TestReply : public QPlaceReply +{ +public: + TestReply(QObject *parent) : QPlaceReply(parent) {} + void setFinished(bool finished) { QPlaceReply::setFinished(finished); } + void setError(QPlaceReply::Error error, const QString &errorString) { + QPlaceReply::setError(error,errorString); + } +}; + +class tst_QPlaceReply : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceReply(); + +private Q_SLOTS: + void constructorTest(); + void typeTest(); + void finishedTest(); + void errorTest(); +}; + +tst_QPlaceReply::tst_QPlaceReply() +{ + +} + +void tst_QPlaceReply::constructorTest() +{ + TestReply *reply = new TestReply(this); + QCOMPARE(reply->parent(), this); + delete reply; +} + +void tst_QPlaceReply::typeTest() +{ + TestReply *reply = new TestReply(this); + QCOMPARE(reply->type(), QPlaceReply::Reply); + + delete reply; +} + +void tst_QPlaceReply::finishedTest() +{ + TestReply *reply = new TestReply(this); + QCOMPARE(reply->isFinished(), false); + reply->setFinished(true); + QCOMPARE(reply->isFinished(), true); + + delete reply; +} + +void tst_QPlaceReply::errorTest() +{ + TestReply *reply = new TestReply(this); + QCOMPARE(reply->error(), QPlaceReply::NoError); + QCOMPARE(reply->errorString(), QString()); + + reply->setError(QPlaceReply::CommunicationError, QStringLiteral("Could not connect to server")); + QCOMPARE(reply->error(), QPlaceReply::CommunicationError); + QCOMPARE(reply->errorString(), QStringLiteral("Could not connect to server")); + + reply->setError(QPlaceReply::NoError, QString()); + QCOMPARE(reply->error(), QPlaceReply::NoError); + QCOMPARE(reply->errorString(), QString()); + + delete reply; +} + +QTEST_APPLESS_MAIN(tst_QPlaceReply) + +#include "tst_qplacereply.moc" diff --git a/tests/auto/qplaceresult/qplaceresult.pro b/tests/auto/qplaceresult/qplaceresult.pro new file mode 100644 index 0000000..966c240 --- /dev/null +++ b/tests/auto/qplaceresult/qplaceresult.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaceresult + +SOURCES += tst_qplaceresult.cpp + +QT += location testlib diff --git a/tests/auto/qplaceresult/tst_qplaceresult.cpp b/tests/auto/qplaceresult/tst_qplaceresult.cpp new file mode 100644 index 0000000..4074d0d --- /dev/null +++ b/tests/auto/qplaceresult/tst_qplaceresult.cpp @@ -0,0 +1,270 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" + +QT_USE_NAMESPACE + +class tst_QPlaceResult : public QObject +{ + Q_OBJECT + +public: + QPlaceResult initialSubObject(); + bool checkType(const QPlaceSearchResult &); + void detach(QPlaceSearchResult *); + void setSubClassProperty(QPlaceResult *); + +private Q_SLOTS: + void constructorTest(); + void title(); + void icon(); + void distance(); + void place(); + void sponsored(); + void conversion(); +}; + +QPlaceResult tst_QPlaceResult::initialSubObject() +{ + QPlaceResult placeResult; + placeResult.setTitle(QStringLiteral("title")); + + QPlaceIcon icon; + QVariantMap parameters; + parameters.insert(QPlaceIcon::SingleUrl, + QUrl(QStringLiteral("file:///opt/icons/icon.png"))); + icon.setParameters(parameters); + placeResult.setIcon(icon); + + QPlace place; + place.setName(QStringLiteral("place")); + placeResult.setPlace(place); + + placeResult.setDistance(5); + placeResult.setSponsored(true); + + return placeResult; +} + +bool tst_QPlaceResult::checkType(const QPlaceSearchResult &result) +{ + return result.type() == QPlaceSearchResult::PlaceResult; +} + +void tst_QPlaceResult::detach(QPlaceSearchResult * result) +{ + result->setTitle("title"); +} + +void tst_QPlaceResult::setSubClassProperty(QPlaceResult *result) +{ + result->setSponsored(false); +} + +void tst_QPlaceResult::constructorTest() +{ + QPlaceResult result; + QCOMPARE(result.type(), QPlaceSearchResult::PlaceResult); + + result.setTitle(QStringLiteral("title")); + + QPlaceIcon icon; + QVariantMap parameters; + parameters.insert(QStringLiteral("paramKey"), QStringLiteral("paramValue")); + icon.setParameters(parameters); + result.setIcon(icon); + + QPlace place; + place.setName("place"); + result.setPlace(place); + + result.setDistance(500); + result.setSponsored(true); + + //check copy constructor + QPlaceResult result2(result); + QCOMPARE(result2.title(), QStringLiteral("title")); + QCOMPARE(result2.icon(), icon); + QCOMPARE(result2.place(), place); + QVERIFY(qFuzzyCompare(result.distance(), 500)); + QCOMPARE(result2.isSponsored(), true); + + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + //check results are the same after detachment of underlying shared data pointer + result2.setTitle("title"); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + //check construction of SearchResult using a PlaceResult + QPlaceSearchResult searchResult(result); + QCOMPARE(searchResult.title(), QStringLiteral("title")); + QCOMPARE(searchResult.icon(), icon); + QVERIFY(QLocationTestUtils::compareEquality(searchResult, result)); + QVERIFY(searchResult.type() == QPlaceSearchResult::PlaceResult); + result2 = searchResult; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + //check construction of a SearchResult using a SearchResult + //that is actually a PlaceResult underneath + QPlaceSearchResult searchResult2(searchResult); + QCOMPARE(searchResult2.title(), QStringLiteral("title")); + QCOMPARE(searchResult2.icon(), icon); + QVERIFY(QLocationTestUtils::compareEquality(searchResult2, result)); + QVERIFY(QLocationTestUtils::compareEquality(searchResult, searchResult2)); + QVERIFY(searchResult2.type() == QPlaceSearchResult::PlaceResult); + result2 = searchResult2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceResult::title() +{ + QPlaceResult result; + QVERIFY(result.title().isEmpty()); + + result.setTitle(QStringLiteral("title")); + QCOMPARE(result.title(), QStringLiteral("title")); + + result.setTitle(QString()); + QVERIFY(result.title().isEmpty()); + + QPlaceResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setTitle("title"); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setTitle("title"); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceResult::icon() +{ + QPlaceResult result; + QVERIFY(result.icon().isEmpty()); + + QPlaceIcon icon; + QVariantMap iconParams; + iconParams.insert(QStringLiteral("paramKey"), QStringLiteral("paramValue")); + icon.setParameters(iconParams); + result.setIcon(icon); + QCOMPARE(result.icon(), icon); + + result.setIcon(QPlaceIcon()); + QVERIFY(result.icon().isEmpty()); + + QPlaceResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setIcon(icon); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setIcon(icon); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceResult::distance() +{ + QPlaceResult result; + QVERIFY(qIsNaN(result.distance())); + + result.setDistance(3.14); + QVERIFY(qFuzzyCompare(result.distance(), 3.14)); + + result.setDistance(qQNaN()); + QVERIFY(qIsNaN(result.distance())); + + QPlaceResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setDistance(3.14); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setDistance(3.14); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceResult::place() +{ + QPlaceResult result; + QCOMPARE(result.place(), QPlace()); + + QPlace place; + place.setName("place"); + result.setPlace (place); + QCOMPARE(result.place(), place); + + result.setPlace(QPlace()); + QCOMPARE(result.place(), QPlace()); + + QPlaceResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setPlace(place); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setPlace(place); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceResult::sponsored() +{ + QPlaceResult result; + QCOMPARE(result.isSponsored(), false); + + result.setSponsored(true); + QCOMPARE(result.isSponsored(), true); + + result.setSponsored(false); + QCOMPARE(result.isSponsored(), false); + + QPlaceResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setSponsored(true); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setSponsored(true); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceResult::conversion() +{ + QLocationTestUtils::testConversion(this); +} + +QTEST_APPLESS_MAIN(tst_QPlaceResult) + +#include "tst_qplaceresult.moc" diff --git a/tests/auto/qplacereview/qplacereview.pro b/tests/auto/qplacereview/qplacereview.pro new file mode 100644 index 0000000..950e7a3 --- /dev/null +++ b/tests/auto/qplacereview/qplacereview.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacereview + +SOURCES += tst_qplacereview.cpp + +QT += location testlib diff --git a/tests/auto/qplacereview/tst_qplacereview.cpp b/tests/auto/qplacereview/tst_qplacereview.cpp new file mode 100644 index 0000000..7f7edfe --- /dev/null +++ b/tests/auto/qplacereview/tst_qplacereview.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" + +QT_USE_NAMESPACE + +class tst_QPlaceReview : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceReview(); + + //needed for QLocationTestUtils::testConversion + QPlaceReview initialSubObject(); + bool checkType(const QPlaceContent &); + void detach(QPlaceContent *); + void setSubClassProperty(QPlaceReview *); + +private Q_SLOTS: + void constructorTest(); + void supplierTest(); + void dateTest(); + void textTest(); + void languageTest(); + void ratingTest(); + void reviewIdTest(); + void titleTest(); + void userTest(); + void operatorsTest(); + void conversionTest(); +}; + +tst_QPlaceReview::tst_QPlaceReview() +{ +} + +QPlaceReview tst_QPlaceReview::initialSubObject() +{ + QPlaceUser user; + user.setName("user 1"); + user.setUserId("0001"); + + QPlaceSupplier supplier; + supplier.setName("supplier"); + supplier.setSupplierId("1"); + + QPlaceReview review; + review.setTitle("title"); + review.setText("text"); + review.setRating(4.5); + review.setLanguage("en"); + review.setDateTime(QDateTime::fromString("01:02 03/04/2000", + "hh:mm dd/MM/yyyy")); + review.setUser(user); + review.setSupplier(supplier); + review.setAttribution("attribution"); + + return review; +} + +bool tst_QPlaceReview::checkType(const QPlaceContent &content) +{ + return content.type() == QPlaceContent::ReviewType; +} + +void tst_QPlaceReview::detach(QPlaceContent *content) +{ + content->setAttribution("attribution"); +} + +void tst_QPlaceReview::setSubClassProperty(QPlaceReview *review) +{ + review->setTitle("new title"); +} + +void tst_QPlaceReview::constructorTest() +{ + QPlaceReview testObj; + testObj.setLanguage("testId"); + QPlaceReview *testObjPtr = new QPlaceReview(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceReview::supplierTest() +{ + QPlaceReview testObj; + QVERIFY2(testObj.supplier().supplierId() == QString(), "Wrong default value"); + QPlaceSupplier sup; + sup.setName("testName1"); + sup.setSupplierId("testId"); + testObj.setSupplier(sup); + QVERIFY2(testObj.supplier() == sup, "Wrong value returned"); +} + +void tst_QPlaceReview::dateTest() +{ + QPlaceReview testObj; + QCOMPARE(testObj.dateTime(), QDateTime()); + + QDateTime dt = QDateTime::currentDateTime(); + testObj.setDateTime(dt); + QCOMPARE(testObj.dateTime(), dt); +} + +void tst_QPlaceReview::textTest() +{ + QPlaceReview testObj; + QVERIFY2(testObj.text() == QString(), "Wrong default value"); + testObj.setText("testText"); + QVERIFY2(testObj.text() == "testText", "Wrong value returned"); +} + +void tst_QPlaceReview::languageTest() +{ + QPlaceReview testObj; + QVERIFY2(testObj.language() == QString(), "Wrong default value"); + testObj.setLanguage("testText"); + QVERIFY2(testObj.language() == "testText", "Wrong value returned"); +} + +void tst_QPlaceReview::ratingTest() +{ + QPlaceReview testObj; + QVERIFY2(testObj.rating() == 0, "Wrong default value"); + testObj.setRating(-10); + QCOMPARE(testObj.rating(), -10.0); + testObj.setRating(3.4); + QCOMPARE(testObj.rating(), 3.4); +} + +void tst_QPlaceReview::operatorsTest() +{ + QPlaceReview testObj; + testObj.setText("testValue"); + QPlaceReview testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setLanguage("testValue2"); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QPlaceReview::reviewIdTest() +{ + QPlaceReview testObj; + QVERIFY2(testObj.reviewId() == QString(), "Wrong default value"); + testObj.setReviewId("testText"); + QVERIFY2(testObj.reviewId() == "testText", "Wrong value returned"); +} +void tst_QPlaceReview::titleTest() +{ + QPlaceReview testObj; + QVERIFY2(testObj.title() == QString(), "Wrong default value"); + testObj.setTitle("testText"); + QVERIFY2(testObj.title() == "testText", "Wrong value returned"); +} + +void tst_QPlaceReview::userTest() +{ + QPlaceReview review; + QVERIFY(review.user().userId().isEmpty()); + QVERIFY(review.user().name().isEmpty()); + QPlaceUser user; + user.setUserId(QStringLiteral("11111")); + user.setName(QStringLiteral("Bob")); + + review.setUser(user); + QCOMPARE(review.user().userId(), QStringLiteral("11111")); + QCOMPARE(review.user().name(), QStringLiteral("Bob")); + + review.setUser(QPlaceUser()); + QVERIFY(review.user().userId().isEmpty()); + QVERIFY(review.user().name().isEmpty()); +} + +void tst_QPlaceReview::conversionTest() +{ + QLocationTestUtils::testConversion(this); +} + +QTEST_APPLESS_MAIN(tst_QPlaceReview) + +#include "tst_qplacereview.moc" diff --git a/tests/auto/qplacesearchreply/qplacesearchreply.pro b/tests/auto/qplacesearchreply/qplacesearchreply.pro new file mode 100644 index 0000000..8a57839 --- /dev/null +++ b/tests/auto/qplacesearchreply/qplacesearchreply.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacesearchreply + +SOURCES += tst_qplacesearchreply.cpp + +QT += location testlib diff --git a/tests/auto/qplacesearchreply/tst_qplacesearchreply.cpp b/tests/auto/qplacesearchreply/tst_qplacesearchreply.cpp new file mode 100644 index 0000000..14c951e --- /dev/null +++ b/tests/auto/qplacesearchreply/tst_qplacesearchreply.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include +#include +#include +#include + + +QT_USE_NAMESPACE + +class TestSearchReply : public QPlaceSearchReply +{ + Q_OBJECT +public: + TestSearchReply(QObject *parent) : QPlaceSearchReply(parent) {} + TestSearchReply() {} + + void setResults(const QList &results) { + QPlaceSearchReply::setResults(results); + } + + void setRequest(const QPlaceSearchRequest &request) { + QPlaceSearchReply::setRequest(request); + } +}; + +class tst_QPlaceSearchReply : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceSearchReply(); + +private Q_SLOTS: + void constructorTest(); + void typeTest(); + void requestTest(); + void resultsTest(); +}; + +tst_QPlaceSearchReply::tst_QPlaceSearchReply() +{ +} + +void tst_QPlaceSearchReply::constructorTest() +{ + TestSearchReply *reply = new TestSearchReply(this); + QVERIFY(reply->parent() == this); + delete reply; +} + +void tst_QPlaceSearchReply::typeTest() +{ + TestSearchReply *reply = new TestSearchReply(this); + QVERIFY(reply->type() == QPlaceReply::SearchReply); + delete reply; +} + +void tst_QPlaceSearchReply::requestTest() +{ + TestSearchReply *reply = new TestSearchReply(this); + QPlaceSearchRequest request; + request.setLimit(10); + + QGeoCircle circle; + circle.setCenter(QGeoCoordinate(10,20)); + request.setSearchArea(circle); + + request.setSearchTerm("pizza"); + + reply->setRequest(request); + QCOMPARE(reply->request(), request); + reply->setRequest(QPlaceSearchRequest()); + QCOMPARE(reply->request(), QPlaceSearchRequest()); + delete reply; +} + +void tst_QPlaceSearchReply::resultsTest() +{ + TestSearchReply *reply = new TestSearchReply(this); + QList results; + QPlace winterfell; + winterfell.setName("Winterfell"); + QPlace casterlyRock; + casterlyRock.setName("Casterly Rock"); + QPlace stormsEnd; + stormsEnd.setName("Storm's end"); + + QPlaceResult result1; + result1.setPlace(winterfell); + QPlaceResult result2; + result2.setPlace(casterlyRock); + QPlaceResult result3; + result3.setPlace(stormsEnd); + results << result1 << result2 << result3; + + reply->setResults(results); + QCOMPARE(reply->results(), results); + reply->setResults(QList()); + QCOMPARE(reply->results(), QList()); + delete reply; +} + +QTEST_APPLESS_MAIN(tst_QPlaceSearchReply) + +#include "tst_qplacesearchreply.moc" diff --git a/tests/auto/qplacesearchrequest/qplacesearchrequest.pro b/tests/auto/qplacesearchrequest/qplacesearchrequest.pro new file mode 100644 index 0000000..45d5d1a --- /dev/null +++ b/tests/auto/qplacesearchrequest/qplacesearchrequest.pro @@ -0,0 +1,6 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacesearchrequest +SOURCES += tst_qplacesearchrequest.cpp + +QT += location testlib diff --git a/tests/auto/qplacesearchrequest/tst_qplacesearchrequest.cpp b/tests/auto/qplacesearchrequest/tst_qplacesearchrequest.cpp new file mode 100644 index 0000000..959fede --- /dev/null +++ b/tests/auto/qplacesearchrequest/tst_qplacesearchrequest.cpp @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QPlaceSearchRequest : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceSearchRequest(); + +private Q_SLOTS: + void constructorTest(); + void searchTermTest(); + void categoriesTest(); + void boundingCircleTest(); + void boundingBoxTest(); + void searchAreaTest(); + void visibilityScopeTest(); + void relevanceHintTest(); + void searchContextTest(); + void operatorsTest(); + void clearTest(); +}; + +tst_QPlaceSearchRequest::tst_QPlaceSearchRequest() +{ +} + +void tst_QPlaceSearchRequest::constructorTest() +{ + QPlaceSearchRequest testObj; + Q_UNUSED(testObj); + + QPlaceSearchRequest *testObjPtr = new QPlaceSearchRequest(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceSearchRequest::searchTermTest() +{ + QPlaceSearchRequest testObj; + QVERIFY2(testObj.searchTerm() == QString(), "Wrong default value"); + testObj.setSearchTerm("testText"); + QVERIFY2(testObj.searchTerm() == "testText", "Wrong value returned"); +} + +void tst_QPlaceSearchRequest::categoriesTest() +{ + QPlaceSearchRequest testObj; + QVERIFY2(testObj.categories().count() == 0, "Wrong default value"); + QPlaceCategory cat; + cat.setCategoryId("45346"); + testObj.setCategory(cat); + QVERIFY2(testObj.categories().count() == 1, "Wrong categories count returned"); + QVERIFY2(testObj.categories()[0] == cat, "Wrong category returned"); + + testObj.setCategory(QPlaceCategory()); + QVERIFY(testObj.categories().isEmpty()); +} + +void tst_QPlaceSearchRequest::boundingCircleTest() +{ + QPlaceSearchRequest query; + QVERIFY2(query.searchArea() == QGeoShape(), "Wrong default value"); + QGeoCircle circle; + circle.setCenter(QGeoCoordinate(30,20)); + circle.setRadius(500.0); + query.setSearchArea(circle); + + QVERIFY(query.searchArea() != QGeoShape()); + QVERIFY(query.searchArea().type() == QGeoShape::CircleType); + QVERIFY(query.searchArea() == circle); + + QGeoCircle retrievedCircle = query.searchArea(); + QVERIFY2(retrievedCircle.center() == QGeoCoordinate(30,20), "Wrong value returned"); + QVERIFY2(retrievedCircle.radius() == 500.0, "Wrong value returned"); + query.clear(); + QVERIFY2(query.searchArea() == QGeoShape(), "Search area not cleared"); +} + +void tst_QPlaceSearchRequest::boundingBoxTest() +{ + QPlaceSearchRequest query; + QVERIFY2(query.searchArea() == QGeoShape(), "Wrong default value"); + QGeoRectangle box; + + box.setTopLeft(QGeoCoordinate(30,20)); + box.setBottomRight(QGeoCoordinate(10,50)); + query.setSearchArea(box); + + QVERIFY(query.searchArea() != QGeoShape()); + QVERIFY(query.searchArea().type() == QGeoShape::RectangleType); + QVERIFY(query.searchArea() == box); + + QGeoRectangle retrievedBox = query.searchArea(); + QVERIFY2(retrievedBox.topLeft() == QGeoCoordinate(30,20), "Wrong value returned"); + QVERIFY2(retrievedBox.bottomRight() == QGeoCoordinate(10,50), "Wrong value returned"); + + query.clear(); + QVERIFY2(query.searchArea() == QGeoShape(), "Wrong cleared value returned"); +} + +void tst_QPlaceSearchRequest::searchAreaTest() +{ + //test assignment of new search area over an old search area + QPlaceSearchRequest *query = new QPlaceSearchRequest; + QGeoCircle circle; + circle.setCenter(QGeoCoordinate(30,20)); + circle.setRadius(500.0); + query->setSearchArea(circle); + + QVERIFY(query->searchArea() == circle); + QGeoRectangle box; + box.setTopLeft(QGeoCoordinate(30,20)); + box.setBottomRight(QGeoCoordinate(10,50)); + query->setSearchArea(box); + QVERIFY2(query->searchArea() == box, "New search area not assigned"); +} + +void tst_QPlaceSearchRequest::visibilityScopeTest() +{ + QPlaceSearchRequest query; + QVERIFY2(query.visibilityScope() == QLocation::UnspecifiedVisibility, "Wrong default value"); + + query.setVisibilityScope(QLocation::DeviceVisibility); + QCOMPARE(query.visibilityScope(), QLocation::DeviceVisibility); + + query.setVisibilityScope(QLocation::DeviceVisibility | QLocation::PublicVisibility); + QVERIFY(query.visibilityScope() & QLocation::DeviceVisibility); + QVERIFY(!(query.visibilityScope() & QLocation::PrivateVisibility)); + QVERIFY(query.visibilityScope() & QLocation::PublicVisibility); +} + +void tst_QPlaceSearchRequest::relevanceHintTest() +{ + QPlaceSearchRequest request; + QCOMPARE(request.relevanceHint(), QPlaceSearchRequest::UnspecifiedHint); + request.setRelevanceHint(QPlaceSearchRequest::DistanceHint); + QCOMPARE(request.relevanceHint(), QPlaceSearchRequest::DistanceHint); + request.setRelevanceHint(QPlaceSearchRequest::UnspecifiedHint); + QCOMPARE(request.relevanceHint(), QPlaceSearchRequest::UnspecifiedHint); +} + +void tst_QPlaceSearchRequest::searchContextTest() +{ + QPlaceSearchRequest request; + QVERIFY(!request.searchContext().value().isValid()); + request.setSearchContext(QUrl(QStringLiteral("http://www.example.com/"))); + QCOMPARE(request.searchContext().value(), QUrl(QStringLiteral("http://www.example.com/"))); +} + +void tst_QPlaceSearchRequest::operatorsTest() +{ + QPlaceSearchRequest testObj; + testObj.setSearchTerm(QStringLiteral("testValue")); + QPlaceSearchRequest testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setSearchTerm(QStringLiteral("abc")); + QVERIFY2(testObj != testObj2, "Object should be different"); + testObj2.setSearchTerm(QStringLiteral("testValue")); + QVERIFY(testObj == testObj2); + + QGeoRectangle b1(QGeoCoordinate(20,20), QGeoCoordinate(10,30)); + QGeoRectangle b2(QGeoCoordinate(20,20), QGeoCoordinate(10,30)); + QGeoRectangle b3(QGeoCoordinate(40,40), QGeoCoordinate(10,40)); + + //testing that identical boxes match + testObj.setSearchArea(b1); + testObj2.setSearchArea(b2); + QVERIFY2(testObj == testObj2, "Identical box areas are not identified as matching"); + + //test that different boxes do not match + testObj2.setSearchArea(b3); + QVERIFY2(testObj != testObj2, "Different box areas identified as matching"); + + QGeoCircle c1(QGeoCoordinate(5,5),500); + QGeoCircle c2(QGeoCoordinate(5,5),500); + QGeoCircle c3(QGeoCoordinate(9,9),600); + + //test that identical cirlces match + testObj.setSearchArea(c1); + testObj2.setSearchArea(c2); + QVERIFY2(testObj == testObj2, "Identical circle areas are not identified as matching"); + + //test that different circle don't match + testObj2.setSearchArea(c3); + QVERIFY2(testObj != testObj2, "Different circle areas identified as matching"); + + //test that circles and boxes do not match + QGeoRectangle b4(QGeoCoordinate(20,20),QGeoCoordinate(10,30)); + QGeoCircle c4(QGeoCoordinate(20,20),500); + testObj.setSearchArea(b4); + testObj2.setSearchArea(c4); + QVERIFY2(testObj != testObj2, "Circle and box identified as matching"); + + //test that identical visibility scopes match + testObj.clear(); + testObj2.clear(); + testObj.setVisibilityScope(QLocation::PublicVisibility); + testObj2.setVisibilityScope(QLocation::PublicVisibility); + QVERIFY2(testObj == testObj2, "Identical scopes not identified as matching"); + + //test that different scopes do not match + testObj2.setVisibilityScope(QLocation::PrivateVisibility); + QVERIFY2(testObj != testObj2, "Different scopes identified as matching"); + + //test that different search contexts do not match + testObj.clear(); + testObj2.clear(); + testObj2.setSearchContext(QUrl(QStringLiteral("http://www.example.com/"))); + QVERIFY(testObj != testObj2); +} + +void tst_QPlaceSearchRequest::clearTest() +{ + QPlaceSearchRequest req; + req.setSearchTerm("pizza"); + req.setSearchArea(QGeoCircle(QGeoCoordinate(1,1), 5000)); + QPlaceCategory category; + category.setName("Fast Food"); + req.setCategory(category); + req.setLimit(100); + + req.clear(); + QVERIFY(req.searchTerm().isEmpty()); + QVERIFY(req.searchArea() == QGeoShape()); + QVERIFY(req.categories().isEmpty()); + QVERIFY(req.limit() == -1); +} + +QTEST_APPLESS_MAIN(tst_QPlaceSearchRequest) + +#include "tst_qplacesearchrequest.moc" diff --git a/tests/auto/qplacesearchresult/qplacesearchresult.pro b/tests/auto/qplacesearchresult/qplacesearchresult.pro new file mode 100644 index 0000000..935819a --- /dev/null +++ b/tests/auto/qplacesearchresult/qplacesearchresult.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacesearchresult + +SOURCES += tst_qplacesearchresult.cpp + +QT += location testlib diff --git a/tests/auto/qplacesearchresult/tst_qplacesearchresult.cpp b/tests/auto/qplacesearchresult/tst_qplacesearchresult.cpp new file mode 100644 index 0000000..9306392 --- /dev/null +++ b/tests/auto/qplacesearchresult/tst_qplacesearchresult.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class tst_QPlaceSearchResult : public QObject +{ + Q_OBJECT + +private Q_SLOTS: + void constructorTest(); + void title(); + void icon(); + void operators(); +}; + +void tst_QPlaceSearchResult::constructorTest() +{ + QPlaceSearchResult result; + + QCOMPARE(result.type(), QPlaceSearchResult::UnknownSearchResult); + QVERIFY(result.title().isEmpty()); + QVERIFY(result.icon().isEmpty()); + + result.setTitle(QStringLiteral("title")); + QPlaceIcon icon; + QVariantMap parameters; + parameters.insert(QStringLiteral("paramKey"), QStringLiteral("paramValue")); + icon.setParameters(parameters); + result.setIcon(icon); + + QPlaceSearchResult result2(result); + QCOMPARE(result2.title(), QStringLiteral("title")); + QCOMPARE(result2.icon().parameters().value(QStringLiteral("paramKey")).toString(), + QStringLiteral("paramValue")); + + QCOMPARE(result2, result); +} + +void tst_QPlaceSearchResult::title() +{ + QPlaceSearchResult result; + QVERIFY(result.title().isEmpty()); + result.setTitle(QStringLiteral("title")); + QCOMPARE(result.title(), QStringLiteral("title")); + result.setTitle(QString()); + QVERIFY(result.title().isEmpty()); +} + +void tst_QPlaceSearchResult::icon() +{ + QPlaceSearchResult result; + QVERIFY(result.icon().isEmpty()); + QPlaceIcon icon; + QVariantMap iconParams; + iconParams.insert(QStringLiteral("paramKey"), QStringLiteral("paramValue")); + result.setIcon(icon); + QCOMPARE(result.icon(), icon); + result.setIcon(QPlaceIcon()); + QVERIFY(result.icon().isEmpty()); +} + +void tst_QPlaceSearchResult::operators() +{ + QPlaceSearchResult result1; + QPlaceSearchResult result2; + + QVERIFY(result1 == result2); + QVERIFY(!(result1 != result2)); + + result1.setTitle(QStringLiteral("title")); + QVERIFY(!(result1 == result2)); + QVERIFY(result1 != result2); + + result2.setTitle(QStringLiteral("title")); + QVERIFY(result1 == result2); + QVERIFY(!(result1 != result2)); +} + +QTEST_APPLESS_MAIN(tst_QPlaceSearchResult) + +#include "tst_qplacesearchresult.moc" diff --git a/tests/auto/qplacesearchsuggestionreply/qplacesearchsuggestionreply.pro b/tests/auto/qplacesearchsuggestionreply/qplacesearchsuggestionreply.pro new file mode 100644 index 0000000..c436333 --- /dev/null +++ b/tests/auto/qplacesearchsuggestionreply/qplacesearchsuggestionreply.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacesearchsuggestionreply + +SOURCES += tst_qplacesearchsuggestionreply.cpp + +QT += location testlib diff --git a/tests/auto/qplacesearchsuggestionreply/tst_qplacesearchsuggestionreply.cpp b/tests/auto/qplacesearchsuggestionreply/tst_qplacesearchsuggestionreply.cpp new file mode 100644 index 0000000..8670e44 --- /dev/null +++ b/tests/auto/qplacesearchsuggestionreply/tst_qplacesearchsuggestionreply.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class SuggestionReply : public QPlaceSearchSuggestionReply +{ + Q_OBJECT +public: + SuggestionReply(QObject *parent) : QPlaceSearchSuggestionReply(parent){} + + void setSuggestions(const QStringList &suggestions) { + QPlaceSearchSuggestionReply::setSuggestions(suggestions); + } +}; + +class tst_QPlaceSearchSuggestionReply : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceSearchSuggestionReply(); + +private Q_SLOTS: + void constructorTest(); + void typeTest(); + void suggestionsTest(); +}; + +tst_QPlaceSearchSuggestionReply::tst_QPlaceSearchSuggestionReply() +{ +} + +void tst_QPlaceSearchSuggestionReply::constructorTest() +{ + SuggestionReply *reply = new SuggestionReply(this); + QCOMPARE(reply->parent(), this); + + delete reply; +} + +void tst_QPlaceSearchSuggestionReply::typeTest() +{ + SuggestionReply *reply = new SuggestionReply(this); + QCOMPARE(reply->type(), QPlaceReply::SearchSuggestionReply); + + delete reply; +} + +void tst_QPlaceSearchSuggestionReply::suggestionsTest() +{ + QStringList suggestions; + suggestions << QStringLiteral("one") << QStringLiteral("two") + << QStringLiteral("three"); + + SuggestionReply *reply = new SuggestionReply(this); + reply->setSuggestions(suggestions); + QCOMPARE(reply->suggestions(), suggestions); + + delete reply; +} + +QTEST_APPLESS_MAIN(tst_QPlaceSearchSuggestionReply) + +#include "tst_qplacesearchsuggestionreply.moc" diff --git a/tests/auto/qplacesupplier/qplacesupplier.pro b/tests/auto/qplacesupplier/qplacesupplier.pro new file mode 100644 index 0000000..06715f4 --- /dev/null +++ b/tests/auto/qplacesupplier/qplacesupplier.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplacesupplier + +SOURCES += tst_qplacesupplier.cpp + +QT += location testlib diff --git a/tests/auto/qplacesupplier/tst_qplacesupplier.cpp b/tests/auto/qplacesupplier/tst_qplacesupplier.cpp new file mode 100644 index 0000000..6d6bdf8 --- /dev/null +++ b/tests/auto/qplacesupplier/tst_qplacesupplier.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QPlaceSupplier : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceSupplier(); + +private Q_SLOTS: + void constructorTest(); + void nameTest(); + void supplierIdTest(); + void urlTest(); + void iconTest(); + void operatorsTest(); + void isEmptyTest(); +}; + +tst_QPlaceSupplier::tst_QPlaceSupplier() +{ +} + +void tst_QPlaceSupplier::constructorTest() +{ + QPlaceSupplier testObj; + Q_UNUSED(testObj); + + QPlaceSupplier *testObjPtr = new QPlaceSupplier(testObj); + QVERIFY2(testObjPtr != NULL, "Copy constructor - null"); + QVERIFY2(*testObjPtr == testObj, "Copy constructor - compare"); + delete testObjPtr; +} + +void tst_QPlaceSupplier::nameTest() +{ + QPlaceSupplier testObj; + QVERIFY2(testObj.name() == QString(), "Wrong default value"); + testObj.setName("testText"); + QVERIFY2(testObj.name() == "testText", "Wrong value returned"); +} + +void tst_QPlaceSupplier::supplierIdTest() +{ + QPlaceSupplier testObj; + QVERIFY2(testObj.supplierId() == QString(), "Wrong default value"); + testObj.setSupplierId("testText"); + QVERIFY2(testObj.supplierId() == "testText", "Wrong value returned"); +} + +void tst_QPlaceSupplier::urlTest() +{ + QPlaceSupplier testObj; + const QUrl testUrl = QUrl::fromEncoded("http://example.com/testUrl"); + QVERIFY2(testObj.url() == QString(), "Wrong default value"); + testObj.setUrl(testUrl); + QVERIFY2(testObj.url() == testUrl, "Wrong value returned"); +} + +void tst_QPlaceSupplier::iconTest() +{ + QPlaceSupplier testObj; + QVERIFY(testObj.icon().isEmpty()); + QPlaceIcon icon; + QVariantMap iconParams; + iconParams.insert(QPlaceIcon::SingleUrl, QUrl::fromEncoded("http://example.com/icon.png")); + icon.setParameters(iconParams); + testObj.setIcon(icon); + QCOMPARE(testObj.icon(), icon); + QCOMPARE(testObj.icon().url(), QUrl::fromEncoded("http://example.com/icon.png")); + + testObj.setIcon(QPlaceIcon()); + QVERIFY(testObj.icon().isEmpty()); + QCOMPARE(testObj.icon().url(), QUrl()); +} + +void tst_QPlaceSupplier::operatorsTest() +{ + QPlaceSupplier testObj; + testObj.setName(QStringLiteral("Acme")); + QPlaceIcon icon; + QVariantMap iconParams; + iconParams.insert(QPlaceIcon::SingleUrl, QUrl::fromEncoded("http://example.com/icon.png")); + icon.setParameters(iconParams); + testObj.setIcon(icon); + testObj.setSupplierId(QStringLiteral("34292")); + + QPlaceSupplier testObj2; + testObj2 = testObj; + QVERIFY2(testObj == testObj2, "Not copied correctly"); + testObj2.setSupplierId(QStringLiteral("testValue2")); + QVERIFY2(testObj != testObj2, "Object should be different"); +} + +void tst_QPlaceSupplier::isEmptyTest() +{ + QPlaceIcon icon; + QVariantMap iconParametersMap; + iconParametersMap.insert(QStringLiteral("Para"), QStringLiteral("meter")); + icon.setParameters(iconParametersMap); + QVERIFY(!icon.isEmpty()); + + QPlaceSupplier supplier; + + QVERIFY(supplier.isEmpty()); + + // name + supplier.setName(QStringLiteral("Name")); + QVERIFY(!supplier.isEmpty()); + supplier.setName(QString()); + QVERIFY(supplier.isEmpty()); + + // supplierId + supplier.setSupplierId(QStringLiteral("1")); + QVERIFY(!supplier.isEmpty()); + supplier.setSupplierId(QString()); + QVERIFY(supplier.isEmpty()); + + // url + supplier.setUrl(QUrl(QStringLiteral("www.example.com"))); + QVERIFY(!supplier.isEmpty()); + supplier.setUrl(QUrl()); + QVERIFY(supplier.isEmpty()); + + // icon + supplier.setIcon(icon); + QVERIFY(!supplier.isEmpty()); + supplier.setIcon(QPlaceIcon()); + QVERIFY(supplier.isEmpty()); +} + +QTEST_APPLESS_MAIN(tst_QPlaceSupplier); + +#include "tst_qplacesupplier.moc" diff --git a/tests/auto/qplaceuser/qplaceuser.pro b/tests/auto/qplaceuser/qplaceuser.pro new file mode 100644 index 0000000..3eab126 --- /dev/null +++ b/tests/auto/qplaceuser/qplaceuser.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qplaceuser + +SOURCES += tst_qplaceuser.cpp + +QT += location testlib diff --git a/tests/auto/qplaceuser/tst_qplaceuser.cpp b/tests/auto/qplaceuser/tst_qplaceuser.cpp new file mode 100644 index 0000000..086b184 --- /dev/null +++ b/tests/auto/qplaceuser/tst_qplaceuser.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include + +QT_USE_NAMESPACE + +class tst_QPlaceUser : public QObject +{ + Q_OBJECT + +public: + tst_QPlaceUser(); + +private Q_SLOTS: + void constructorTest(); + void nameTest(); + void userIdTest(); + void operatorsTest(); + void operatorsTest_data(); +}; + +tst_QPlaceUser::tst_QPlaceUser() +{ +} + +void tst_QPlaceUser::constructorTest() +{ + QPlaceUser user; + QVERIFY(user.name().isEmpty()); + QVERIFY(user.userId().isEmpty()); + + user.setName(QStringLiteral("Thomas Anderson")); + user.setUserId(QStringLiteral("Neo")); + + QPlaceUser user2(user); + QCOMPARE(user2.name(), QStringLiteral("Thomas Anderson")); + QCOMPARE(user2.userId(), QStringLiteral("Neo")); +} + +void tst_QPlaceUser::nameTest() +{ + QPlaceUser user; + user.setName(QStringLiteral("Thomas Anderson")); + QCOMPARE(user.name(), QStringLiteral("Thomas Anderson")); + user.setName(QString()); + QVERIFY(user.name().isEmpty()); +} + +void tst_QPlaceUser::userIdTest() +{ + QPlaceUser user; + user.setUserId(QStringLiteral("Neo")); + QCOMPARE(user.userId(), QStringLiteral("Neo")); + user.setUserId(QString()); + QVERIFY(user.userId().isEmpty()); +} + +void tst_QPlaceUser::operatorsTest() +{ + QPlaceUser user1; + user1.setName(QStringLiteral("Thomas Anderson")); + user1.setUserId(QStringLiteral("Neo")); + + QPlaceUser user2; + user2.setName(QStringLiteral("Thomas Anderson")); + user2.setUserId(QStringLiteral("Neo")); + + QVERIFY(user1 == user2); + QVERIFY(!(user1 != user2)); + QVERIFY(user2 == user1); + QVERIFY(!(user2 != user1)); + + QPlaceUser user3; + QVERIFY(!(user1 == user3)); + QVERIFY(user1 != user3); + QVERIFY(!(user3 == user1)); + QVERIFY(user3 != user1); + + user3 = user1; + QVERIFY(user1 == user3); + QVERIFY(!(user1 != user3)); + QVERIFY(user3 == user1); + QVERIFY(!(user3 != user1)); + + QFETCH(QString, field); + + if (field == QStringLiteral("name")) + user3.setName(QStringLiteral("bob")); + else if (field == QStringLiteral("userId")) + user3.setUserId(QStringLiteral("Morpheus")); + else + qFatal("Unknown data field"); + + QVERIFY(!(user1 == user3)); + QVERIFY(user1 != user3); + QVERIFY(!(user3 == user1)); + QVERIFY(user3 != user1); +} + +void tst_QPlaceUser::operatorsTest_data() +{ + QTest::addColumn("field"); + + QTest::newRow("user name") << "name"; + QTest::newRow("user id") << "userId"; +} + +QTEST_APPLESS_MAIN(tst_QPlaceUser) + +#include "tst_qplaceuser.moc" diff --git a/tests/auto/qproposedsearchresult/qproposedsearchresult.pro b/tests/auto/qproposedsearchresult/qproposedsearchresult.pro new file mode 100644 index 0000000..bae784c --- /dev/null +++ b/tests/auto/qproposedsearchresult/qproposedsearchresult.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +CONFIG += testcase +TARGET = tst_qproposedsearchresult + +SOURCES += tst_qproposedsearchresult.cpp + +QT += location testlib diff --git a/tests/auto/qproposedsearchresult/tst_qproposedsearchresult.cpp b/tests/auto/qproposedsearchresult/tst_qproposedsearchresult.cpp new file mode 100644 index 0000000..8693445 --- /dev/null +++ b/tests/auto/qproposedsearchresult/tst_qproposedsearchresult.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Aaron McCarthy +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "../utils/qlocationtestutils_p.h" + +QT_USE_NAMESPACE + +class tst_QPlaceProposedSearchResult : public QObject +{ + Q_OBJECT + +public: + QPlaceProposedSearchResult initialSubObject(); + bool checkType(const QPlaceSearchResult &result); + void detach(QPlaceSearchResult *result); + void setSubClassProperty(QPlaceProposedSearchResult *result); + +private Q_SLOTS: + void constructorTest(); + void title(); + void icon(); + void searchRequest(); + void conversion(); +}; + +QPlaceProposedSearchResult tst_QPlaceProposedSearchResult::initialSubObject() +{ + QPlaceProposedSearchResult proposedSearchResult; + proposedSearchResult.setTitle(QStringLiteral("title")); + + QPlaceIcon icon; + QVariantMap parameters; + parameters.insert(QPlaceIcon::SingleUrl, + QUrl(QStringLiteral("file:///opt/icons/icon.png"))); + icon.setParameters(parameters); + proposedSearchResult.setIcon(icon); + + QPlaceSearchRequest searchRequest; + searchRequest.setSearchContext(QUrl(QStringLiteral("http://www.example.com/"))); + proposedSearchResult.setSearchRequest(searchRequest); + + return proposedSearchResult; +} + +bool tst_QPlaceProposedSearchResult::checkType(const QPlaceSearchResult &result) +{ + return result.type() == QPlaceSearchResult::ProposedSearchResult; +} + +void tst_QPlaceProposedSearchResult::detach(QPlaceSearchResult *result) +{ + result->setTitle(QStringLiteral("title")); +} + +void tst_QPlaceProposedSearchResult::setSubClassProperty(QPlaceProposedSearchResult *result) +{ + QPlaceSearchRequest request; + request.setSearchContext(QUrl(QStringLiteral("http://www.example.com/place-search"))); + result->setSearchRequest(request); +} + +void tst_QPlaceProposedSearchResult::constructorTest() +{ + QPlaceProposedSearchResult result; + QCOMPARE(result.type(), QPlaceSearchResult::ProposedSearchResult); + + result.setTitle(QStringLiteral("title")); + + QPlaceIcon icon; + QVariantMap parameters; + parameters.insert(QStringLiteral("paramKey"), QStringLiteral("paramValue")); + icon.setParameters(parameters); + result.setIcon(icon); + + QPlaceSearchRequest searchRequest; + searchRequest.setSearchContext(QUrl(QStringLiteral("http://www.example.com/place-search"))); + result.setSearchRequest(searchRequest); + + //check copy constructor + QPlaceProposedSearchResult result2(result); + QCOMPARE(result2.title(), QStringLiteral("title")); + QCOMPARE(result2.icon(), icon); + QCOMPARE(result2.searchRequest(), searchRequest); + + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + //check results are the same after detachment of underlying shared data pointer + result2.setTitle(QStringLiteral("title")); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + //check construction of SearchResult using a ProposedSearchResult + QPlaceSearchResult searchResult(result); + QCOMPARE(searchResult.title(), QStringLiteral("title")); + QCOMPARE(searchResult.icon(), icon); + QVERIFY(QLocationTestUtils::compareEquality(searchResult, result)); + QVERIFY(searchResult.type() == QPlaceSearchResult::ProposedSearchResult); + result2 = searchResult; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + //check construction of a SearchResult using a SearchResult + //that is actually a PlaceResult underneath + QPlaceSearchResult searchResult2(searchResult); + QCOMPARE(searchResult2.title(), QStringLiteral("title")); + QCOMPARE(searchResult2.icon(), icon); + QVERIFY(QLocationTestUtils::compareEquality(searchResult2, result)); + QVERIFY(QLocationTestUtils::compareEquality(searchResult, searchResult2)); + QVERIFY(searchResult2.type() == QPlaceSearchResult::ProposedSearchResult); + result2 = searchResult2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceProposedSearchResult::title() +{ + QPlaceProposedSearchResult result; + QVERIFY(result.title().isEmpty()); + + result.setTitle(QStringLiteral("title")); + QCOMPARE(result.title(), QStringLiteral("title")); + + result.setTitle(QString()); + QVERIFY(result.title().isEmpty()); + + QPlaceProposedSearchResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setTitle("title"); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setTitle("title"); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceProposedSearchResult::icon() +{ + QPlaceProposedSearchResult result; + QVERIFY(result.icon().isEmpty()); + + QPlaceIcon icon; + QVariantMap iconParams; + iconParams.insert(QStringLiteral("paramKey"), QStringLiteral("paramValue")); + icon.setParameters(iconParams); + result.setIcon(icon); + QCOMPARE(result.icon(), icon); + + result.setIcon(QPlaceIcon()); + QVERIFY(result.icon().isEmpty()); + + QPlaceProposedSearchResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setIcon(icon); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setIcon(icon); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceProposedSearchResult::searchRequest() +{ + QPlaceProposedSearchResult result; + QCOMPARE(result.searchRequest(), QPlaceSearchRequest()); + + QPlaceSearchRequest placeSearchRequest; + placeSearchRequest.setSearchContext(QUrl(QStringLiteral("http://www.example.com/"))); + result.setSearchRequest(placeSearchRequest); + QCOMPARE(result.searchRequest(), placeSearchRequest); + + result.setSearchRequest(QPlaceSearchRequest()); + QCOMPARE(result.searchRequest(), QPlaceSearchRequest()); + + QPlaceProposedSearchResult result2; + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); + + result2.setSearchRequest(placeSearchRequest); + QVERIFY(QLocationTestUtils::compareInequality(result, result2)); + + result.setSearchRequest(placeSearchRequest); + QVERIFY(QLocationTestUtils::compareEquality(result, result2)); +} + +void tst_QPlaceProposedSearchResult::conversion() +{ + QLocationTestUtils::testConversion(this); +} + +QTEST_APPLESS_MAIN(tst_QPlaceProposedSearchResult) + +#include "tst_qproposedsearchresult.moc" diff --git a/tests/auto/utils/qlocationtestutils.cpp b/tests/auto/utils/qlocationtestutils.cpp new file mode 100644 index 0000000..d6e7785 --- /dev/null +++ b/tests/auto/utils/qlocationtestutils.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qlocationtestutils_p.h" + +bool QLocationTestUtils::hasDefaultSource() +{ + return false; +} + +bool QLocationTestUtils::hasDefaultMonitor() +{ + return false; +} + +QString QLocationTestUtils::addNmeaChecksumAndBreaks(const QString &sentence) +{ + Q_ASSERT(sentence[0] == '$' && sentence[sentence.length()-1] == '*'); + + // XOR byte value of all characters between '$' and '*' + int result = 0; + for (int i=1; i +#include +#include + +namespace QLocationTestUtils +{ + bool hasDefaultSource(); + bool hasDefaultMonitor(); + + QString addNmeaChecksumAndBreaks(const QString &sentence); + + QString createRmcSentence(const QDateTime &dt); + QString createGgaSentence(const QTime &time); + QString createGgaSentence(int lat, int lng, const QTime &time); + QString createZdaSentence(const QDateTime &dt); + QString createGsaSentence(); + + //The purpose of compareEquality() is to test equality + //operators where it is expected that A == B. + template + bool compareEquality(const A &first, const B &second) { + if (first != second) { + qWarning() << "compareEquality() failed: first != second"; + return false; + } + + if (second != first) { + qWarning() << "compareEquality() failed: second != first"; + return false; + } + + if (!(first == second)) { + qWarning() << "compareEquality() failed: !(first == second)"; + return false; + } + + if (!(second == first)) { + qWarning() << "compareEquality() failed: !(second == first)"; + return false; + } + + return true; + } + + //The purpose of compareInequality() is to test equality + //operators where it is expected that A != B. + //Using !compareEquality(...) is not sufficient because + //only the first operator checked would end up being tested. + template + bool compareInequality(const A &first, const B &second) { + if (!(first != second)){ + qWarning() << "compareInequality() failed: !(first != second)"; + return false; + } + + if (!(second != first)) { + qWarning() << "compareInequality() failed: !(second != first)"; + return false; + } + + if (first == second) { + qWarning() << "compareInequality() failed: first == second)"; + return false; + } + + if (second == first) { + qWarning() << "compareInequality() failed: second == first"; + return false; + } + return true; + } + + // Tests conversions between sub and base classes + // TC (test case) must implement: + // SubClass initialSubObject(); + // bool checkType(const BaseClass &) + // void detach(BaseClass *) - calls a mutator method, but doesn't actually modify the + // property to something different. + // void setSubClassProperty(SubClass *) - sets a property in the subclass instance + template + void testConversion(TC *tc) { + SubClass sub = tc->initialSubObject(); + //check conversion from SubClass -> BaseClass + //using assignment operator + BaseClass base; + base = sub; + QVERIFY(QLocationTestUtils::compareEquality(base, sub)); + QVERIFY(tc->checkType(base)); + + //check comparing base classes + BaseClass base2; + base2 = sub; + QVERIFY(QLocationTestUtils::compareEquality(base, base2)); + + //check conversion from BaseClass -> SubClass + //using assignment operator + SubClass sub2; + sub2 = base; + QVERIFY(QLocationTestUtils::compareEquality(sub, sub2)); + QVERIFY(tc->checkType(sub2)); + + //check that equality still holds with detachment of underlying data pointer + tc->detach(&base); + sub2 = base; + QVERIFY(QLocationTestUtils::compareEquality(sub, sub2)); + QVERIFY(QLocationTestUtils::compareEquality(sub, base)); + QVERIFY(QLocationTestUtils::compareEquality(base, base2)); + + //check that comparing objects are not the same + //when an underlying subclass field has been modified + tc->setSubClassProperty(&sub2); + base2 = sub2; + QVERIFY(QLocationTestUtils::compareInequality(sub, sub2)); + QVERIFY(QLocationTestUtils::compareInequality(sub, base2)); + QVERIFY(QLocationTestUtils::compareInequality(base, base2)); + + //check conversion from SubClass -> BaseClass + //using copy constructor + BaseClass base3(sub); + QVERIFY(QLocationTestUtils::compareEquality(sub, base3)); + QVERIFY(QLocationTestUtils::compareEquality(base, base3)); + + //check conversion from BaseClass -> SubClass + //using copy constructor + SubClass sub3(base3); + QVERIFY(QLocationTestUtils::compareEquality(sub, sub3)); + + //check conversion to subclass using a default base class instance + BaseClass baseDefault; + SubClass subDefault; + SubClass sub4(baseDefault); + QVERIFY(QLocationTestUtils::compareEquality(sub4, subDefault)); + + SubClass sub5 = baseDefault; + QVERIFY(QLocationTestUtils::compareEquality(sub5, subDefault)); + } +}; + +#endif diff --git a/tests/global/global.cfg b/tests/global/global.cfg new file mode 100644 index 0000000..8cc6269 --- /dev/null +++ b/tests/global/global.cfg @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/plugins/declarativetestplugin/declarativetestplugin.pro b/tests/plugins/declarativetestplugin/declarativetestplugin.pro new file mode 100644 index 0000000..0a99e7a --- /dev/null +++ b/tests/plugins/declarativetestplugin/declarativetestplugin.pro @@ -0,0 +1,27 @@ +CXX_MODULE = location +TARGET = declarative_location_test +TARGETPATH = QtLocation/Test + +QT += gui-private qml quick location testlib + +INCLUDEPATH += ../../../src/imports/location +INCLUDEPATH += ../../../src/location + +HEADERS += \ + qdeclarativepinchgenerator_p.h \ + qdeclarativelocationtestmodel_p.h \ + testhelper.h + +SOURCES += \ + locationtest.cpp \ + qdeclarativepinchgenerator.cpp \ + qdeclarativelocationtestmodel.cpp + +IMPORT_FILES = \ + qmldir + +load(qml_plugin) + + +# must be after load(qml_plugin) +include(../imports.pri) diff --git a/tests/plugins/declarativetestplugin/locationtest.cpp b/tests/plugins/declarativetestplugin/locationtest.cpp new file mode 100644 index 0000000..44e74e8 --- /dev/null +++ b/tests/plugins/declarativetestplugin/locationtest.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepinchgenerator_p.h" +#include "qdeclarativelocationtestmodel_p.h" +#include "testhelper.h" + +#include +#include + +#include + +QT_BEGIN_NAMESPACE + +static QObject *helper_factory(QQmlEngine *engine, QJSEngine *scriptEngine) +{ + Q_UNUSED(engine); + Q_UNUSED(scriptEngine); + TestHelper *helper = new TestHelper(); + return helper; +} + +class QLocationDeclarativeTestModule: public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + virtual void registerTypes(const char* uri) + { + if (QLatin1String(uri) == QLatin1String("QtLocation.Test")) { + qmlRegisterType(uri, 5, 5, "PinchGenerator"); + qmlRegisterType(uri, 5, 5, "TestModel"); + qmlRegisterSingletonType(uri, 5, 6, "LocationTestHelper", helper_factory); + } else { + qWarning() << "Unsupported URI given to load location test QML plugin: " << QLatin1String(uri); + } + } +}; + +#include "locationtest.moc" + +QT_END_NAMESPACE + diff --git a/tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel.cpp b/tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel.cpp new file mode 100644 index 0000000..939225a --- /dev/null +++ b/tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel.cpp @@ -0,0 +1,246 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativelocationtestmodel_p.h" +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativeLocationTestModel::QDeclarativeLocationTestModel(QObject *parent): + QAbstractListModel(parent), + delay_(0), + datacount_(0), + componentCompleted_(false), + crazyLevel_(0), + crazyMode_(false) +{ + timer_.setSingleShot(true); + connect(&timer_, SIGNAL(timeout()), this, SLOT(timerFired())); +} + +QDeclarativeLocationTestModel::~QDeclarativeLocationTestModel() +{ + if (timer_.isActive()) + timer_.stop(); + if (!dataobjects_.isEmpty()) { + qDeleteAll(dataobjects_); + dataobjects_.clear(); + } +} + +void QDeclarativeLocationTestModel::timerFired() +{ + //qDebug() << "timer fired" ; + repopulate(); + if (crazyMode_) { + //qDebug() << "raw randomw value: " << qrand(); + int delay = (QRandomGenerator::global()->bounded(uint(INT_MAX) + 1) % crazyLevel_); // writing software is exact science + delay = qMax(1000, delay); // 3 ms at minimum + qDebug() << "starting timer with : " << delay; + timer_.start(delay); + } +} + +void QDeclarativeLocationTestModel::componentComplete() +{ + componentCompleted_ = true; + scheduleRepopulation(); +} + + +int QDeclarativeLocationTestModel::datacount() const +{ + return datacount_; +} + +void QDeclarativeLocationTestModel::setDatacount(int datacount) +{ + if (datacount_ == datacount) + return; + datacount_ = datacount; + emit datacountChanged(); + scheduleRepopulation(); +} + +int QDeclarativeLocationTestModel::delay() const +{ + return delay_; +} + +void QDeclarativeLocationTestModel::setDelay(int delay) +{ + if (delay_ == delay) + return; + delay_ = delay; + emit delayChanged(); +} + +QString QDeclarativeLocationTestModel::datatype() const +{ + return datatype_; +} + +void QDeclarativeLocationTestModel::setDatatype(QString datatype) +{ + if (datatype_ == datatype) + return; + datatype_ = datatype; + emit datatypeChanged(); + scheduleRepopulation(); +} + +int QDeclarativeLocationTestModel::crazyLevel() const +{ + return crazyLevel_; +} + +void QDeclarativeLocationTestModel::setCrazyLevel(int level) +{ + if (level == crazyLevel_) + return; + crazyLevel_ = level; + reset(); + scheduleRepopulation(); + emit crazyLevelChanged(); +} + +bool QDeclarativeLocationTestModel::crazyMode() const +{ + return crazyMode_; +} + +void QDeclarativeLocationTestModel::setCrazyMode(bool mode) +{ + if (mode == crazyMode_) + return; + crazyMode_ = mode; + //if (!crazyMode_) + //reset(); + //else + if (crazyMode_) + scheduleRepopulation(); + emit crazyModeChanged(); +} + +// only coordinate datatype for now to get started with, +// refactor if more usecases for the model emerge. +void QDeclarativeLocationTestModel::repopulate() +{ + double latitude = -30; + double longitude = 153; + beginResetModel(); + if (!dataobjects_.isEmpty()) { + qDeleteAll(dataobjects_); + dataobjects_.clear(); + } + int datacount = datacount_; + if (crazyMode_) + datacount = QRandomGenerator::global()->bounded(datacount_); + + for (int i = 0; i < datacount; ++i) { + DataObject* dataobject = new DataObject; + dataobject->coordinate_ = QGeoCoordinate(latitude, longitude); + dataobjects_.append(dataobject); + longitude -= 0.2; + latitude += 0.2; + } + endResetModel(); +} + +void QDeclarativeLocationTestModel::update() +{ + scheduleRepopulation(); +} + +void QDeclarativeLocationTestModel::reset() +{ + if (timer_.isActive()) + timer_.stop(); + beginResetModel(); + if (!dataobjects_.isEmpty()) { + qDeleteAll(dataobjects_); + dataobjects_.clear(); + } + endResetModel(); +} + +void QDeclarativeLocationTestModel::scheduleRepopulation() +{ + if (!componentCompleted_) + return; + + if (datacount_ <= 0) + return; + + if (timer_.isActive()) + timer_.stop(); + + if (crazyMode_) { + // start generating arbitrary amount of data at arbitrary intervals + int delay = QRandomGenerator::global()->bounded(crazyLevel_); // writing software is exact science + delay = qMax(3, delay); // 3 ms at minimum + qDebug() << "starting timer with : " << delay; + timer_.start(delay); + } else { + // just update + if (delay_ > 0) + timer_.start(delay_); + else + repopulate(); + } +} + +int QDeclarativeLocationTestModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + return dataobjects_.count(); +} + +// Returns the stored under the given role for the item referred to by the index. +QVariant QDeclarativeLocationTestModel::data(const QModelIndex& index, int role) const +{ + switch (role) { + case TestDataRole: + if (dataobjects_.at(index.row())) { + return QVariant::fromValue(qobject_cast(dataobjects_.at(index.row()))); + } + break; + } + return QVariant(); +} + +QHash QDeclarativeLocationTestModel::roleNames() const +{ + QHash roles = QAbstractListModel::roleNames(); + roles.insert(TestDataRole, "modeldata"); + return roles; +} + +QT_END_NAMESPACE diff --git a/tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel_p.h b/tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel_p.h new file mode 100644 index 0000000..925125a --- /dev/null +++ b/tests/plugins/declarativetestplugin/qdeclarativelocationtestmodel_p.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVELOCATIONTESTMODEL_H +#define QDECLARATIVELOCATIONTESTMODEL_H + +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class DataObject: public QObject +{ + Q_OBJECT + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate CONSTANT) + +public: + DataObject() {} + ~DataObject() {} + + QGeoCoordinate coordinate_; + QGeoCoordinate coordinate() const {return coordinate_;} +}; + +class QDeclarativeLocationTestModel : public QAbstractListModel, public QQmlParserStatus +{ + Q_OBJECT + Q_PROPERTY(int datacount READ datacount WRITE setDatacount NOTIFY datacountChanged) + Q_PROPERTY(int delay READ delay WRITE setDelay NOTIFY delayChanged) + Q_PROPERTY(bool crazyMode READ crazyMode WRITE setCrazyMode NOTIFY crazyModeChanged) + Q_PROPERTY(int crazyLevel READ crazyLevel WRITE setCrazyLevel NOTIFY crazyLevelChanged) + Q_PROPERTY(QString datatype READ datatype WRITE setDatatype NOTIFY datatypeChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + QDeclarativeLocationTestModel(QObject* parent = 0); + ~QDeclarativeLocationTestModel(); + + enum Roles { + TestDataRole = Qt::UserRole + 500 + }; + + // from QQmlParserStatus + virtual void componentComplete(); + virtual void classBegin() {} + + // From QAbstractListModel + virtual int rowCount(const QModelIndex &parent) const; + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QHash roleNames() const; + + int datacount() const; + void setDatacount(int datacount); + + int delay() const; + void setDelay(int delay); + + int crazyLevel() const; + void setCrazyLevel(int level); + + bool crazyMode() const; + void setCrazyMode(bool mode); + + QString datatype() const; + void setDatatype(QString datatype); + + //Q_INVOKABLE void clear(); + Q_INVOKABLE void reset(); + Q_INVOKABLE void update(); + //Q_INVOKABLE void reset(); + +signals: + void countChanged(); + void datacountChanged(); + void datatypeChanged(); + void delayChanged(); + void modelChanged(); + void crazyLevelChanged(); + void crazyModeChanged(); + +private slots: + void repopulate(); + void timerFired(); + +private: + void scheduleRepopulation(); + +private: + int delay_; + int datacount_; + bool componentCompleted_; + QString datatype_; + QTimer timer_; + QList dataobjects_; + int crazyLevel_; + bool crazyMode_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tests/plugins/declarativetestplugin/qdeclarativepinchgenerator.cpp b/tests/plugins/declarativetestplugin/qdeclarativepinchgenerator.cpp new file mode 100644 index 0000000..b8866b4 --- /dev/null +++ b/tests/plugins/declarativetestplugin/qdeclarativepinchgenerator.cpp @@ -0,0 +1,383 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdeclarativepinchgenerator_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +QDeclarativePinchGenerator::QDeclarativePinchGenerator(): + target_(0), + state_(Invalid), + window_(0), + activeSwipe_(0), + replayTimer_(-1), + replayBookmark_(-1), + masterSwipe_(-1), + replaySpeedFactor_(1.0), + enabled_(true) +{ + setAcceptedMouseButtons(Qt::LeftButton | Qt::MidButton | Qt::RightButton); + swipeTimer_.invalidate(); + device_ = new QTouchDevice; + device_->setType(QTouchDevice::TouchScreen); + QWindowSystemInterface::registerTouchDevice(device_); +} + +QDeclarativePinchGenerator::~QDeclarativePinchGenerator() +{ + clear(); +} + +void QDeclarativePinchGenerator::componentComplete() +{ + QQuickItem::componentComplete(); +} + +void QDeclarativePinchGenerator::mousePressEvent(QMouseEvent *event) +{ + if (state_ != Idle || !enabled_) { + event->ignore(); + return; + } + Q_ASSERT(!activeSwipe_); + Q_ASSERT(!swipeTimer_.isValid()); + // Start recording a pinch gesture. + activeSwipe_ = new Swipe; + activeSwipe_->touchPoints << event->pos(); + activeSwipe_->durations << 0; + swipeTimer_.start(); + setState(Recording); +} + +void QDeclarativePinchGenerator::mouseMoveEvent(QMouseEvent *event) +{ + if (state_ != Recording || !enabled_) { + event->ignore(); + return; + } + Q_ASSERT(activeSwipe_); + Q_ASSERT(swipeTimer_.isValid()); + + activeSwipe_->touchPoints << event->pos(); + activeSwipe_->durations << swipeTimer_.elapsed(); + swipeTimer_.restart(); +} + +void QDeclarativePinchGenerator::mouseReleaseEvent(QMouseEvent *event) +{ + if (state_ != Recording || !enabled_) { + event->ignore(); + return; + } + Q_ASSERT(activeSwipe_); + Q_ASSERT(swipeTimer_.isValid()); + activeSwipe_->touchPoints << event->pos(); + activeSwipe_->durations << swipeTimer_.elapsed(); + + if (swipes_.count() == SWIPES_REQUIRED) + delete swipes_.takeFirst(); + swipes_ << activeSwipe_; + activeSwipe_ = 0; + swipeTimer_.invalidate(); + if (window_ && target_) setState(Idle); else setState(Invalid); +} + +void QDeclarativePinchGenerator::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_UNUSED(event); + if (!enabled_) { + event->ignore(); + return; + } + stop(); + clear(); + if (window_ && target_) setState(Idle); else setState(Invalid); +} + +void QDeclarativePinchGenerator::keyPressEvent(QKeyEvent *e) +{ + if (!enabled_) { + e->ignore(); + } + + if (e->key() == Qt::Key_C) { + clear(); + } else if (e->key() == Qt::Key_R) { + replay(); + } else if (e->key() == Qt::Key_S) { + stop(); + } else if (e->key() == Qt::Key_Plus) { + setReplaySpeedFactor(replaySpeedFactor() + 0.1); + } else if (e->key() == Qt::Key_Minus) { + setReplaySpeedFactor(replaySpeedFactor() - 0.1); + } else { + qDebug() << metaObject()->className() << "Unsupported key event."; + } +} + +bool QDeclarativePinchGenerator::enabled() const +{ + return enabled_; +} + + +void QDeclarativePinchGenerator::setEnabled(bool enabled) +{ + if (enabled == enabled_) + return; + enabled_ = enabled; + if (!enabled_) { + stop(); + clear(); + } + emit enabledChanged(); +} + + +qreal QDeclarativePinchGenerator::replaySpeedFactor() const +{ + return replaySpeedFactor_; +} + +void QDeclarativePinchGenerator::setReplaySpeedFactor(qreal factor) +{ + if (factor == replaySpeedFactor_ || factor < 0.001) + return; + replaySpeedFactor_ = factor; + emit replaySpeedFactorChanged(); +} + + +QString QDeclarativePinchGenerator::state() const +{ + switch (state_) { + case Invalid: + return "Invalid"; + case Idle: + return "Idle"; + break; + case Recording: + return "Recording"; + break; + case Replaying: + return "Replaying"; + break; + default: + Q_ASSERT(false); + } + return "How emberassing"; +} + +void QDeclarativePinchGenerator::setState(GeneratorState state) +{ + if (state == state_) + return; + state_ = state; + emit stateChanged(); +} + +void QDeclarativePinchGenerator::itemChange(ItemChange change, const ItemChangeData & data) +{ + if (change == ItemSceneChange) { + window_ = data.window; + if (target_) + setState(Idle); + } +} + +void QDeclarativePinchGenerator::timerEvent(QTimerEvent *event) +{ + Q_ASSERT(replayTimer_ == event->timerId()); + Q_UNUSED(event); + Q_ASSERT(state_ == Replaying); + + int slaveSwipe = masterSwipe_ ^ 1; + + int masterCount = swipes_.at(masterSwipe_)->touchPoints.count(); + int slaveCount = swipes_.at(slaveSwipe)->touchPoints.count(); + + if (replayBookmark_ == 0) { + QTest::touchEvent(window_, device_) + .press(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_)) + .press(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_)); + } else if (replayBookmark_ == (slaveCount - 1)) { + if (masterCount != slaveCount) { + QTest::touchEvent(window_, device_) + .move(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_)) + .release(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_)); + } else { + QTest::touchEvent(window_, device_) + .release(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_)) + .release(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_)); + } + } else if (replayBookmark_ == (masterCount - 1)) { + QTest::touchEvent(window_, device_) + .release(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_)); + } + else { + QTest::touchEvent(window_, device_) + .move(0, swipes_.at(masterSwipe_)->touchPoints.at(replayBookmark_)) + .move(1, swipes_.at(slaveSwipe)->touchPoints.at(replayBookmark_)); + } + + replayBookmark_++; + if (replayBookmark_ >= swipes_.at(masterSwipe_)->touchPoints.count()) + stop(); + else { + killTimer(replayTimer_); + replayTimer_ = startTimer((swipes_.at(masterSwipe_)->durations.at(replayBookmark_) + 5) / replaySpeedFactor_ ); + } +} + +QQuickItem* QDeclarativePinchGenerator::target() const +{ + return target_; +} + +void QDeclarativePinchGenerator::setTarget(QQuickItem* target) +{ + if (target == target_) + return; + target_ = target; + stop(); + clear(); + if (window_) + setState(Idle); + else + setState(Invalid); + emit targetChanged(); +} + +void QDeclarativePinchGenerator::pinch(QPoint point1From, + QPoint point1To, + QPoint point2From, + QPoint point2To, + int interval1, + int interval2, + int samples1, + int samples2) +{ + Q_ASSERT(interval1 > 10); + Q_ASSERT(interval2 > 10); + Q_ASSERT(samples1 >= 2); // we need press and release events at minimum + Q_ASSERT(samples2 >= 2); + + clear(); + + Swipe* swipe1 = new Swipe; + Swipe* swipe2 = new Swipe; + for (int i = 0; i < samples1; ++i) { + swipe1->touchPoints << point1From + (point1To - point1From) / samples1 * i; + swipe1->durations << interval1; + } + for (int i = 0; i < samples2; ++i) { + swipe2->touchPoints << point2From + (point2To - point2From) / samples2 * i; + swipe2->durations << interval2; + } + swipes_ << swipe1 << swipe2; + Q_ASSERT(swipes_.at(0)); + Q_ASSERT(swipes_.at(1)); + + masterSwipe_ = (samples1 >= samples2) ? 0 : 1; + + replayTimer_ = startTimer(swipes_.at(masterSwipe_)->durations.at(0) / replaySpeedFactor_); + replayBookmark_ = 0; + setState(Replaying); +} + +void QDeclarativePinchGenerator::pinchPress(QPoint point1From, QPoint point2From) +{ + QTest::touchEvent(window_, device_).press(0, point1From).press(1, point2From); +} + +void QDeclarativePinchGenerator::pinchMoveTo(QPoint point1To, QPoint point2To) +{ + QTest::touchEvent(window_, device_).move(0, point1To).move(1, point2To); +} + +void QDeclarativePinchGenerator::pinchRelease(QPoint point1To, QPoint point2To) +{ + QTest::touchEvent(window_, device_).release(0, point1To).release(1, point2To); +} + +void QDeclarativePinchGenerator::replay() +{ + if (state_ != Idle) { + qDebug() << "Wrong state, will not replay pinch, state: " << state_; + return; + } + if (swipes_.count() < SWIPES_REQUIRED) { + qDebug() << "Too few swipes, cannot replay, amount: " << swipes_.count(); + return; + } + if ((swipes_.at(0)->touchPoints.count() < 2) || (swipes_.at(1)->touchPoints.count() < 2)) { + qDebug() << "Too few touchpoints, won't replay, amount: " << + swipes_.at(0)->touchPoints.count() << (swipes_.at(1)->touchPoints.count() < 2); + return; + } + + masterSwipe_ = (swipes_.at(0)->touchPoints.count() >= swipes_.at(1)->touchPoints.count()) ? 0 : 1; + + replayTimer_ = startTimer(swipes_.at(masterSwipe_)->touchPoints.count() / replaySpeedFactor_); + replayBookmark_ = 0; + setState(Replaying); +} + +void QDeclarativePinchGenerator::clear() +{ + stop(); + delete activeSwipe_; + activeSwipe_ = 0; + if (!swipes_.isEmpty()) { + qDeleteAll(swipes_); + swipes_.clear(); + } +} + +void QDeclarativePinchGenerator::stop() +{ + if (state_ != Replaying) + return; + // stop replay + Q_ASSERT(replayTimer_ != -1); + killTimer(replayTimer_); + replayTimer_ = -1; + setState(Idle); +} + +int QDeclarativePinchGenerator::startDragDistance() +{ + return qApp->styleHints()->startDragDistance(); +} + +QT_END_NAMESPACE diff --git a/tests/plugins/declarativetestplugin/qdeclarativepinchgenerator_p.h b/tests/plugins/declarativetestplugin/qdeclarativepinchgenerator_p.h new file mode 100644 index 0000000..23d8b65 --- /dev/null +++ b/tests/plugins/declarativetestplugin/qdeclarativepinchgenerator_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDECLARATIVEPINCHGENERATOR_H +#define QDECLARATIVEPINCHGENERATOR_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SWIPES_REQUIRED 2 + +QT_BEGIN_NAMESPACE + +typedef struct { + QList touchPoints; + QList durations; +} Swipe; + +class QDeclarativePinchGenerator : public QQuickItem +{ + Q_OBJECT + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QString state READ state NOTIFY stateChanged) + Q_PROPERTY(qreal replaySpeedFactor READ replaySpeedFactor WRITE setReplaySpeedFactor NOTIFY replaySpeedFactorChanged) + Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged) + Q_INTERFACES(QQmlParserStatus) + +public: + QDeclarativePinchGenerator(); + ~QDeclarativePinchGenerator(); + + QString state() const; + QQuickItem* target() const; + void setTarget(QQuickItem* target); + qreal replaySpeedFactor() const; + void setReplaySpeedFactor(qreal factor); + bool enabled() const; + void setEnabled(bool enabled); + + Q_INVOKABLE void replay(); + Q_INVOKABLE void clear(); + Q_INVOKABLE void stop(); + Q_INVOKABLE int startDragDistance(); + + // programmatic interface, useful for autotests + Q_INVOKABLE void pinch(QPoint point1From, + QPoint point1To, + QPoint point2From, + QPoint point2To, + int interval1 = 20, + int interval2 = 20, + int samples1 = 10, + int samples2 = 10); + + Q_INVOKABLE void pinchPress(QPoint point1From, QPoint point2From); + Q_INVOKABLE void pinchMoveTo(QPoint point1To, QPoint point2To); + Q_INVOKABLE void pinchRelease(QPoint point1To, QPoint point2To); + +signals: + void stateChanged(); + void targetChanged(); + void replaySpeedFactorChanged(); + void enabledChanged(); + +public: + enum GeneratorState { + Invalid = 0, + Idle = 1, + Recording = 2, + Replaying = 3 + }; + + // from QQmlParserStatus + virtual void componentComplete(); + // from QQuickItem + void itemChange(ItemChange change, const ItemChangeData & data); + +protected: + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void timerEvent(QTimerEvent *event); + +private: + void setState(GeneratorState state); + +private: + QQuickItem* target_; + GeneratorState state_; + QQuickWindow* window_; + QList swipes_; + Swipe* activeSwipe_; + QElapsedTimer swipeTimer_; + int replayTimer_; + int replayBookmark_; + int masterSwipe_; + qreal replaySpeedFactor_; + bool enabled_; + QTouchDevice* device_; +}; + +QT_END_NAMESPACE + +#endif diff --git a/tests/plugins/declarativetestplugin/qmldir b/tests/plugins/declarativetestplugin/qmldir new file mode 100644 index 0000000..c2582ed --- /dev/null +++ b/tests/plugins/declarativetestplugin/qmldir @@ -0,0 +1,3 @@ +module QtLocation.Test +plugin declarative_location_test +classname QLocationDeclarativeTestModule diff --git a/tests/plugins/declarativetestplugin/testhelper.h b/tests/plugins/declarativetestplugin/testhelper.h new file mode 100644 index 0000000..164d183 --- /dev/null +++ b/tests/plugins/declarativetestplugin/testhelper.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef TESTHELPER_H +#define TESTHELPER_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class TestHelper: public QObject +{ + Q_OBJECT +public: + TestHelper(QObject *parent = nullptr):QObject(parent){} + Q_INVOKABLE bool waitForPolished(QQuickItem *item, int timeout = 10000) const + { + QSignalSpy spy(item->window(), &QQuickWindow::afterAnimating); + return spy.wait(timeout); + } + + Q_INVOKABLE int x86Bits() const + { + if ( QSysInfo::currentCpuArchitecture() == "x86_64" ) + return 64; + else + return 32; + } +}; + +QT_END_NAMESPACE + +#endif // TESTHELPER_H diff --git a/tests/plugins/imports.pri b/tests/plugins/imports.pri new file mode 100644 index 0000000..00f9333 --- /dev/null +++ b/tests/plugins/imports.pri @@ -0,0 +1,5 @@ +!contains(DESTDIR, $$[QT_INSTALL_QML]/$$TARGETPATH) { + importfiles.files = $$IMPORT_FILES + importfiles.path = $$DESTDIR + COPIES += importfiles +} diff --git a/tests/tests.pro b/tests/tests.pro new file mode 100644 index 0000000..4fa2b33 --- /dev/null +++ b/tests/tests.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = auto +qtHaveModule(location):qtHaveModule(quick): SUBDIRS += plugins/declarativetestplugin -- 2.30.2
  • Z6$bLU)QjYD6 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

  • w8HR%LZ0Z{mv6 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 literal 0 HcmV?d00001 diff --git a/examples/positioning/weatherinfo/icons/weather-showers.png b/examples/positioning/weatherinfo/icons/weather-showers.png new file mode 100644 index 0000000000000000000000000000000000000000..07489aaafbc535d764bead3870e422ba237ac45d GIT binary patch literal 76340 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